AOP总结
**Spring-AOP**
1.Spring-AOP理论:
aop 是软件设计领域中的面向切面编程,它是面向对象(oop)编程的一种补充和完善
实际项目中我们通常将面向对象理解为一个静态过程(例如一个系统有多少模块,一个模块有哪些对象,对象有哪些属性),面向切面中包含一个一个动态过程(在对象运行时动态织入一些功能。)
2.AOP的作用:
AOP就是要在基于OCP(开闭原则)在不改变原有系统核心业务代码的基础上,动态添加一些扩展功能并可以控制对象的执行。
3.AOP实际项目的应用场景:
AOP 通常应用于日志的处理,事务处理,权限处理,缓存处理等等。
4.AOP底层原理分析:
AOP底层基于代理机制实现功能扩展:(了解)
- 假如目标对象(被代理对象)实现接口,则底层默认采用JDK动态代理机制为目标对象创建代理对象(目标类和代理类会实现共同接口)
如图(1):
图片绿色的都为底层原理及调用
1.1)代码步骤:
(1.1.1)在spring框架整合中的核心配置文件中(我的配置文件名叫spring-configs.xml)配置一行:
<!--启用Aop -->
<aop:aspectj-autoproxy/>
(1.1.2)spring-configs.xml中的代码为:
<?xml version="1.0" encoding="UTF-8"?><beans
default-lazy-init="true"
xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.3.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd">
<!-- 指定spring 框架要扫描的包结构 -->
<context:component-scan base-package="com.db"/>
<!-- 加载configs.properties :
1)系统底层会读取配置文件configs.properties
2)系统底层会将配置文件内容封装到Properties对象
3)系统底层将Properties对象交给Spring容器进行管理-->
<util:properties id="cfg"
location="classpath:configs.properties"/>
<!-- 加载spring-model.xml(此配置文件中要配置
service,dao,datasource,mybatis) -->
<import resource="classpath:spring-model.xml"/>
<import resource="classpath:spring-shiro.xml"/>
<!-- 加载spring-web.xml -->
<import resource="classpath:spring-web.xml"/>
******<!--启用Aop 切面-->
<aop:aspectj-autoproxy/>******
</beans>
(1.1.3)自定义一个注解后面用,这里根据你自己的想法来。不过通过这个注解可以进行细粒度的切入具体类的哪个方法。
package com.db.common.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/*
* 自定义注解
* 1.jdk1.5推出的一种元数据
* 2.本质上是一个特殊的Class
* 3.主要用于描述类,方法,属性,参数。
*
* spring中描述bean对象方式
* xml:<bean id="">
* 注解:@service,@Controller。。。。
*
* 注解的应用:
* 1)编译时:@Override...
* 2)运行时:@Controller...会结合反射一起应用,否则注解无效。
* 自定义注解:
* 1)框架中定义
* 2)自己定义(借助@interface)
*
*
* @author A
*
*/
//可以修饰类,方法:METHOD表示修饰方法
@Target(ElementType.METHOD)
//何时有效 :RUNTIME运行时有效被阿表示
@Retention(RetentionPolicy.RUNTIME)
public @interface RequiredLog {
/*@RequiredLog("123456")
*String value();
*/
//@RequiredLog
String value()default"";
}
(1.1.4)Pointcut:切入点;
@Pointcut(“bean(*ServiceImpl)”)所有类的所有方法,粗粒度
@Pointcut(“bean(sysUserServiceImpl)”)访问那个类中的所有方法
@Pointcut("@annotation(com.db.common.annotation.RequiredLog)")//细粒度的访问哪个类的哪个方法
SysLogAspect这个类中的代码(动态的业务实现,这个类中主要写的是AOP切面的应用):
/**
* 日志切面类型(对用户行为进行记录)
* @author A
* @Aspect
*
*/
@Order(1)
@Aspect
@Service
public class SysLogAspect {
@Autowired
private SysLogDao sysLogDao;
//当方法参数为ProceedingJoinPoint类型时候,并且希望在目标方法之前或者之后都有功能扩展,一般借助于@Around进行修饰
@Pointcut("@annotation(com.db.common.annotation.RequiredLog)")
public void doLongpoint(){}
@Around("doLongpoint()")
public Object around(ProceedingJoinPoint jp)throws Throwable{
System.out.println("start:"+System.nanoTime());
Object result = jp.proceed();//执行目标方法
System.out.println("end:"+System.nanoTime());
long t1 = System.currentTimeMillis();
long t2 = System.currentTimeMillis();
System.out.println("记录用户行为");
//将日志信息写入导数据库
saveLog(jp,(t2-t1));
return result;
}
private void saveLog(ProceedingJoinPoint jp, long time) throws Exception, SecurityException {
//获取日志信息
MethodSignature s = (MethodSignature) jp.getSignature();
Class<?>[]parameterTypes=s.getParameterTypes();
System.out.println(parameterTypes.toString());
String methodName = s.getName();
//获取目标方法对象
Class<?>targetClass=jp.getTarget().getClass();
Method targetMethod=targetClass.getDeclaredMethod(methodName, parameterTypes);
//获取目标方法信息(类名+"."+方法名)
String method = jp.getTarget().getClass().getName();
//获取方法上的注解
RequiredLog requiredLog = targetMethod.getDeclaredAnnotation(RequiredLog.class);
//获取注释上指定的操作名
String operation="";
if(requiredLog!=null){
operation=requiredLog.value();
}
//获取登录的用户名
SysUser user = (SysUser)SecurityUtils.getSubject().getPrincipal();
String username = user.getUsername();
//封装用户信息
SysLog sysLog = new SysLog();
//1.9获取ip地址
String ip=IPUtils.getIpAddr();//工具类暂且不需要理解
//2.封装日志信息
SysLog log=new SysLog();
log.setUsername(username);
log.setIp(ip);
log.setOperation(operation);
log.setMethod(method);
log.setParams(Arrays.toString(jp.getArgs()));
log.setTime(time);
log.setCreatedTime(new Date());
//3.持久化日志信息
sysLogDao.insertObject(log);
}
}
(1.1.5)代理类实现接口的 @Before,@After,@AfterReturning,@AfterThrowing5,@Around大注解执行顺序:
代码如下:
`@Order(2)
@Aspect
@Service
public class Otheraspect {
@Pointcut(“bean(SysUserServiceImpl)”)
public void doPointCut(){}
@Before(" doPointCut()")
public void beforeMethod(){
System.out.println("@Before");
}
@After(" doPointCut()")
public void afterMethod(){
System.out.println("@After");
}
@AfterReturning(" doPointCut()")
public void returnMethod(){
System.out.println("@AfterReturning");
}
@AfterThrowing(" doPointCut()")
public void throwMethod(){
System.out.println("@AfterThrowing");
}
@Around(" doPointCut()")
public Object arroundMethod(ProceedingJoinPoint jp) throws Throwable{
try {
System.out.println("@Around before");
Object obj = jp.proceed();
System.out.println("@Around afterReturning");
return obj;
} catch (Exception e) {
System.out.println("@Around afterthrowing");
throw e;
}finally{
System.out.println("Around after");
}
}
}`
(1.1.5.1)第一种情况当程序正常运行时:
代码运行结果:
(1.1.6.1)第二种情况当程序没有正常运行时:
代码运行结果:
- 假如目标对象(被代理对象)没有实现接口,则底层默认采用CGLIB代理机制为目标对象创建代理对象(默认创建的代理类会继承目标对象类型)。
如图(2):
图片绿色的都为底层原理及调用
这是一个Dome类测试的TestCglibo1代码如下 :
//目标类
class GuanggaoService{
public void Service(){
System.out.println("广告");
}
}
//CGLIB代理
public class TestCglibo1 {
public static void main(String[] args) {
//创建目标对象
GuanggaoService target = new GuanggaoService();
//2.为目标对象创建代理对象
//2.1创建Enhancer对象,通过此对象为目标对象创建代理对象
Enhancer en = new Enhancer();
en.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodproxy) throws Throwable {
System.out.println("en:"+en);
Object result = methodproxy.invokeSuper(obj, args);
return result;
}
});
//设置代理对象的父类类型
en.setSuperclass(target.getClass());
//创建代理对象
GuanggaoService proxy = (GuanggaoService) en.create();
//调用代理对象的方法、
proxy.Service();
}
}