SpringAOP基本概念及实现原理(三)

来源:互联网 发布:mac怎么看u盘隐藏文件 编辑:程序博客网 时间:2024/06/05 22:13
面向切面编程(AOP)是针对面向对象编程(OOP)的补充,可以非侵入式的为多个不具有继承关系的对象引入相同的公共行为例如日志、安全、事务、性能监控等等。SpringAOP允许将公共行为从业务逻辑中抽离出来,并将这些行为以一种非侵入的方式织入到所有需要的业务逻辑中,相较于OOP纵向模式的业务逻辑实现,其关注的方向是横向的切面。
从Spring2.0开始,引入AspectJ注释来对POJO进行标注,支持通过切点函数、逻辑运算符、通配符等高级功能来对切点进行灵活的定义,结合各种类型的通知来形成强大的连接点描述能力。
下面先给出一个实现例子,然后介绍SpringAOP的基本概念并通过源码分析其实现原理。

第三部分 实现原理源码分析

1 SpringAOP启动时机

当我们需要使用SpringAOP时我们在配置类上打上@EnableAspectJAutoProxy注释就可启动SpringAOP机制;或者我们在配置类上打上@EnableCaching或@EnableTransactionManagement来启动SpringCache或SpringTransaction机制,而我们知道它们都是依基于SpringAOP机制来实现的,因此也会先启动SpringAOP机制;更有甚者,如果我们使用SpringBoot,只要引入了spring-aop的类库就会启动SpringAOP机制,Spring底层对此是如何实现的呢。

1.1 @EnableAspectJAutoProxy启动SpringAOP

在注释EnableAspectJAutoProxy中使用Import机制引入了AspectJAutoProxyRegistrar:
@Import(AspectJAutoProxyRegistrar.class)public @interface EnableAspectJAutoProxy {    ..................}

当我们在配置类上打上@EnableAspectJAutoProxy注释,就会引入AspectJAutoProxyRegistrar类并执行其registerBeanDefinitions函数:
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);AnnotationAttributes enableAspectJAutoProxy =AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}

此函数委托AnnotationConfigUtils来注册AspectJAnnotationAutoProxyCreator并配置参数。AnnotationConfigUtils最终调用
registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
来注册AnnotationAwareAspectJAutoProxyCreator类型的BeanDefinition到BeanDefinitionRegistry。源码如下:
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");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;}RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);beanDefinition.setSource(source);beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);return beanDefinition;}

其中,首先判断是否已经注册了AUTO_PROXY_CREATOR_BEAN_NAME代表的BeanDefinition了。如果已注册并且与当前要注册的是不同的类型,就对比其优先级:如果当前的优先级大于已经注册的优先级,就将BeanDefinition的beanClassName设置为新注册的类名。
优先级的判断是根据预定义列表:

private static final List<Class<?>> APC_PRIORITY_LIST = new ArrayList<Class<?>>();static {APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);}

在列表中的index越大,就代表优先级越高。因此

AnnotationAwareAspectJAutoProxyCreator>AspectJAwareAdvisorAutoProxyCreator>InfrastructureAdvisorAutoProxyCreator。
这三个类都是AbstractAdvisorAutoProxyCreator的子类,上述代码确保BeanDefinitionRegistry中只有唯一一个AbstractAdvisorAutoProxyCreator的bean。如果有多个地方启动不同类型的AbstractAdvisorAutoProxyCreator则最终会创建优先级最高的那个。

AbstractAdvisorAutoProxyCreator的子类就是基于检测到的切面来构建AOP代理的关键类,可以说AbstractAdvisorAutoProxyCreator类型的bean生成以后就意味着SpringAOP机制已经开启。其详细功能我们将在下面讲解。

1.2 其他注释导致SpringAOP启动

@EnableCaching通过Import机制导入CachingConfigurationSelector
@Import(CachingConfigurationSelector.class)public @interface EnableCaching {    ................}

