对Spring AOP的进一步深入理解
Posted 敲代码的小小酥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对Spring AOP的进一步深入理解相关的知识,希望对你有一定的参考价值。
前言
从AOP的开启,到切面生成,再到代理类注入,最后增强方法的调用整个流程做一个整理和理解。将Spring AOP功能整体串联起来。
@EnableAspectJAutoProxy开启AOP功能
前面已经研究过这个注解原理:Spring之@EnableAspectJAutoProxy开启AOP功能原理
简单来说,就是这个注解通过@Import注解向Spring容器注入了一个BeanDefinition对象,这个BeanDefinition对象实例化后是AnnotationAwareAspectJAutoProxyCreator类,即AOP的入口类,这个类是一个BeanPostProcessor类。
AOP入口类工作原理
AnnotationAwareAspectJAutoProxyCreator是一个BeanPostProcessor类。那么在Spring容器bean实例化过程中,会调用postProcessBeforeInitialization方法和postProcessAfterInitialization方法。AOP入口是在postProcessAfterInitialization方法切入的,下面看这个方法源码:
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName)
if (bean != null)
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) //这里的判断跟循环依赖有关,如果涉及到循环依赖,则在这里之前就生成了循环依赖对象的代理对象。这里不考虑这种情况,直接执行wrapIfNecessary方法
return wrapIfNecessary(bean, beanName, cacheKey);
return bean;
下面看wrapIfNecessary方法源码:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey)
// ......省略......
// Create proxy if we have advice.
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY)
this.advisedBeans.put(cacheKey, Boolean.TRUE);
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
// ......省略......
截取重点代码,可以看到,首先收集正在实例化bean的Advisors(切面)和Adivces(增强),如果收集到了,则调用createProxy方法创建一个代理对象并返回。道理都懂,那么这个返回的代理对象,是如何与收集到的Advisors(切面)和Adivces(增强)交互的呢?这就需要查看createProxy方法源码:
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource)
// ...........省略..............
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered())
proxyFactory.setPreFiltered(true);
// Use original ClassLoader if bean class not locally loaded in overriding class loader
ClassLoader classLoader = getProxyClassLoader();
if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader())
classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
return proxyFactory.getProxy(classLoader);
可以看出,这里找出了bean对应的所有advisors对象,然后在生成代理对象定义增强时,执行增强代码。生成的代理对象返回给Spring容器中。
调用代理对象方法
调用代理对象的方法,最终走到的是ReflectiveMethodInvocation类的process()方法,该方法里维护了代理对象每个方法的一个拦截器链,拦截器链就是对原方法的增强。具体流程参考另一篇博客:Spring之Joinpoint类详解
这里需要解释的一点就是,为何代理方法执行的时候会执行ReflectiveMethodInvocation的process方法呢?一定是在生成代理对象时,不管是jdk代理对象还是cglib代理对象,设置增强的回调函数时,设置成了ReflectiveMethodInvocation的process方法。拦截器链也是在设置回调函数时,根据获取到的advisors生成了拦截器链,只不过这部分代码,没有继续追踪源码,而是猜想而来的。Spring一定是按照这个逻辑,进行的AOP代理。
综上所述,AOP的核心思想就是代理模式的应用。以BeanPostProcessor为切入,生成bean的代理对象。获取到代理对象的切面,并在代理的回调函数中执行切面中的增强方法。这样,在bean实例化完成后,调用其方法时,就走回调函数,然后根据拦截器链,执行增强方法。
感悟
上面讲到,通过 @EnableAspectJAutoProxy开启AOP功能的本质,就是这个注解@Import到Spring容器中一个BeanPostProcessor,这个BeanPostProcessor具有AOP功能。其他开启功能的注解思路和这个都一致。本质就是往Spring容器中添加bean,然后这些bean就具备了要开启的功能。
只不过添加bean的方式略有不同。比如可以通过registry BeanDefinition方式加入,也可以通过FactoryBean方式加入,或者直接将BeanPostProcessor加入Spring容器等等。这些不同方式的加入,都是由开启功能的注解中@Import进来的类而决定的。
@Async中AOP的应用
我们自定义切面时,通过@Aspect来标注切面。那么第三方的注解,如@Async、@Transaction、@Cache等注解,是如何进行AOP操作的呢?不同注解实现方式略有不同,但是核心思想都是收集这些注解,然后生成注解所在类的切面。切点就是注解修饰的方法,而增强就是注解要增强的功能逻辑。以@Async为例进行说明:
具体原理参考:一文彻底讲透@Async注解的原理和使用方法
简单来说,就是通过@EnableAsync注解开启异步功能,这个注解将AsyncAnnotationBeanPostProcessor类加入了Spring容器,而这个类里,自己创建了Advisor,并设置了增强Advice。而切点PointCut就是注解修饰的方法。自己生成切面后,在AOP收集切面时,就会收集到这个切面,然后在实例化bean时,如果这个bean有切面,则生成代理对象,代理方法的增强就是@Async定义的异步增强。
画外音
之前一直认为一个对象,如果有接口,AOP生成代理对象时就用jdk动态代理,如果没有接口,则就用cglib代理。而在研究本篇博客内容时发现,在SpringBoot项目中,无论是否有接口,默认生成的代理对象都是cglib代理对象。查阅网上资料发现是在SpringBoot2.x版本,默认都使用了cglib代理来规避jdk动态代理面向接口代理而产生的一些问题。详情自行百度即可,这里只是记录一下这个情况。
以上是关于对Spring AOP的进一步深入理解的主要内容,如果未能解决你的问题,请参考以下文章