【Spring源码分析】Spring之AOP底层源码解析(含@Async源码解析)
一、预备知识
在看源码前需要点前置知识,不然源码也很难进行下去。
第一点:
首先是需要知道 Spring AOP 是对面向切面编程这种思想的一种实现,而这种实现其他相关技术也是可以支持的,而Spring AOP中注解(Pointcut、Before、Around…)是直接拿 AspectJ 来用的,也仅仅是拿来用,但是Spring启动时候对这些注解是解析,都是Spring自己去做的,这也是 Spring 和 AspectJ 的关系,只是拿它规范的注解来用而已。
第二点:
AOP的概念:
- Aspect(Advisor):表示切面,就比如@Aspect注解修饰的类就是切面,或者说实现了Advisor接口;
- Join Point:表示连接点,表示一个程序在执行过程中的一个点,比如一个方法的执行;
- Advice:表示通知,表示在一个特定连接点上所采取的动作;
- Pointcut:表示切点,用来匹配多个连接点的;
- Target Object:目标对象,被代理的对象;
- AOP Proxy:表示代理工厂,用来创建代理对象的,在Spring Framework 中,要么是 JDK 动态代理,要么是 CGLIB 代理
第三:
针对上面的通知 Advice,表示代理逻辑,一般有以下注解修饰,然后被解析成对应的Advice类:
- @Before:AspectJMethodBeforeAdvice,实际上就是 MethodBeforeAdvice;
- @AfterReturning:AspectJAfterReturningAdvice,实际上就是一个AfterReturningAdvice
- @AfterThrowing:AspectJAfterThrowingAdvice,实际上就是一个MethodInterceptor
- @After:AspectJAfterAdvice,实际上就是一个MethodInterceptor
- @Around:AspectJAroundAdvice,实际上就是一个MethodInterceptor
二、ProxyFactory
(为什么说它,是因为Spring中的AOP或者所用到了动态代理的逻辑里,如@Async,@Lazy…都是通过 ProxyFactory去获取对应的代理实例的)
- 它getProxy的逻辑就是,先判断是使用哪个 AopProxy,JdkDynamicAopProxy 或 ObjenesisCglibAopProxy
- 随后调用对应的 AopProxy#getProxy方法获取对应的代理对象
AopProxy的获取
先解释解释它,再源码分析它
Spring AOP 的实现用了俩种动态代理方式,一个是 JDK 动态代理,一个是 CGLIB 动态代理;这俩种代理如何使用都是由 ProxyFactory 去决定的(就是最后获得的AopProxy是JDK的还是CGLIB的)。源码当中就是通过 ProxyFactory 去生成代理实例的。
使用如下:
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new UserService());
proxyFactory.addAdvisor(new UserServiceAdvisor());
proxyFactory.setInterfaces(new Class[]{TestService.class});
UserService proxy = (UserService) proxyFactory.getProxy();
proxy.aaa();
}
// 可以看见使用ProxyFactory需要传递Advisor和Target
原理:
ProxyFactory会给我们去判断,如果实现了接口,那么ProxyFactory底层就会使用 Jdk 动态代理,如果没有实现接口,就会用 CGLIB 技术。(其实按照我们的编码习惯,一般容器的Bean都实现了接口的)
源码如下:
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 如果ProxyFactory的isOptimize为true,Spring认为cglib比jdk动态代理要快
// 或者isProxyTargetClass为true,
// 或者被代理对象没有实现接口,
// 或者只实现了SpringProxy这个接口
// 那么则利用Cglib进行动态代理,但如果被代理类是接口,或者被代理类已经是进行过JDK动态代理而生成的代理类了则只能进行JDK动态代理
// 其他情况都会进行JDK动态代理,比如被代理类实现了除SpringProxy接口之外的其他接口
// 是不是在GraalVM虚拟机上运行
if (!NativeDetector.inNativeImage() &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
这个是否有接口不是ProxyFactory去解析判断的,是判断我们外面是否通过 addInterface 向 ProxyFactory 中的 interfaces 集合中加了对应的接口。AOP进行代理的时候会进行如下,即是手动加的:
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/8c2c45f5200e44a1b9b1576de409d2e3.pn
- optimize 默认是 false;
- proxyTargetClass 也是默认是 false;
就是说默认情况下,只要对应代理类实现了接口,就会使用 JDK 的动态代理,而没有实现接口就会使用CGLIB的动态代理。
对应 AopProxy#getProxy() 的实现
先看 JdkDynamicAopProxy#getProxy() 的逻辑,很简单就是Proxy去new一个代理对象,需要注意的是把this传递进了InvocationHandler参数上,说明JdkDynamicAopProxy实现了InvocationHandler:
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
// this实现了InvocationHandler
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
接下来分析重点:
JdkDynamicAopProxy#invoke()
只分析核心代码:
// 被代理对象和代理类
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 代理对象在执行某个方法时,根据方法筛选出匹配的Advisor,并适配成Interceptor
// 获取Advice链
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// 如果没有Advice,则直接调用对应方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 如果Advice链不为空,就交给ReflectiveMethodInvocation处理
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
- 先是获取到代理类和代理实例;
- 然后去根据方法去筛选出匹配的 Advisor(注意这是invoke的逻辑,是根据Method去找对应的…),并适配成对应的Interceptor,就是Advice;
- 然后判断chain是否是空,就是判断有没有匹配的 Advisor 的意思,没有就直接执行方法,有就交个 ReflectiveMethodInvocation
获取方法匹配的 Advice 逻辑如下:
- 先是拿到设置到ProxyFactory中的所有Advisor;
- 然后拿Advisor中的Pointcut去筛选,先根据classFilter筛选类,然后根据MethodMatcher去筛选方法;
- 最后封装成 MethodInterceptor 放入集合中进行返回。
- 就是说上面那集合里的都是MethodInterceptor对象,它是一个函数式接口,里面就一个invoke方法,参数是MethodInvocation,注意和ReflectiveMethodInvocation类型匹配。
然后如果返回的这个匹配到的通知链有的话就会去调用 ReflectiveMethodInvocation#proceed 方法,其源码如下:
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
// currentInterceptorIndex初始值为-1,每调用一个interceptor就会加1
// 当调用完了最后一个interceptor后就会执行被代理方法
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// currentInterceptorIndex初始值为-1
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
// 当前interceptor是InterceptorAndDynamicMethodMatcher,则先进行匹配,匹配成功后再调用该interceptor
// 如果没有匹配则递归调用proceed()方法,调用下一个interceptor
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
// Evaluate dynamic method matcher here: static part will already have
// been evaluated and found to match.
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
// 动态匹配,根据方法参数匹配
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
// Dynamic matching failed.
// Skip this interceptor and invoke the next in the chain.
// 不匹配则执行下一个MethodInterceptor
return proceed();
}
}
else {
// It's an interceptor, so we just invoke it: The pointcut will have
// been evaluated statically before this object was constructed.
// 直接调用MethodInterceptor,传入this,在内部会再次调用proceed()方法进行递归
// 比如MethodBeforeAdviceInterceptor
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
有点像责任链模式,但又不完全是,因为是通过了这个proceed进行过渡的。简单画个简图吧:
说白了就是先执行的MethodInterceptor,压栈,然后最后执行被代理的方法,最后慢慢对MethodInterceptor进行出栈,就这么个过程。
AOP 其底层实现逻辑是一样的,只是构造代理对象是使用继承类的方式,而JDK是采用实现接口的方式,CGLIB是基于ASM的,相比之下性能要比JDK要高,但在这解析动态代理这里,其实没有啥本质区别。
三、AOP源码分析
首先呢,一般我们是在配置类上加上@EnableAspectJAutoProxy
去开启AOP(看了前面源码就知道,Spring容器的都可以称为配置类)
@EnableAspectJAutoProxy 导入了一个 AspectJAutoProxyRegister
而它就是把 AnnotationAwareAspectJAutoProxyCreator 注册成BeanDefinition,然后在Spring解析完这个配置类后,下一步就是实例化BeanPostProcessor了,就会把这个实例化放入Spring容器中了(在阐述Spring启动流程阐述了)。
就是说咱只要分析 AnnotationAwareAspectJAutoProxyCreator 源码就好了,类关系图如下:
它主要是在Bean的生命周期中的初始化后那个时候会进行AOP的分析。
-
先获取容器类的Advisor
- 容器中实现Advisor接口的;
- 被@Aspect注解修饰了的,将其@Before、@After、@AfterReturn、@AfterThrowing修饰的方法封装成Advice然后进行返回;
- 切点Aspect嘛,Pointcut和Advice组成的;
-
然后就是构建一个 ProxyFactory 实例,将这个 Advisor 集合放入进去,最后调用 ProxyFactory#getProxy 获取对应的代理对象,就OK了;
四、Async源码解析
我们要使用 @Async 的话需要加 @EnableAsync,
这个注解也会注入一个BeanPostProcessor。
我找解析地方找了我半天,它是实现了BeanFactoryAware
接口,它是在初始化前去执行对应Aware方法的时候构建了一个Advisor:
通知逻辑不难,就是把这个方法逻辑封装成Callable对象,然后直接doSubmit,是直接用的线程池去执行,配合的CompletableFuture。
但是你看看它的Executor
那一直创建线程,不就浪费吗,所以使用@Async的时候记得自己定义一个Executor到容器中。
这个构造的Pointcut匹配也比较简单,就是判断方法上有没有@Async注解的。
那初始化后方法实现更简单:
五、总结
-
使用 @EnableAspectJAutoProxy 开启SpringAOP;
-
Spring AOP 本质是通过动态代理去实现的,它内部是使用的 ProxyFactory 去获取的代理对象,ProxyFactory 支持 JDK 和 CGLIB 动态代理,Spring AOP 在实例化 ProxyFactory 的时候,其选择逻辑主要和 optimize、proxyTargetClass、是否向ProxyFactory填写了这个类实现的接口,有的话为false,但三个参数都空false时,就会选择JDK动态代理,反之去实现CGLIB动态代理。optimize、proxyTargetClass 默认都是 false。Spring在创建ProxyFactory的时候涉及到如下参数变化:
就是只有当实现的接口有方法的时候才会往proxyFactory中添加接口,也就是走JDK动态代理,如果没的话就把ProxyTargetClass调成true,表示走CGLIB动态代理。
如果我们所有的代理对象都使用CGLIB动态代理,那我们配置时:@EnableAspectJAutoProxy(proxyTargetClass = true)
即可。(CGLIB加载慢但是执行快,JDK的话加载快,但是执行慢)如果可以的话还是建议把 proxyTargetClass 调整为 true,一是执行方法快,二是不会出现类型转换错误风险。
- 当代理对象执行方法的时候,首先是去找到ProxyFactory中符合的Advisor,通过里面的Pointcut去筛选,然后将里面的Advice封装成MethodInterceptor对象,最后行成一个 MethodInterceptor 链;
- 最后先执行MethodInterceptor链,然后执行对应的方法;
- 在使用 @EnableAspectJAutoProxy 的时候会往容器中注入一个
AnnotationAwareAspectJAutoProxyCreator
,它会在实例化某Bean的初始化后阶段把符合这个Bean里面方法的Advisor都找到封装起来,然后通过ProxyFactory去得到对应代理对象进行返回。
使用 @EnableAsync 开启 @Async 注解的解析;
- 使用 @EnableAsync 注解会向容器中注入一个 AsyncAnnotationBeanPostProcessor,这个实现了 BeanFactoryAware 接口;
- 那么在初始化前的时候会执行BeanFactoryAware这个实现的方法,在这个方法里,会实例化一个 Advisor 赋值给属性,由对应Pointcut和Advice组成,Pointcut就是去匹配@Async注解,Advice就是使用Executor去执行那个方法;
- 而在真正初始化后,会去拿到这个Advisor去判断当前实例化的Bean中有无符合Pointcut的方法,有封装成对应的Advisor然后返回通过ProxyFactory返回一个代理对象。
- 这里需要注意的就是,Advice里使用的Executor如果容器里没有TaskExecutor类型的Bean的话,Advice使用的是一个SimpleAsyncTaskExecutor,它是新new一个线程去执行的,这样会造成资源的浪费,所以想使用@Async的话,记得向容器里放一个Executor的Bean。