CachingConfigurationSelector是一个ImportSelector,在其selectImports方法中通过判断标注上的adviceMode值来判断引入动态代理实现机制还是引入AspectJ原生机制,由于我们使用动态代理,因此会执行:
List<String> result = new ArrayList<String>();result.add(AutoProxyRegistrar.class.getName());result.add(ProxyCachingConfiguration.class.getName());
来引入动态代理机制,其中AutoProxyRegistrar就是用来启动SpringAOP机制的;ProxyCachingConfiguration是启动SpringCache机制,SpringCache实现原理详细介绍请参考《SpringCache实现原理及核心业务逻辑(二)》。
AutoProxyRegistrar是一个ImportBeanDefinitionRegistrar,其registerBeanDefinitions函数代码如下:
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {boolean candidateFound = false;Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();for (String annoType : annoTypes) {AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);if (candidate == null) {continue;}Object mode = candidate.get("mode");Object proxyTargetClass = candidate.get("proxyTargetClass");if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&Boolean.class == proxyTargetClass.getClass()) {candidateFound = true;if (mode == AdviceMode.PROXY) {AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);if ((Boolean) proxyTargetClass) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);return;}}}}......}
其作用是判断注释上的proxyTargetClass和mode属性,如果proxyTargetClass存在并且类型为Boolean,而且mode存在且类型为AdviceMode并且其值为AdviceMode.PROXY,则执行:
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);if ((Boolean) proxyTargetClass) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);return;}
来委托AopConfigUtils注册InfrastructureAdvisorAutoProxyCreator的BeanDefinition到BeanDefinitionFactory。该逻辑在上面1.1节已经介绍过了,当在多个地方注册时最终会注册优先级最高的那个。
@EnableTransactionManagement的实现机制与@EnableCaching一模一样,在此不再赘述。

1.3 SpringBoot自动配置机制中的AopAutoConfiguration

SpringBoot本着约定大于配置的原则,在其核心中加入了autoconfig模块,因此我们都无需做任何配置,只要引入了spring-aop类库,就会直接启动SpringAOP机制。其内置用来配置SpringAOP的配置类AopAutoConfiguration代码如下:
@Configuration@ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class })@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)public class AopAutoConfiguration {@Configuration@EnableAspectJAutoProxy(proxyTargetClass = false)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = true)public static class JdkDynamicAutoProxyConfiguration {}@Configuration@EnableAspectJAutoProxy(proxyTargetClass = true)@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = false)public static class CglibAutoProxyConfiguration {}}

如果存在EnableAspectJAutoProxy、Aspect、Advice类,并且spring.aop.auto=true或者没配置,则在Configuration类上标注@EnableAspectJAutoProxy。proxyTargetClass是用来表示使用Cglib还是JdkDynamicProxy来实现代理,proxyTargetClass=true代表使用Cglib,否则使用JdkDynamicProxy。

2 切面织入

当Configuration类上标注了@EnableAspectJAutoProxy注释时会注册AnnotationAwareAspectJAutoProxyCreator来实现基于检测到的切面来构建AOP代理的功能。
AnnotationAwareAspectJAutoProxyCreator属于Ordered InstantiationAwareBeanPostProcessor,而InstantiationAwareBeanPostProcessor继承了BeanPostProcessor。因此它会在其注册到beanfactory之后每一个bean创建时对该bean进行处理。

2.1 BeanPostProcessor对bean处理的时机

BeanPostProcessor对bean处理的时机有两个postProcessBeforeInitialization;postProcessAfterInitialization。
其子接口InstantiationAwareBeanPostProcessor又额外增加了三个:postProcessBeforeInstantiation、postProcessAfterInstantiation、postProcessPropertyValues。
五个不同处理时机函数的说明如下:

postProcessBeforeInstantiation:bean实例化之前被调用postProcessAfterInstantiation:bean实例化之后,但是属性值被初始化之前被调用postProcessPropertyValues:bean属性值已经可获得,但是尚未设置到bean实例中时调用,可以用来对属性值做修改postProcessBeforeInitialization:bean初始化之前执行postProcessAfterInitialization:bean初始化以后执行
具体执行逻辑请详见Spring beans核心库中的AbstractAutowireCapableBeanFactory.createBean方法。

AnnotationAwareAspectJAutoProxyCreator对于上述五个处理函数的实现直接继承自其父类AbstractAutoProxyCreator,相关代码如下:

@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {Object cacheKey = getCacheKey(beanClass, beanName);if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {if (this.advisedBeans.containsKey(cacheKey)) {return null;}if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return null;}}if (beanName != null) {TargetSource targetSource = getCustomTargetSource(beanClass, beanName);if (targetSource != null) {this.targetSourcedBeans.add(beanName);Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}}return null;}@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) {return true;}@Overridepublic PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {return pvs;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) {return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (!this.earlyProxyReferences.contains(cacheKey)) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}

可以看到,实际起作用的是实例化前函数(postProcessBeforeInstantiation)和初始化后函数(postProcessAfterInitialization),下面分析这两个函数的作用。

