前言
这里分析的是注解方式的aop, 流程图从网上找的,作为参考。
流程
图1:初始化advice过程
图2: 代理bean过程
图3:被aop代理的类被调用的执行过程
准备
添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>springAopTest</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<version>2.4.0</version>
</dependency>
</dependencies>
</project>
创建一个目标类
@Component
public class TargetClass {
public String test(String value) {
System.out.println("目标方法test被执行");
if (!StringUtils.hasLength(value)) {
throw new RuntimeException("value不能为空");
}
return value;
}
}
编写切面类
@Aspect
@Component
public class MyAspect {
@Pointcut("execution(public * TargetClass.test(..))")
public void pointcut(){
}
@Before("pointcut()")
public void onBefore(JoinPoint joinPoint) {
System.out.println("onBefore:" + joinPoint.getSignature().getName() + "方法开始执行,参数:"
+ Arrays.asList(joinPoint.getArgs()));
}
@After("pointcut()")
public void onAfter(JoinPoint joinPoint) {
System.out.println("onAfter:" + joinPoint.getSignature().getName() + "方法执行结束,参数:"
+ Arrays.asList(joinPoint.getArgs()));
}
}
拦截器执行顺序
-
Spring4.x
- 正常情况:@Before —-> 目标方法 —-> @After —-> @AfterReturning
- 异常情况:@Before —-> 目标方法 —-> @After —-> @AfterThrowing
-
Spring5.x
- 正常情况:@Before —-> 目标方法 —-> @AfterReturning —-> @After
- 异常情况:@Before —-> 目标方法 —-> @AfterThrowing —-> @After
加载自动代理创建类
@EnableAspectJAutoProxy(自动加载)
在springboot中,通过@EnableAspectJAutoProxy,@import导入AspectJAutoProxyRegistrar,这个类加载了AnnotationAwareAspectJAutoProxyCreator。
图4:AnnotationAwareAspectJAutoProxyCreator继承关系图
通过AnnotationAwareAspectJAutoProxyCreator继承关系图中看出,这个是实现了BeanPostProcess接口,那么推测Bean代理可能发生在postProcessBeforeInstantiation方法或postProcessAfterInitialization方法。
生成代理对象
执行AnnotationAwareAspectJAutoProxyCreator
初始化之前
先看postProcessBeforeInitialization这个方法,这个方法主要看bean是不是advise(切面),如果是放入advicedBeans这个map中。(返回null标识继续初始化,如果返回对象则不进行初始化)
初始化之后
进入postProcessAfterInitialization方法,首先获取到当前bean对象匹配的切面数组,
在执行createProxy这里有一个SingletonTargetSource,这里把bean包装成为一个单例的代理对象。
然后把bean匹配的切面生成切面对象数组,还有把SingletonTargetSource(目标对象也就是我们的被代理的bean)set到ProxyFactory, 到createAopProxy方法中生成一个具体的Proxy,如果在spring配置中 @EnableAspectJAutoProxy(proxyTargetClass = true)或者spring.aop.proxy-target-class=true,则强制启用cglib来生成代理对象,即ObjenesisCglibAopProxy,如果bean实现了接口,则用JdkDynamicAopProxy,如果没有实现接口,则用ObjenesisCglibAopProxy。
如何生成代理对象的
这里只是根据我自己的理解简单总结一下,以后有精力了详细看参考链接里的文章
JdkDynamicAopProxy
这里就是调用Proxy.newProxyInstance()方法,生成一个名为$Proxy的对象,这里通过重写编写了一个字节码文件加载到ClassLoader里,我们的原方法里面的内容则变成这个变成了InvocationHandler.invoke(), JdkDynamicAopProxy继承了InvocationHandler,所以实际执行的是JdkDynamicAopProxy.invoke()里面的方法。
参考链接:
ObjenesisCglibAopProxy
这个比较复杂,也没看懂,放个参考链接
参考链接: spring aop创建代理对象
调用对象
CglibAopProxy
生成targetSource(目标对象)的方法拦截链(责任链模式),如果拦截链不为空,则创建一个CglibMethodInvocation对象,执行它的process方法。
调用父类ReflectiveMethodInvocation proceed方法,程序第一次进该方法时currentInterceptorIndex值为-1,this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex)取出拦截器链第一个拦截器ExposeInvocationInterceptor(默认拦截器,系统自动加上的),方法最后调用该拦截器的invoke方法。
拦截器链都是MethodInterceptor的实现类,常用的是MethodBeforeAdviceInterceptor、AspectJAfterAdvice、AfterReturningAdviceInterceptor和AspectJAfterThrowingAdvice
这个这个mi其实是ReflectiveMethodInvocation,所以又会到了proceed方法,然后第二次是一个MethodBeforeAdviceInterceptor,再次执行invoke(this)
然后返回到ReflectiveMethodInvocation proceed方法, 然后第三次是一个AspectJAfterAdvice,在此调用invoke方法
再次此调用ReflectiveMethodInvocation.proceed()方法,
然后这时inde和拦截链的长度是一致的了,然后调用invokeJoinpoint()执行target(目标类方法),然后就会递归的方式来到AspectJAfterAdvice finally执行invokeAdviceMthod方法,然后回到MethodBeforeAdviceInterceptor,再回到ExposeInvocationInterceptor
最后回到CglibAopProxy.intercept方法,之后随之方法intercept方法执行结束,整个AOP的拦截器执行链也结束了。
总结
在SpringBoot中以注解的方式,首先通过通过@EnableAspectJAutoProxy, @import导入AspectJAutoProxyRegistrar,这个类加载了AnnotationAwareAspectJAutoProxyCreator。
在Spring Bean 生命周期过程中,初始化之前执行postProcessBeforeInstantiation方法时把是advise(切面)放bean放入advisedBeans缓存中,初始化之后执行postProcessAfterInstantiation方法时把需要代理的bean,如果在spring配置中 @EnableAspectJAutoProxy(proxyTargetClass = true)或者spring.aop.proxy-target-class=true,则强制启用cglib来生成代理对象,即ObjenesisCglibAopProxy,如果bean实现了接口,则用JdkDynamicAopProxy,如果没有实现接口,则用ObjenesisCglibAopProxy,返回代理对象。
以Cglib代理为例,在实际调用target(目标代理对象)的方法,首先执行的是CglibAopProxy.DynamicAdvisedInterceptor.intercept方法,生成target的拦截链,拦截链都实现了MethodInterceptor接口的invoke方法,然后以递归的方式调用责任链的执行自定义的advice方法,最后完成Aop拦截器执行链。
参考链接: