概述
上篇文章中通过一个简单示例介绍了AOP的简单用法,并结合示例说明了AOP中使用到的切面,切点以及切面的增强动作等内容,本篇文章开始,将深入分析注解版AOP的底层实现原理。
使用注解版的AOP实现切面功能时,AOP功能如果要生效,必须先在配置类上使用注解开启AOP支持,使用到的注解就是@EnableAspectJAutoProxy,配置类如下:
@Configuration
@ComponentScan(basePackages = "com.wb.spring.aop")
// 开启AOP支持
@EnableAspectJAutoProxy
public class AopConfig {
}
一、AOP的前期初始化过程
1.1、后置处理器初始化过程分析
那么**@EnableAspectJAutoProxy**注解究竟干了什么事呢?打开其源码实现,如下:
// 实现了ImportBeanDefinitionRegistrar接口,用来在Spring启动时给bean注册中心自定义注册组件
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 如果有需要,则给容器中注册一个AspectJAnnotationAutoProxyCreator组件.
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
// 获取EnableAspectJAutoProxy注解上标注的属性值
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata,
EnableAspectJAutoProxy.class);
// ... 其他源码暂时省略
}
}
这个组件是给容器的bean定义注册中心自定义注册一个bean组件,通过调用AOP工具类中AopConfigUtils中的registerAspectJAnnotationAutoProxyCreatorIfNecessary 方法完成。
继续查看方法registerAspectJAnnotationAutoProxyCreatorIfNecessary的实现,内容总共分为如下的三步,最终的效果就是给容器中注册一个名称为internalAutoProxyCreator,类型为AnnotationAwareAspectJAutoProxyCreator的bean组件。
public abstract class AopConfigUtils {
// 第一步:调用该方法给容器中注册一个bean组件
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
}
// 第二步:再次调用该方法,传入bean组件的类型为:AnnotationAwareAspectJAutoProxyCreator的bean组件
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
/**
* 第三步:最后调用这个方法给容器中注册一个名称为org.springframework.aop.config.internalAutoProxyCreator,
* 类型为AnnotationAwareAspectJAutoProxyCreator的组件
*/
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
// cls = AnnotationAwareAspectJAutoProxyCreator.class
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// 判断容器中是否有名称为org.springframework.aop.config.internalAutoProxyCreator的Bean定义,第一次运行的时候没有.
if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
}
// 创建一个AnnotationAwareAspectJAutoProxyCreator类型的Bean定义
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
// 设置最高优先级.
beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 设置bean定义的名称为:‘org.springframework.aop.config.internalAutoProxyCreator’
registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
return beanDefinition;
}
// ... 工具类中的其他代码暂时省略.
}
所以,重点就转移到了这个类型为AnnotationAwareAspectJAutoProxyCreator的bean组件,这个组件的功能及运行时机搞清楚,整个AOP的原理就清楚了,后续的其他类似使用注解@Enablexxx实现的功能,例如注解版事务@EnableTransactionManagement,套路都是一样,都是给容器中注册一些组件,然后在某个时机执行对应的方法。深究其底层原理其实通过查看该注解给容器中注入的组件就可以了。然后继续查看这个组件的源码,发现这个类的继承关系特别特别深,怎么看呢?首先列出其继承关系,如下:
从继承关系图中可以看到,这个类最终是实现了BeanPostProcessor和 Aware 接口的,在之前文章中介绍过Spring的扩展原理,Spring的一个最强大的功能就是可以支持灵活扩展,提供了很多扩展点,这两个接口就是Spring中提供的两个重要扩展点,而且这两个扩展点对应的方法会在Bean的创建过程中被调用。
所以,重点再一次转移到和这些后置处理器相关的方法上,查看方法的实现中都有什么功能,对于以上的三个扩展接口,对应的扩展方法如下:
BeanFactoryAware接口的扩展方法如下:
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
BeanClassLoaderAware接口的扩展方法如下:
void setBeanClassLoader(ClassLoader classLoader);
SmartInstantiationAwareBeanPostProcessor接口继承了两个接口,其包括的扩展方法如下:
// 下面前两个方法是BeanPostProcessor中的两个方法
// 该方法是Spring后置处理器中的方法
// 该方法是在Bean实例化(new)之后,初始化(设置各种属性)之前会被调用
// 在调用afterPropertiesSet方法之前会被调用
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// 在bean初始化之后会被调用
// 在调用InitializingBean的afterPropertiesSet方法或者init-method指定的方法执行之后调用.
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
// -------------------------
// 下面是InstantiationAwareBeanPostProcessor中的方法
@Nullable
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
default boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
return true;
}
// 5.1版本之后新扩展的方法
@Nullable
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException {
return null;
}
// 5.1之后要废弃的方法
@Deprecated
@Nullable
default PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
return pvs;
}
// ------------------
// 下面是SmartInstantiationAwareBeanPostProcessor中的方法
@Nullable
default Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
@Nullable
default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)
throws BeansException {
return null;
}
default Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
return bean;
}
下面就从与后置处理器及Aware接口有关系的方法开始查看其整个执行流程。因为继承关系太深,而父类是直接实现了Aware及后置处理器相关接口的,所以可以从父类开始向子类中分析,重点关注和后置处理器及Aware接口相关的方法,实际分析过程中可以在相关方法上加上断点,然后逐步debug。
(1)首先查看最上层的实现类中AbstractAutoProxyCreator中和后置处理器及Aware接口有关而且带有方法体的方法,有如下几个:
setBeanFactory:通过Aware方法来注入bean工厂
AbstractAutoProxyCreator.setBeanFactory()
postProcessBeforeInstantiation:bean实例化之前调用
AbstractAutoProxyCreator.postProcessBeforeInstantiation()
postProcessAfterInitialization:在Bean初始化之后调用
AbstractAutoProxyCreator.postProcessAfterInitialization()
(2)然后,查看AbstractAutoProxyCreator的子类AbstractAdvisorAutoProxyCreator中和后置处理器及Aware接口有关的方法,如下:
setBeanFactory:注入bean工厂,重写了父类的方法,所以调用的时候,会调用本类中的setBeanFactory方法,而且里面调用了initBeanFactory
AbstractAdvisorAutoProxyCreator.setBeanFactory()
(3)然后再查看AbstractAdvisorAutoProxyCreator的子类AspectJAwareAdvisorAutoProxyCreator中和后置处理器及Aware接口有关的方法,如下:
AspectJAwareAdvisorAutoProxyCreator.initBeanFactory()
发现其重写了父类AbstractAdvisorAutoProxyCreator中的initBeanfactory方法,所以在父类中调用initBeanFactory的时候,调用的其实还是子类的initBeanFactory方法。
1.2、后置处理器初始化的调用过程
整个后置处理器调用的方法分析完成之后,那么后置处理器在Spring容器创建过程中是如何完成初始化创建动作并加入到容器中的单实例Bean集合中的呢?调用链如下:
(1)在TestMain测试类中,通过AnnotationConfigApplicationContext加载AopConfig配置类,创建IOC容器对象;
(2)在AnnotationConfigApplicationContext中将配置类AopConfig解析为一个bean定义,并注册到容器中,然后调用容器的refresh刷新方法(注意:其他的初始化操作本篇文章中暂时省略,后面会继续详解)
(3)在refresh方法中调用registerBeanPostProcessors方法给容器中注册后置处理器,这个过程中,就会注册与Aop相关的后置处理器AnnotationAwareAspectJAutoProxyCreator;
(4)在注册AnnotationAwareAspectJAutoProxyCreator的过程中,会通过Spring的Bean创建过程去完成后置处理器Bean组件的实例化,属性赋值及初始化的操作。实例化即通过反射的方式去创建对象;填充属性,即通过内省操作对象的setter方法完成;初始化操作中,又分为如下几个小步骤:
① invokeAwareMethods(beanName, bean),主要是用来调用与Aware接口有关的方法,因为这个AOP对应的后置处理器实现了BeanFactoryAware接口,所以会调用其setBeanFactory方法;
② applyBeanPostProcessorsBeforeInitialization,在bean初始化之前调用,可以再bean创建之前做一些自定义的操作,因为AOP对应的后置处理器实现了BeanPostProcessor接口,所以会调用该方法;
③ invokeInitMethods,执行自定义的初始化方法,例如:init-method指定的方法或者如果bean实现了InitializingBean接口,则在该步骤中会执行afterPropertiesSet方法;
④ applyBeanPostProcessorAfterInitialization,在bean初始化之后调用,可以再bean初始化之后,再做一些自定义操作,由于AOP对应的后置处理器实现了BeanPostProcessor接口,所以该方法在Bean初始化完成之后也会被调用,Spring创建AOP的代理对象就是在该步骤中完成的,通常有两种方式:cglib和JDK动态代理;
(5)经过上述一系列操作之后,AnnotationAwareAspectJAutoProxyCreator后置处理器就被创建完成,然后将创建完成的后置处理器组件加入到Spring容器中的beanFactory对象中。
上述整个过程调用链比较深,代码也比较多。篇幅有限,就不贴代码了,在看源码的过程中,可以根据上述的过程去一步步查看。
1.3、容器中单实例Bean的创建过程
上述过程完成之后,只会给容器中注册一个AnnotationAwareAspectJAutoProxyCreator类型的后置处理器。但是容器中需要被增强的Bean示例还未创建出来,例如上篇文章中所演示的MathCalculator对应的Bean实例,那么这个Bean示例是在什么时候被创建的呢?下面分析一下其创建过程,由于代码太长,此处就不贴代码了。
(1)通过断点可以看到,在容器刷新refresh过程的倒数第二步(容器刷新的核心过程总共分为了12大步,后面会详细介绍每一步的功能)中会完成容器中剩余的单实例Bean的创建及初始化。即:finishBeanFactoryInitialization(beanFactory)方法。
(2)而在创建bean的过程中,会受到容器中后置处理器的拦截操作,包括上述第一步给容器中注册的AnnotationAwareAspectJAutoProxyCreator后置处理器,会在Bean创建前后及初始化前后执行后置处理器方法,对Bean做一些自定义的拦截操作,包括对bean进行包装,生成对应的代理对象;
(3)在创建MathCalculator和LogAspect对应的bean时,被后置处理器拦截到之后,会执行如下的处理过程,具体创建代理对象的过程在postProcessAfterInitialization方法中:
① 判断当前bean是否已经被增强过,如果已经被增强,则会直接返回这个bean;判断依据就是根据bean的名称判断已经增强的bean对应的Map集合中是否包括当前需要创建的bean;
② 如果当前bean未被增强过,则去判断当前bean是否需要被包装,如果不需要被包装,则直接返回原来的bean,如果需要被包装,则会通过AbstractAutoProxyCreator的wrapIfNecessary方法进行包装;
③ 如果需要被包装,即执行了wrapIfNecessary方法,则会先去获取所有的增强,如果能够获取到增强器,则会调用AbstractAutoProxyCreator的createProxy方法去创建代理对象。
AbstractAutoProxyCreator.createProxy方法的创建逻辑:
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
// 其他不相关的代码略...
// 默认不指定生成代理的方式,则proxyTargetClass为false,使用的是jdk动态代理
// 如果未强制指定使用cglib生成动态代理,则会去校验当前接口是否能够正常生成代理对象
if (!proxyFactory.isProxyTargetClass()) {
// 当前bean定义中指定了preserveTargetClass属性为true,则会强制使用cglib动态代理
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
// 判断当前bean实现的接口是否为标记类的接口
// 如果为标记类的接口,例如:Aware接口,则还是会强制使用cglib去生成代理
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
/** 找到所有的advisors增强点,即:环绕,前置,后置,返回,异常等通知方法 */
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
/** 设置增强点 */
proxyFactory.addAdvisors(advisors);
/** 设置需要代理的目标类 */
proxyFactory.setTargetSource(targetSource);
/** 定制代理工厂,可以使用自定义的代理工厂,此处使用的还是默认的DefaultAopProxyFactory工厂 */
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
/** 创建代理对象,里面会判断使用JDK动态代理还是CGLIB动态代理 */
return proxyFactory.getProxy(getProxyClassLoader());
}
创建代理的时候会根据条件去决定是使用cglib创建代理还是根据jdk去创建代理,如下DefaultAopProxyFactory的createAopProxy方法,该方法源码如下:
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// isOptimize:表示是否需要对生成代理的策略进行优化,可以在配置中指定
// isProxyTargetClass:表示是否需要强制使用cglib来生成代理,默认为false,通常都会指定强制使用cglib,即将该值设置为true
// (当前被代理的类是否未实现接口,实现的接口数为0)或者(是否实现了SpringProxy接口)
if (config.isOptimize() || config.isProxyTargetClass()
|| hasNoUserSuppliedProxyInterfaces(config)) {
// 获取需要被代理的目标类
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动态代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 否则使用cglib动态代理创建对象
return new ObjenesisCglibAopProxy(config);
}
// 如果创建代理时不需要优化,未指定使用cglib,而且存在接口,则直接使用jdk创建代理对象
else {
return new JdkDynamicAopProxy(config);
}
}
此处返回的就是一个通过策略类创建的代理对象,使用了【策略设计模式】,根据不同的条件使用不同的实现策略去创建代理对象,包括ObjenesisCglibAopProxy对应的策略实现以及JdkDynamicAopProxy对应的策略实现,他们都是AopProxy策略接口的实现类。
1.4、Spring生成代理的方式
Spring底层同时支持了两种生成代理的方式,那么cglib动态代理和jdk的动态代理究竟有什么区别呢?如果选择呢?
(1)使用方式不同
JDK动态代理的前提是被代理的类必须实现了某一个接口,而cglib不需要强制要求被代理类实现接口,可以是接口或者实现类。
(2)生成字节码的方式不同
JDK动态代理和cglib动态代理都是运行期间为被代理对象生成字节码,JDK是直接操作字节码,而cglib是使用了asm框架操作字节码。
(3)生成代理的效率不同
JDK生成字节码是直接使用接口,逻辑比较简单,效率稍高。而cglib生成字节码的逻辑比较复杂,所以生成代理的效率比较低。
(4)执行效率不同
JDK调用代理方法时,是需要通过反射调用,而cglib是通过fastClass机制直接调用方法,执行效率更高。
那么什么又是FastClass机制呢?为什么FastClass就这么快呢?
cglib执行代理方法的效率比jdk高,是因为cglib采用了fastClass机制,而JDK是通过反射调用的。
FastClass的原理:生成代理类时,为代理类和被代理类各生成一个Class,这个Class会为代理类或者被代理类的方法分配一个int类型的索引index,调用的时候将这个index当做一个入参,FastClass就可以直接通过索引定位到要调用的方法直接进行调用,所以省略了反射调用,执行效率高于JDK。使用类似于数据库索引的设计思想。
1.5、整体初始化过程小结
Spring的AOP初始化过程比较复杂,为了防止过程中晕车,将整体的大思路可以总结为如下几个步骤:
(1)注册后置处理器
给容器中导入一个用于拦截Bean床架的后置处理器,利用了Spring的扩展接口Aware及BeanPostProcessor接口。在注解版中,是通过@EnableAspectJAutoProxy来导入的,而在传统的xml中类似,只不过是通过配置的方式,但最终还是通过Spring的xml解析器将xml中配置的内容解析为了bean定义,并注册到Spring容器中了;
(2)创建增强之后的Bean
在容器启动的时候,会去创建所有使用注解标注的Bean组件,例如:@Component注解,而且会解析出标有@Aspect注解的切面类,并解析出类中定义的切点表达式。然后在初始化其他bean的时候,会根据切点表达式去匹配当前类是否需要增强,如果需要增强,则会对当前的类创建代理对象,创建代理对象是在Bean初始化完成之后做的;
(3)创建增强Bean的方式
在后置处理器中创建增强Bean时,会根据当前类是否实现了接口,代理类是否需要被优化,实现的接口是否为Spring原生的标记类接口,是否强制使用cglib方式等等条件去决定是使用cglib的方式还是jdk的方式,最后通过AopProxy策略接口的具体策略实现类去创建对应的代理类,然后加入到Spring容器中,整个初始化过程就执行完毕。
二、代理类的执行过程
上述内容分析了一下AOP的初始化阶段及创建代理的过程,创建完代理之后,执行的时候是如何使用增强器去执行的呢?请等待下篇文章的分析。欢迎关注转发!