2.2 postProcessBeforeInstantiation函数

postProcessBeforeInstantiation函数的目的在于对目标进行是否应该被忽略的预处理并缓存其结果,下面对其代码进行分析:


Object cacheKey = getCacheKey(beanClass, beanName);
根据beanClass和beanName得到一个唯一值用于缓存。

if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {if (this.advisedBeans.containsKey(cacheKey)) {return null;}if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return null;}}
this.targetSourcedBeans代表了使用定制目标源的beanName,bean键值对。上述代码表示如果beanName为null或者beanName没有对应着定制的targetSourceBean则执行括号内的逻辑:首先判断this.advisedBeans中是否包含了cacheKey,如果包含了,说明前面已经处理过该beanName了,就直接返回null;若没包含,则判断该beanClass是否是基础架构类isInfrastructureClass(beanClass)或是否应该被忽略shouldSkip(beanClass, beanName),若其中之一为真,则设置:
this.advisedBeans.put(cacheKey, Boolean.FALSE);

其中,判断是否基础架构类的方法被AnnotationAwareAspectJAutoProxyCreator重写过,判断
  • a) 类型是否为:Advice、Pointcut、Advisor或AopInfrastructureBean
  • b) 类型是否标注了@Aspect注释
  • c) 是否被Ajc编译过(使用原生AspectJ静态织入)
若符合其中之一,就代表是基础架构类

判断是否应该被忽略方法被AspectJAwareAdvisorAutoProxyCreator重写过,方法如下:
protected boolean shouldSkip(Class<?> beanClass, String beanName) {// TODO: Consider optimization by caching the list of the aspect namesList<Advisor> candidateAdvisors = findCandidateAdvisors();for (Advisor advisor : candidateAdvisors) {if (advisor instanceof AspectJPointcutAdvisor) {if (((AbstractAspectJAdvice) advisor.getAdvice()).getAspectName().equals(beanName)) {return true;}}}return super.shouldSkip(beanClass, beanName);}
获得所有的advisors并对于每一个advisor,如果beanName等于其advice的aspectName,说明该beanName对应的是一个切面定义类,无需AOP代理,应该被忽略掉。其中获得所有advisors函数findCandidateAdvisors是一个比较重要的函数,我们将在下面展开讲解。
接着往下看:

if (beanName != null) {TargetSource targetSource = getCustomTargetSource(beanClass, beanName);if (targetSource != null) {this.targetSourcedBeans.add(beanName);Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}}
本段代码表示,如果beanName不为null,并且通过注册在AbstractAutoProxyCreator.customTargetSourceCreators中的TargetSourceCreator能得到非空的TargetSource,那么首先设置
this.targetSourcedBeans.add(beanName);

然后直接为targetSource创建代理,并且返回该代理(意味着在postProcessBeforeInstantiation方法中返回了非空的值,直接中断postProcessBeforeInstantiation循环,并且直接为该返回值应用postProcessAfterInitialization方法,具体执行逻辑可翻阅Spring beans核心库中的AbstractAutowireCapableBeanFactory.createBean方法),否则返回null。customTargetSourceCreators中默认没有注册任何TargetSourceCreator,只有在我们需要特殊定制时才需要注册。
总体来看,postProcessBeforeInstantiation方法的意义在于过滤并记录无需使用AOP代理的beanName,并将其放置到advisedBeans中:

this.advisedBeans.put(cacheKey, Boolean.FALSE);
为后面postProcessAfterInitialization使用。

2.3 postProcessAfterInitialization函数

postProcessAfterInitialization会根据postProcessBeforeInstantiation的预处理结果并进行更近一步的判断来确定bean是否确实需要代理,并执行实际的创建代理操作,代码如下:
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {if (bean != null) {Object cacheKey = getCacheKey(bean.getClass(), beanName);if (!this.earlyProxyReferences.contains(cacheKey)) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}

如果bean不为空,并且为了解决循环依赖所导致的提前关联对象中不包含该beanName(为解决循环依赖而提前关联的对象无法使用proxy),那么执行:
wrapIfNecessary(bean, beanName, cacheKey)
其代码如下:
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {if (beanName != null && this.targetSourcedBeans.contains(beanName)) {return bean;}if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// 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;}this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}

其中前三个判断都使用了上面postProcessBeforeInstantiation方法的成果。避免不必要的额外开销。如果通过了前面三个判断,就来到了比较关键的操作:
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
顾名思意,该方法是用来返回指定的bean需要哪些advices和advisors。若有,则代表需要创建代理;若没有,则代表不需要创建代理。我们将在后面详细分析该核心函数。
随后根据返回的specificInterceptors是否为空,决定是否为bean创建AOP代理:

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;}this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;


createProxy中会根据proxyTargetClass属性值来确定使用ObjenesisCglibAopProxy(CglibAopProxy)还是使用JdkDynamicAopProxy来创建代理。并根据上面得到的specificInterceptors创建Advisors数组,保存进代理对象中。该函数的业务逻辑我们将在下面详细讲解。

如此一来,通过动态代理在实际方法调用前对于切面逻辑的织入工作就完成了。

2.4 findCandidateAdvisors函数

该方法被AnnotationAwareAspectJAutoProxyCreator重写了,代码如下:
@Overrideprotected List<Advisor> findCandidateAdvisors() {// Add all the Spring advisors found according to superclass rules.List<Advisor> advisors = super.findCandidateAdvisors();// Build Advisors for all AspectJ aspects in the bean factory.advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());return advisors;}

首先使用父类的findCandidateAdvisors方法,其中通过得到beanFactory中的Advisor.class类型的bean来得到Advisor;然后通过BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors()方法来通过解析@Aspect类中定义的@Pointcut函数和@Advice函数来组装Advisor,最后合并并返回。
BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors()方法初次执行时会遍历beanFactory中的所有的beanName,找到标注了@Aspect的bean,然后使用ReflectiveAspectJAdvisorFactory.getAdvisors方法得到Advisors:

List<Advisor> advisors = new LinkedList<Advisor>();for (Method method : getAdvisorMethods(aspectClass)) {Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);if (advisor != null) {advisors.add(advisor);}}

以上代码首先调用getAdvisorMethods方法通过回调模式得到类中没有标注@Pointcut的函数,称为Advisor函数,然后遍历Advisor函数并为其创建Advisor。其中getAdvisorMethods不仅得到所有Advisor函数,还对得到的Advisor函数进行了一次排序,排序规则及作用我们将在下面详细解释。

生成Advisor的方法getAdvisor代码如下:

public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,int declarationOrderInAspect, String aspectName) {validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());AspectJExpressionPointcut expressionPointcut = getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());if (expressionPointcut == null) {return null;}return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,this, aspectInstanceFactory, declarationOrderInAspect, aspectName);}

