Spring源码——AOP实现原理
引言
Spring AOP(Aspect Orient Programming),AOP翻译过来就是面向切面编程,它体现的是一种编程思想,是对面向对象编程(OOP)的一种补充。
在实际业务开发过程中,有一些代码,跟业务没有任何关系,但在很多地方又会用到,比如:记录日志、计算执行时间、事务、权限验证等等,这些代码跟我们实际业务没有一丁点关系,但在许多的地方又会用到,我们传统的方式,肯定是通过复制粘贴把它粘进需要用到的地方,但这样做,会对原本的业务代码块造成很高侵入性,使代码的阅读性、可维护性变得很糟糕,所以就延伸出来了AOP的概念,它不是针对于某个编程语言、某段代码、而是体现了一种编程思想,通过切面的方式将非业务代码切入到核心业务处理流程中,减少对业务代码的修改,降低对业务代码的侵入程度。
Spring作为一个Bean对象的管理框架,提供了AOP功能集成的扩展接口(比如:事务)。
AOP动态代理
AOP是借助了动态代理的实现机制,通过为需要被切入的对象生成动态代理的方式来实现的。动态代理相关原理请参考笔者另一篇博客———设计模式——动态代理
AOP相关术语
AOP用到的一些专业术语
1. 通知(Advice)
AOP 框架中的增强处理。通知描述了切面何时执行以及如何执行增强处理。
2. 连接点(join point)
连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出。在 Spring AOP 中,连接点总是方法的调用。
3. 切点(PointCut)
可以插入增强处理的连接点。
4. 切面(Aspect)
切面是通知和切点的结合。
5. 引入(Introduction)
引入允许我们向现有的类添加新的方法或者属性。
6. 织入(Weaving)
将增强处理添加到目标对象中,并创建一个被增强的对象,这个过程就是织入。
Spring AOP
在Spring对Bean对象的创建过程中,会将已实例化但尚未初始化的 Bean对象(半成品),最先放入到三级缓存(singletonFactories)中,而在放入的时候,并不是放入的一个实例对象那么简单,它是将获取这个半成品的工厂方法给放入了三级缓存中,然后再对这个半成品进行属性填充、初始化等操作,这里也是解决循环依赖的核心所在,在后续代码执行中,就可以通过beanName在三级缓存中,获取到这个ObjectFactory对象,通过调用ObjectFactory的getObject方法,获取之前创建的半成品。
而对于AOP对象的动态代理对象的创建,也就是在这个从三级缓存(半成品) 放入 二级缓存(成品的动态代理)过程中的进行处理的。
以下doCreateBean方法里省略了部分代码
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException{
...
// 判断当前bean是否需要提前曝光:单例&允许循环依赖&当前bean正在创建中,检测循环依赖
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isTraceEnabled()) {
logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
// 为避免后期循环依赖,可以在bean初始化完成前将创建实例的ObjectFactory加入工厂
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
...
allowCircularReferences这个值默认等于true。以上代码调用了addSingletonFactory方法,实际上它是向三级缓存中添加了一个lambd表达式
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
// 使用singletonObjects进行加锁,保证线程安全
synchronized (this.singletonObjects) {
// 如果单例对象的高速缓存【beam名称-bean实例】没有beanName的对象
if (!this.singletonObjects.containsKey(beanName)) {
// 将beanName,singletonFactory放到单例工厂的缓存【bean名称 - ObjectFactory】
this.singletonFactories.put(beanName, singletonFactory);
// 从早期单例对象的高速缓存【bean名称-bean实例】 移除beanName的相关缓存对象
this.earlySingletonObjects.remove(beanName);
// 将beanName添加已注册的单例集中
this.registeredSingletons.add(beanName);
}
}
}
lambd表达式里面的方法,getEarlyBeanReference(beanName, mbd, bean)方法源码
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
// 默认最终公开的对象是bean,通过createBeanInstance创建出来的普通对象
Object exposedObject = bean;
// mbd的systhetic属性:设置此bean定义是否是"synthetic",一般是指只有AOP相关的pointCut配置或者Advice配置才会将 synthetic设置为true
// 如果mdb不是synthetic且此工厂拥有InstantiationAwareBeanPostProcessor
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
// 遍历工厂内的所有后处理器
for (BeanPostProcessor bp : getBeanPostProcessors()) {
// 如果bp是SmartInstantiationAwareBeanPostProcessor实例
if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
// 让exposedObject经过每个SmartInstantiationAwareBeanPostProcessor的包装
exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);
}
}
}
// 返回最终经过层次包装后的对象
return exposedObject;
}
三级缓存中添加的是一个获取对象的lambd表达式,所以这个表达式只是添加进去了,并不会立刻运行,以上就是lambd表达式里面的内容
在lambd表达式里面,我们看到SmartInstantiationAwareBeanPostProcessor这样的一个接口,它属性BPP。
对于每个Bean对象都会遍历BPP,如果是BPP是SmartInstantiationAwareBeanPostProcessor的实现类,就会运行它的getEarlyBeanReference(exposedObject, beanName)方法。
而对于开启了AOP注解(@EnableAspectJAutoProxy)功能的项目,会添加一个AnnotationAwareAspectJAutoProxyCreator,它就属性SmartInstantiationAwareBeanPostProcessor(BPP)
看到这里,大家就应该明白了,AOP功能也是通过对BPP的扩展来实现的。
AnnotationAwareAspectJAutoProxyCreator继承了父类AbstractAutoProxyCreator中的getEarlyBeanReference方法
/**
* 放到集合中,然后判断要不要包装,其实就是在循环依赖注入属性的时候如果有AOP代理的话,也会进行代理,然后返回
* @param bean the raw bean instance
* @param beanName the name of the bean
* @return
*/
@Override
public Object getEarlyBeanReference(Object bean, String beanName) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
this.earlyProxyReferences.put(cacheKey, bean);
return wrapIfNecessary(bean, beanName, cacheKey);
}
核心代码wrapIfNecessary(bean, beanName, cacheKey),到此,我们大致明白了,AOP的基本处理流程了(其实因为AnnotationAwareAspectJAutoProxyCreator属于BPP,在执行BPP的后置处理器有时候也有机会执行wrapIfNecessary方法,并返回代理对象)
/**
* 先判断是否已经处理过,是否需要跳过,跳过的话直接就放进advisedBeans里,表示不进行代理,如果这个bean处理过了,获取通知拦截器,然后开始进行代理
*
* Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
* @param bean the raw bean instance
* @param beanName the name of the bean
* @param cacheKey the cache key for metadata access
* @return a proxy wrapping the bean, or the raw bean instance as-is
*/
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 如果已经处理过,直接返回
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 这里advisedBeans缓存了已经进行了代理的bean,如果缓存中存在,则可以直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 这里isInfrastructureClass()用于判断当前bean是否为Spring系统自带的bean,自带的bean是
// 不用进行代理的;shouldSkip()则用于判断当前bean是否应该被略过
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
// 对当前bean进行缓存
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
// 获取当前bean的Advices和Advisors
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
// 对当前bean的代理状态进行缓存
if (specificInterceptors != DO_NOT_PROXY) {
// 对当前bean的代理状态进行缓存
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 根据获取到的Advices和Advisors为当前bean生成代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
// 缓存生成的代理bean的类型,并且返回生成的代理bean
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
Spring AOP生成代理类时JDK代理和CGLIB代理的选择
/**
* 真正的创建代理,判断一些列条件,有自定义的接口的就会创建jdk代理,否则就是cglib
* @param config the AOP configuration in the form of an
* AdvisedSupport object
* @return
* @throws AopConfigException
*/
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 这段代码用来判断选择哪种创建代理对象的方式
// config.isOptimize() 是否对代理类的生成使用策略优化 其作用是和isProxyTargetClass是一样的 默认为false
// config.isProxyTargetClass() 是否使用Cglib的方式创建代理对象 默认为false
// hasNoUserSuppliedProxyInterfaces目标类是否有接口存在 且只有一个接口的时候接口类型不是SpringProxy类型
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
// 上面的三个方法有一个为true的话,则进入到这里
// 从AdvisedSupport中获取目标类 类对象
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
// 判断目标类是否是接口 如果目标类是接口的话,则还是使用JDK的方式生成代理对象
// 如果目标类是Proxy类型 则还是使用JDK的方式生成代理对象
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 配置了使用Cglib进行动态代理或者目标类没有接口,那么使用Cglib的方式创建代理对象
return new ObjenesisCglibAopProxy(config);
}
else {
// 使用JDK的提供的代理方式生成代理对象
return new JdkDynamicAopProxy(config);
}
}
Spring AOP生成动态代理大致流程如下
两种代理方式执行逻辑
AOP执行逻辑
AOP代理方式有两种:JDK代理 和 CGLIB代理
JdkDynamicAopProxy
JdkDynamicAopProxy类是实现了反射中的InvocationHandler接口
所以调用过程中的核心执行方法在invoke方法中
invoke方法中核心代码块
// We need to create a method invocation...
// 将拦截器封装在ReflectiveMethodInvocation,以便于使用其proceed进行处理
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
// 执行拦截器链
retVal = invocation.proceed();
看得出来,它的执行是交给一个MethodInvocation去执行的,而ReflectiveMethodInvocation是一个执行器链,里面根据我们的配置有可能包含了以下五种通知
采用责任链模式对自定义配置的五种通知进行依次调用