首先根据上面得到的Advisor method的注释获得一个AspectJExpressionPointcut,然后根据expressionPointcut等信息创建一个InstantiationModelAwarePointcutAdvisorImpl实例并返回。其中getPointcut函数代码如下:
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {AspectJAnnotation<?> aspectJAnnotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);if (aspectJAnnotation == null) {return null;}AspectJExpressionPointcut ajexp =new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);ajexp.setExpression(aspectJAnnotation.getPointcutExpression());ajexp.setBeanFactory(this.beanFactory);return ajexp;}

注意,此时只是单纯的将method上定义的pointcut描述信息保存进AspectJExpressionPointcut中,并没有对其进行语义解析。实际解析的时机是在后面判断该Advisor是否可以应用到被创建的bean上时才会执行。
2.4.1 getAdvisorMethods方法内对advisorMethods进行排序
在上面构建Advisors时调用来获得Advisor Method的函数getAdvisorMethods代码如下:
private List<Method> getAdvisorMethods(Class<?> aspectClass) {final List<Method> methods = new LinkedList<Method>();ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() {@Overridepublic void doWith(Method method) throws IllegalArgumentException {// Exclude pointcutsif (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {methods.add(method);}}});Collections.sort(methods, METHOD_COMPARATOR);return methods;}

该方法首先通过回调模式得到类中没有标注@Pointcut的函数,然后对这些函数进行排序,使用的对比器METHOD_COMPARATOR代码如下:
private static final Comparator<Method> METHOD_COMPARATOR;static {CompoundComparator<Method> comparator = new CompoundComparator<Method>();comparator.addComparator(new ConvertingComparator<Method, Annotation>(new InstanceComparator<Annotation>(Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),new Converter<Method, Annotation>() {@Overridepublic Annotation convert(Method method) {AspectJAnnotation<?> annotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);return (annotation != null ? annotation.getAnnotation() : null);}}));comparator.addComparator(new ConvertingComparator<Method, String>(new Converter<Method, String>() {@Overridepublic String convert(Method method) {return method.getName();}}));METHOD_COMPARATOR = comparator;}

使用CompoundComparator组合了两个Comparator:首先按照method标注的Advice类型按照Around>Before>After>AfterReturning>AfterThrowing排序;然后对于相同Advice类型的方法按照方法名进行排序。
其意义在于,我们最终转换成的Advisors其顺序并不是按照在@Aspect类中定义的方法的顺序,而是首先按照Advice类型,然后按照方法名进行排序的。结合后面getAdvicesAndAdvisorsForBean中对于适用于某一个具体bean的Advisors的排序,共同构成了实际上在执行方法时拦截器链的执行顺序。


2.5 getAdvicesAndAdvisorsForBean函数

该方法的实现是在子类AbstractAdvisorAutoProxyCreator中,用来过滤出可以应用在给定的bean上的Advisors,代码如下:
@Overrideprotected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);if (advisors.isEmpty()) {return DO_NOT_PROXY;}return advisors.toArray();}

其中findEligibleAdvisors方法代码如下:
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {List<Advisor> candidateAdvisors = findCandidateAdvisors();List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);extendAdvisors(eligibleAdvisors);if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;}

首先得到所有的Advisors,然后判断可应用于指定beanName和beanClass的bean的Advisors,最后对Advisors排序并返回。
findCandidateAdvisors函数已在上一节分析过了。此时的重点在于findAdvisorsThatCanApply方法:
protected List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {ProxyCreationContext.setCurrentProxiedBeanName(beanName);try {return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);}finally {ProxyCreationContext.setCurrentProxiedBeanName(null);}}

在方法中委托工具类AopUtils来过滤可以应用到目标类上的Advisors,AopUtils最终使用canApply方法判断Advisor是否可应用到beanClass,代码如下:
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {if (advisor instanceof IntroductionAdvisor) {return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);}else if (advisor instanceof PointcutAdvisor) {PointcutAdvisor pca = (PointcutAdvisor) advisor;return canApply(pca.getPointcut(), targetClass, hasIntroductions);}else {// It doesn't have a pointcut so we assume it applies.return true;}}

其中当advisor类型是PointcutAdvisor时会执行:
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {Assert.notNull(pc, "Pointcut must not be null");if (!pc.getClassFilter().matches(targetClass)) {return false;}MethodMatcher methodMatcher = pc.getMethodMatcher();if (methodMatcher == MethodMatcher.TRUE) {// No need to iterate the methods if we're matching any method anyway...return true;}IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;if (methodMatcher instanceof IntroductionAwareMethodMatcher) {introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;}Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));classes.add(targetClass);for (Class<?> clazz : classes) {Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);for (Method method : methods) {if ((introductionAwareMethodMatcher != null &&introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||methodMatcher.matches(method, targetClass)) {return true;}}}return false;}

canApply方法判断逻辑如下:
  1.  首先判断Pointcut的classFilter是否matches(targetClass)
  2.  然后遍历目标类的所有接口类及目标类本身,对每一个类都遍历其所有方法,如果有一个方法被pc.getMethodMatcher().matches(method, targetClass)则说明该PointcutAdvisor可以应用到这个targetClass上。
最终由AopUtils.findAdvisorsThatCanApply方法返回所有可应用到目标类上的Advisors。

2.5.1 sortAdvisors对eligibleAdvisors的排序
在上述findEligibleAdvisors方法中,获得所有Advisors并过滤出适合指定的目标类的eligibleAdvisors后,会对eligibleAdvisors再进行一次排序:
if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}

sortAdvisors函数被AspectJAwareAdvisorAutoProxyCreator重写了,其中将所有的Advisors都封装为PartiallyComparableAdvisorHolder,并使用aspectjweaver库中的工具PartialOrder来排序,代码如下:
protected List<Advisor> sortAdvisors(List<Advisor> advisors) {List<PartiallyComparableAdvisorHolder> partiallyComparableAdvisors =new ArrayList<PartiallyComparableAdvisorHolder>(advisors.size());for (Advisor element : advisors) {partiallyComparableAdvisors.add(new PartiallyComparableAdvisorHolder(element, DEFAULT_PRECEDENCE_COMPARATOR));}List<PartiallyComparableAdvisorHolder> sorted =PartialOrder.sort(partiallyComparableAdvisors);if (sorted != null) {List<Advisor> result = new ArrayList<Advisor>(advisors.size());for (PartiallyComparableAdvisorHolder pcAdvisor : sorted) {result.add(pcAdvisor.getAdvisor());}return result;}else {return super.sortAdvisors(advisors);}}

其排序逻辑为:定义在不同@Aspect中的Advisor根据其Order进行排序,而定义在相同切面中的Advisor其order一样;对于order一样的Advisor会进行进一步的排序:对于after类型的advisor其定义位置越靠后说明优先级越高(最后执行);而对于其他类型的advisor其定义位置越靠前说明优先级越高(最先执行)。这里的定义位置并不是在@Aspect类中的函数定义位置,还记得2.4.1小节中介绍的在getAdvisorMethods方法里先做过一次排序吗。
结合这两个阶段的排序,最终得到可以应用于某个bean上的advisor列表顺序如下:
  1. 首先根据其所在的@Aspect类定义的order排序,order越高,优先级越高;
  2. 对于相同order的,根据Advice类型按照Around>Before>After>AfterReturning>AfterThrowing排序;
  3. order与Advice类型都相同的,按照定义时方法名来排序,例如:方法adosomthing()优先级就高于方法bdosomething()。

2.6 createProxy函数创建代理

真正执行创建代理操作的函数createProxy代码如下:
protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);}ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);if (!proxyFactory.isProxyTargetClass()) {if (shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);}else {evaluateProxyInterfaces(beanClass, proxyFactory);}}Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);if (advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}return proxyFactory.getProxy(getProxyClassLoader());}

其中构建了Advisor[] advisors,并将其设置进proxyFactory,proxyFactory本质上是一个ProxyCreatorSupport,其中维护了proxy配置信息、Advices和Advisors的信息,属于创建代理的工厂。然后执行proxyFactory.getProxy,其代码如下:
public Object getProxy(ClassLoader classLoader) {return createAopProxy().getProxy(classLoader);}

最终使用到DefaultAopProxyFactory的createAopProxy方法:
@Overridepublic AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {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.");}if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}}

可以看到,在其中根据config.optimize,config.proxyTargetClass以及被代理对象是否有用户定义接口来共同决定使用JdkDynamicAopProxy还是使用ObjenesisCglibAopProxy(CglibAopProxy)来创建代理。
config.optimize目前就是false,因此如果同时满足config.proxyTargetClass=false并且被代理对象拥有用户定义的接口则使用JdkDynamicAopProxy来创建代理,否则使用ObjenesisCglibAopProxy来创建代理。
最终使用到CglibAopProxy.getProxy(ClassLoader classLoader)或者JdkDynamicAopProxy.getProxy(ClassLoader classLoader)来实际执行创建代理。

3 @Pointcut注释的解析过程

如前所述,创建bean的时候AnnotationAwareAspectJAutoProxyCreator在其postProcessBeforeInstantiate方法中首次使用findCandidateAdvisors函数创建所有的Advisors时,会执行ReflectiveAspectJAdvisorFactory.getPointcut函数生成AspectJExpressionPointcut。但此时并没有从语义上解析其pointcutExpression。
随后在其postProcessAfterInitialization方法中判断是否存在可应用于该bean的Advisors,其中委托AopUtils来处理,最终调用到AopUtils.canApply函数。该函数中首先判断Pointcut的classFilter是否matches(targetClass)然后遍历目标类的所有接口类及目标类本身,对每一个类都遍历其所有方法,如果有一个方法被pc.getMethodMatcher().matches(method, targetClass)则说明该PointcutAdvisor可以应用到这个targetClass上。而getClassFilter或getMethodMatcher方法就触发了AspectJExpressionPointcut语义上的解析:

@Overridepublic ClassFilter getClassFilter() {checkReadyToMatch();return this;}@Overridepublic MethodMatcher getMethodMatcher() {checkReadyToMatch();return this;}


checkReadyToMatch方法就是检查本AspectJExpressionPointcut是否准备好执行matches函数了,也就是是否已经完成语义解析。如果还没有,就执行语义解析:

private void checkReadyToMatch() {if (getExpression() == null) {throw new IllegalStateException("Must set property 'expression' before attempting to match");}if (this.pointcutExpression == null) {this.pointcutClassLoader = determinePointcutClassLoader();this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);}}

在buildPointcutExpression方法中委托PointcutParser来执行解析工作:
private PointcutExpression buildPointcutExpression(ClassLoader classLoader) {PointcutParser parser = initializePointcutParser(classLoader);PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];for (int i = 0; i < pointcutParameters.length; i++) {pointcutParameters[i] = parser.createPointcutParameter(this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);}return parser.parsePointcutExpression(replaceBooleanOperators(getExpression()),this.pointcutDeclarationScope, pointcutParameters);}

PointcutParser又将此工作委托给PatternParser:
public PointcutExpression parsePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters)throws UnsupportedPointcutPrimitiveException, IllegalArgumentException {PointcutExpressionImpl pcExpr = null;try {Pointcut pc = resolvePointcutExpression(expression, inScope, formalParameters);pc = concretizePointcutExpression(pc, inScope, formalParameters);validateAgainstSupportedPrimitives(pc, expression); // again, because we have now followed any ref'd pcutspcExpr = new PointcutExpressionImpl(pc, expression, formalParameters, getWorld());} catch (ParserException pEx) {throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));} catch (ReflectionWorld.ReflectionWorldException rwEx) {throw new IllegalArgumentException(rwEx.getMessage());}return pcExpr;}protected Pointcut resolvePointcutExpression(String expression, Class inScope, PointcutParameter[] formalParameters) {try {PatternParser parser = new PatternParser(expression);parser.setPointcutDesignatorHandlers(pointcutDesignators, world);Pointcut pc = parser.parsePointcut();validateAgainstSupportedPrimitives(pc, expression);IScope resolutionScope = buildResolutionScope((inScope == null ? Object.class : inScope), formalParameters);pc = pc.resolve(resolutionScope);return pc;} catch (ParserException pEx) {throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));}}

最终实际解析工作是由这一句:
Pointcut pc = parser.parsePointcut();
执行的。对于元素切点函数、通配符、逻辑运算符等等的语义上的识别与解析都在PatternParser类中进行处理,本章重点介绍实现原理,对具体语义解析算法感兴趣的同学可以自行打开阅读一下。
注意:AspectJExpressionPointcut是SpringAOP中的概念,但是其对于pointcutExpression的语义解析就委托给了aspectjweaver类库中的PointcutParser来处理,这就是SpringAOP需要依赖aspectjweaver库的核心原因,而不仅仅是依赖其中定义的几个注释。

4 执行方法调用

上面2.6小节介绍了根据不同情况创建JdkDynamicProxy或CglibProxy,这两种代理在实际执行时入口略有不同,但世界拦截器链与方法调用的逻辑是相同的。

4.1 代理调用入口

对于Cglib代理而言,调用方法的入口在其callbacks函数,我们在前面创建代理的时候为其添加了7个回调函数,组装这些回调函数的代码如下:
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {......Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);......Callback[] mainCallbacks = new Callback[] {aopInterceptor,  // for normal advicetargetInterceptor,  // invoke target without considering advice, if optimizednew SerializableNoOp(),  // no override for methods mapped to thistargetDispatcher, this.advisedDispatcher,new EqualsInterceptor(this.advised),new HashCodeInterceptor(this.advised)};Callback[] callbacks;if (isStatic && isFrozen) {Method[] methods = rootClass.getMethods();Callback[] fixedCallbacks = new Callback[methods.length];this.fixedInterceptorMap = new HashMap<String, Integer>(methods.length);for (int x = 0; x < methods.length; x++) {List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());this.fixedInterceptorMap.put(methods[x].toString(), x);}callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];......}else {callbacks = mainCallbacks;}return callbacks;}

我们需要关注的是DynamicAdvisedInterceptor,其初始化参数advised维护了代理中的Advices和Advisors以及配置信息。在实际方法调用的时候,Cglib会逐个调用这些已设置进代理的callback函数,而AOP切面Advice的入口调用就是DynamicAdvisedInterceptor中的interceptor函数:
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {......List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);Object retVal;if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = methodProxy.invoke(target, argsToUse);}else {retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}retVal = processReturnType(proxy, target, method, retVal);return retVal;......}

基于拦截器的AOP框架其核心的逻辑就是
new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
这句,我们在下面详细分析。

接下来看一下jdkproxy,jdkproxy的回调函数只有一个,就是invoke函数,所有的业务逻辑集中在其中处理:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {......List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);if (chain.isEmpty()) {Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);}else {invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);retVal = invocation.proceed();}.......return retVal;}
摒弃掉封装与入口的略有不同,JdkDynamicProxy核心业务逻辑跟CglibProxy完全一致。CglibMethodInvocation是ReflectiveMethodInvocation的子类,而核心函数proceed是实现在ReflectiveMethodInvocation中的。

4.2 advised.getInterceptorsAndDynamicInterceptionAdvice方法

此处的advised就是上面介绍的proxyFactory,其本质上是一个ProxyCreatorSupport,其中维护了proxy配置信息、Advices和Advisors的信息,属于创建代理的工厂。因此其getInterceptorsAndDynamicInterceptionAdvice方法可以获得可应用在指定的类的方法上的拦截器链,代码如下:
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {MethodCacheKey cacheKey = new MethodCacheKey(method);List<Object> cached = this.methodCache.get(cacheKey);if (cached == null) {cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);this.methodCache.put(cacheKey, cached);}return cached;}
其中委托了DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice函数来执行,代码就不贴了,执行的逻辑是将可应用在本类上的Advisors过滤,找出适合应用在本方法上的Advisors并生成Interceptor或者InterceptorAndDynamicMethodMatcher数组,最后返回。

4.3 ReflectiveMethodInvocation.proceed基于拦截器链的方法执行过程

将上面得到的拦截器链和代理对象、目标对象、调用方法、调用参数及目标类等封装为一个ReflectiveMethodInvocation对象,该对象就是一个运行时连接点。
在第二篇基本概念中我们介绍过运行时连接点,当符合某切点条件的函数在被执行时,就产生了一个运行时连接点Joinpoint的概念。运行时连接点代表了一个在静态连接点(程序中的某个位置)上发生的事件。例如:一次调用就是一个对于方法(静态连接点)的运行时连接点。
在基于拦截器框架的上下文中,一个运行时连接点就是对于一个可访问对象的访问过程的具体化。因此ReflectiveMethodInvocation.proceed就是基于拦截器的方法执行过程。ReflectiveMethodInvocation中维护的属性如下:

protected final Object proxy;protected final Object target;protected final Method method;protected Object[] arguments;private final Class<?> targetClass;/** * Lazily initialized map of user-specific attributes for this invocation. */private Map<String, Object> userAttributes;/** * List of MethodInterceptor and InterceptorAndDynamicMethodMatcher * that need dynamic checks. */protected final List<?> interceptorsAndDynamicMethodMatchers;/** * Index from 0 of the current interceptor we're invoking. * -1 until we invoke: then the current interceptor. */private int currentInterceptorIndex = -1;

其中interceptorsAndDynamicMethodMatchers就是我们上面得到的拦截器链,ReflectiveMethodInvocation的proceed方法如下所示:
@Overridepublic Object proceed() throws Throwable {//We start with an index of -1 and increment early.if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();}Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {// Evaluate dynamic method matcher here: static part will already have// been evaluated and found to match.InterceptorAndDynamicMethodMatcher dm =(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {return dm.interceptor.invoke(this);}else {// Dynamic matching failed.// Skip this interceptor and invoke the next in the chain.return proceed();}}else {// It's an interceptor, so we just invoke it: The pointcut will have// been evaluated statically before this object was constructed.return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}}

调用过程总结如下:
  1. 如果拦截器链尚未执行完,就执行拦截器链上的下一个拦截器并将this(本动态连接点)传递过去
  2. 每一个拦截器的拦截函数中都执行自己的前置逻辑并调用invocation.proceed()重复步骤1
  3. 档拦截器链执行完毕,则执行方法调用,并返回结果
  4. 在返回的过程中,按照前面调用顺序的反向顺序执行方法调用的后置逻辑,也就是在invocation.proceed()之后编写的逻辑
  5. 拦截器链反向执行完成后,最终返回结果。
由此可得,一个定义了切面的方法调用过程如下所示:
interceptor1.before()->interceptor2.before()->......interceptorn.before()->method.invoke()->interceptorn.aft()->......interceptor2.aft()->interceptor1.aft()

@Before定义的通知(拦截器)只有before()逻辑;@After、@AfterReturning、@AfterThrowing定义的通知(拦截器)只有after()逻辑;@Around定义的通知(拦截器)可以自己来定义before()和after()逻辑。




小结: SpringAOP在实际中应用非常广泛,从SpringFramework自己内置的SpringCache与SpringTransaction等就是通过SpringAOP来扩展实现的;另外,在诸如日志、事务、安全、性能监控等各方面应用非常广泛。本章通过解读源代码来分析SpringAOP的实现原理,通过对本章的学习可以对SpringAOP中的基本概念及逻辑不仅知其然,还能知其所以然。






相关文章:SpringAOP基本概念及实现原理(一)

相关文章:SpringAOP基本概念及实现原理(二)

原创粉丝点击