Spring AOP源码分析(一)——筛选合适的通知器
本文接上一篇文章Spring-AOP-使用介绍-从前世到今生,开始分析Spring AOP相关的代码实现。主要分为筛选合适的通知器、创建代理和拦截器链的执行过程三个方面。下面先给出一个示例,然后根据示例引出源码分析。
代理示例(xml和注解两种配置):
最终的beanDefinitionMap Debug结果:
从Debug结果可以看到,蓝色方框对应的是id为userService、logArgsAspect、logResultAspect和logResultAspectAnno的普通Bean定义;绿色方框是xml配置中定义的两个切点,即logResultPointcut和internalPointcut;黄色方框对应的是两个通知,即<aop:before .../>
和<aop:after-returning …/>
。这里需要注意,注解@Aspect的切面BeanlogResultAspectAnno
并不是在ApplicationContext解析xml配置文件的时候就转换成了多个Advisor,而是在目标对象bean单例预初始化、给目标对象Bean查找合适的Advisor的时候做了处理,并被构建为一个或多个 Advisor(依赖于AnnotationAwareAspectJAutoProxyCreator)。
一、AOP xml配置的解析
对于如上的AOP xml配置,在Spring Framework中是由AopNamespaceHandler来处理的。
public class AopNamespaceHandler extends NamespaceHandlerSupport {
// Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the
// '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'
// and '{@code scoped-proxy}' tags.
@Override
public void init() {
// 注解parser
// In 2.0 XSD as well as in 2.1 XSD.
registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
// Only in 2.0 XSD: moved to context namespace as of 2.1
registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
}
}
可以看出,在解析xml配置阶段,<aop:config .../>
元素是由ConfigBeanDefinitionParser类处理的,它把<aop:pointcut .../>
解析成AspectJExpressionPointcut、把<aop:before ../>
和<aop:after-returning …/>
解析成AspectJPointcutAdvisor,而<aop:aspectj-autoproxy .../>
是由AspectJAutoProxyBeanDefinitionParser类处理的。AspectJAutoProxyBeanDefinitionParser类做了如下的事情:
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
// 注册AspectJAutoProxyCreator(AnnotationAwareAspectJAutoProxyCreator)
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}
}
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
// 注册或者更新AspectJAutoProxyCreator
BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
// 设置相关属性(proxy-target-class、expose-proxy)
useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
registerComponentIfNecessary(beanDefinition, parserContext);
}
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
BeanDefinitionRegistry registry, @Nullable Object source) {
// 注册或者更新AspectJAutoProxyCreator
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
private static BeanDefinition registerOrEscalateApcAsRequired(
Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// 判断internalAutoProxyCreator是否存在
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;
}
// 注册BeanDefinition
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;
}
可以看出AspectJAutoProxyBeanDefinitionParser主要就是注册AnnotationAwareAspectJAutoProxyCreator的BeanDefinition到BeanFactory。
二、为目标对象筛选合适的通知器
1.AOP入口分析
对于如上的xml配置,在ClassPathXmlApplicationContext读取该配置之后,就存在了以上的BeanDefinitionMap。在初始化非懒加载的单例时(实例化、初始化),对于id为userService的目标对象Bean,Spring AOP可能会为其生成代理对象。
从Spring-IoC容器源码分析这篇文章中可以知道,在单例Bean初始化的过程中,会经过很多生命周期接口的回调处理,如BeanPostprocessor。而Bean代理对象的生成也依赖于BeanPostprocessor处理器的处理(AOP和IOC的整合点),即上文说到的AnnotationAwareAspectJAutoProxyCreator。先看下AnnotationAwareAspectJAutoProxyCreator的类继承图:
可见,AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor、InstantiationAwareBeanPostProcessor和SmartInstantiationAwareBeanPostProcessor接口,并继承自AbstractAutoProxyCreator抽象类。而AOP处理的核心逻辑主要在AbstractAutoProxyCreator类里,因此AnnotationAwareAspectJAutoProxyCreator(BeanPostProcessor)是AOP的入口。本文先分析AOP代理对象生成的第一步,即为目标对象筛选合适通知器。
如下是AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator的相关方法,这些都是跟目标对象Bean预初始化过程中有关的生命周期回调接口,与AOP代理息息相关。
public Object getEarlyBeanReference(Object bean, String beanName) {
// 解决循环依赖问题
// ...
}
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
// ...
}
public boolean postProcessAfterInstantiation(Object bean, String beanName) {
return true;
}
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
return pvs;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
// ...
}
在目标对象(单例)初始化过程中,以上方法首先执行的是postProcessBeforeInstantiation方法,该方法在createBean方法中执行,而在doCreateBean调用之前:
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
Object cacheKey = getCacheKey(beanClass, beanName);
// beanName为空,或者当前方法中未创建对应的代理
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
// cacheKey已经判断过,直接返回null
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
// 对于实现了Advice,Pointcut,Advisor,AopInfrastructureBean接口的bean或者是切面的bean,不需要代理
// shouldSkip方法判断是否跳过代理
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
// 有自定义的TargetSource, 则创建代理
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
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; // 这里代理返回后,bean后续的初始化步骤就不执行了,直接使用该代理
}
return null;
}
postProcessBeforeInstantiation方法的逻辑是:如果存在自定义的TargetSource,则根据自定义的TargetSource创建代理对象并返回,并且这里返回代理后,bean后续的初始化步骤就不执行了。
若postProcessBeforeInstantiation方法返回null,则继续目标对象bean的初始化。并且会在doCreateBean中执行以下代码:
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
这行代码与解决bean之间的循环依赖有关,见Spring IoC中的循环依赖问题及解决办法。
public Object getEarlyBeanReference(Object bean, String beanName) { // 解决循环依赖问题
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
// 将cacheKey添加到earlyProxyReferences中,标记已创建 early 的代理对象
this.earlyProxyReferences.add(cacheKey);
}
return wrapIfNecessary(bean, beanName, cacheKey); // 创建 early 的代理对象
}
AbstractAutoProxyCreator类中的getEarlyBeanReference方法主要用于创建原始目标对象的代理对象(早期引用),用于解决循环依赖问题。
接下来依次执行以下方法,无特殊逻辑:
public boolean postProcessAfterInstantiation(Object bean, String beanName) {
return true;
}
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
return pvs;
}
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
最重要的是postProcessAfterInitialization方法,一般Spring AOP会在这里为目标对象创建代理对象,即Bean调用初始化方法(init-method)之后。
// 目标对象bean调用初始化方法之后的处理
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 不是提早暴露的Bean引用,没涉及循环依赖问题,即没创建早期的代理对象,则在这里创建可能的代理对象
if (!this.earlyProxyReferences.contains(cacheKey)) {
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
// 创建代理对象或者返回原实例
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 有自定义的TargetSource且已经创建代理,则不需要再代理
if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
return bean;
}
// 不需要代理,直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 对于实现了Advice,Pointcut,Advisor,AopInfrastructureBean接口(基础设施类)的bean或者是
// 切面的bean,不需要代理,shouldSkip方法判断是否跳过代理
if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
// 将 <cacheKey, FALSE> 键值对放入缓存中,供上面的 if 分支使用
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
// Create proxy if we have advice.
// 1.找出合适的通知器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) { // 需要代理
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 2.创建代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
// 返回代理对象,此时 IOC 容器输入 bean,得到 proxy。此时,
// beanName 对应的 bean 是代理对象,而非原始的 bean
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
// specificInterceptors = null,直接返回 bean
return bean;
}
以上就是Spring AOP主要的创建代理对象的方法,过程比较简单:
- 如果不需要创建代理,则直接返回;
- 若bean是AOP基础设施类型,则直接返回;
- 为bean查找合适的通知器;
- 如果通知器数组不为空,则为bean生成代理对象,并返回该对象;
- 若数组为空,则返回原始 bean。
本篇及后续的文章会对步骤3、4进行分析,下面先分析3的内容。
2.筛选合适的通知器
向目标 bean 中织入通知之前,先要为 bean 筛选出合适的通知器(通知器持有通知)。如何筛选呢?方式由很多,比如可以通过正则表达式匹配方法名,当然更多的时候用的是 AspectJ 表达式进行匹配。那下面就来看一下使用 AspectJ 表达式筛选通知器的过程,如下:
protected Object[] getAdvicesAndAdvisorsForBean(
Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
// 查找合适的通知器
List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
if (advisors.isEmpty()) {
return DO_NOT_PROXY;
}
return advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
List<Advisor> candidateAdvisors = findCandidateAdvisors(); // 查找所有的通知器
// 筛选可应用在 beanClass 上的 Advisor,通过 ClassFilter 和 MethodMatcher
// 对目标类和方法进行匹配
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors); // 扩展操作
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors); // 排序
}
return eligibleAdvisors;
}
如上,Spring 先查询出所有的通知器,然后再调用 findAdvisorsThatCanApply 对通知器进行筛选。在下面几节中,将分别对 findCandidateAdvisors 和 findAdvisorsThatCanApply 两个方法进行分析。
2.1 查找通知器
前面示例显示了AOP xml配置和注解配置的区别,即xml配置的AOP会在Bean容器解析xml之后直接解析为AspectJPointcutAdvisor和AspectJExpressionPointcut BeanDefinition,而注解@Aspect的切面在这个时候还是普通的BeanDefinition,它会在目标bean查找通知器的过程中被解析为一个或多个Advisor。查找通知器的逻辑如下:
// AnnotationAwareAspectJAutoProxyCreator类
protected List<Advisor> findCandidateAdvisors() {
// 调用父类方法从容器中查找所有的通知器
// Add all the Spring advisors found according to superclass rules.
List<Advisor> advisors = super.findCandidateAdvisors(); // 找出实现了Advisor接口的类
// 解析 @Aspect 注解,并构建通知器
// Build Advisors for all AspectJ aspects in the bean factory.
if (this.aspectJAdvisorsBuilder != null) {
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors()); // buildAspectJAdvisors()重点
}
return advisors;
}
AnnotationAwareAspectJAutoProxyCreator 覆写了父类AbstractAdvisorAutoProxyCreator的方法 findCandidateAdvisors,并增加了一步操作,即解析 @Aspect 注解,并构建成通知器。下面先来分析一下父类中的 findCandidateAdvisors 方法的逻辑,然后再来分析 buildAspectJAdvisors 方法的逻辑。
####### (1) findCandidateAdvisors方法分析
先来看一下 AbstractAdvisorAutoProxyCreator 中 findCandidateAdvisors 方法的定义,如下:
// AbstractAdvisorAutoProxyCreator类
protected List<Advisor> findCandidateAdvisors() {
Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
return this.advisorRetrievalHelper.findAdvisorBeans();
}
从上面的源码中可以看出,AbstractAdvisorAutoProxyCreator中的findCandidateAdvisor方法借助于 BeanFactoryAdvisorRetrievalHelper来查找实现了Advisor接口的Bean,即从bean容器中将Advisor类型的bean查找出来。如下:
public List<Advisor> findAdvisorBeans() {
// Determine list of advisor bean names, if not cached already.
String[] advisorNames = this.cachedAdvisorBeanNames; // cachedAdvisorBeanNames 是 advisor 名称的缓存
// 如果 cachedAdvisorBeanNames 为空,这里到容器中查找,
// 并设置缓存,后续直接使用缓存即可
if (advisorNames == null) {
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the auto-proxy creator apply to them!
// 从容器中查找 Advisor 类型 bean 的名称
advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames; // 设置缓存
}
if (advisorNames.length == 0) {
return new ArrayList<>();
}
List<Advisor> advisors = new ArrayList<>();
for (String name : advisorNames) { // 遍历 advisorNames
if (isEligibleBean(name)) {
if (this.beanFactory.isCurrentlyInCreation(name)) { // 忽略正在创建中的 advisor bean
if (logger.isTraceEnabled()) {
logger.trace("Skipping currently created advisor '" + name + "'");
}
}
else {
try {
// 调用 getBean 方法从容器中获取名称为 name 的 bean,
// 并将 bean 添加到 advisors 中
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
catch (BeanCreationException ex) {
Throwable rootCause = ex.getMostSpecificCause();
if (rootCause instanceof BeanCurrentlyInCreationException) {
BeanCreationException bce = (BeanCreationException) rootCause;
String bceBeanName = bce.getBeanName();
if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
if (logger.isTraceEnabled()) {
logger.trace("Skipping advisor '" + name +
"' with dependency on currently created bean: " + ex.getMessage());
}
// Ignore: indicates a reference back to the bean we're trying to advise.
// We want to find advisors other than the currently created bean itself.
continue;
}
}
throw ex;
}
}
}
}
return advisors;
}
以上就是从容器中查找 Advisor 类型的 bean 所有的逻辑。主要做了两件事情:
- 从容器中查找所有类型为Advisor的bean对应的名称(如示例中的
<aop:before ../>
解析出来的AspectJPointcutAdvisor); - 遍历 advisorNames,并从容器中获取对应的 bean。
看完上面的分析,继续来分析一下 @Aspect 注解的解析过程。
####### (2) buildAspectJAdvisors方法分析
与上一节的内容相比,解析 @Aspect 注解的过程还是比较复杂的,需要一些耐心去看。下面开始分析 buildAspectJAdvisors方法的源码,如下:
public List<Advisor> buildAspectJAdvisors() {
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized (this) {
aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList<>();
aspectNames = new ArrayList<>();
// 从容器中获取所有 bean 的名称
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) { // 遍历 beanNames
if (!isEligibleBean(beanName)) {
continue;
}
// We must be careful not to instantiate beans eagerly as in this case they
// would be cached by the Spring container but would not have been weaved.
// 根据 beanName 获取 bean 的类型
Class<?> beanType = this.beanFactory.getType(beanName);
if (beanType == null) {
continue;
}
// 检测 beanType 是否包含 Aspect 注解
if (this.advisorFactory.isAspect(beanType)) {
aspectNames.add(beanName);
AspectMetadata amd = new AspectMetadata(beanType, beanName);
// SINGLETON
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// 获取通知器
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
}
else {
this.aspectFactoryCache.put(beanName, factory);
}
advisors.addAll(classAdvisors);
}
else {
// Per target or per this.
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName +
"' is a singleton, but aspect instantiation instantiate.model is not singleton");
}
MetadataAwareAspectInstanceFactory factory =
new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
if (aspectNames.isEmpty()) {
return Collections.emptyList();
}
List<Advisor> advisors = new ArrayList<>();
for (String aspectName : aspectNames) {
List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
}
else {
MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
上面就是 buildAspectJAdvisors 的代码,看起来比较长,只需关注重点的方法调用即可。在进行后续的分析前,这里先对 buildAspectJAdvisors 方法的执行流程做个总结。如下:
- 获取容器中所有bean的名称(beanName);
- 遍历上一步获取到的bean名称数组,并获取当前beanName对应的bean类型(beanType);
- 根据beanType判断当前bean是否是一个Aspect注解的类,若不是则不做任何处理;
- 调用advisorFactory.getAdvisors获取通知器(InstantiationModelAwarePointcutAdvisorImpl实例)。
下面来重点分析advisorFactory.getAdvisors(factory)
这个调用,如下:
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
// 获取 aspectClass 和 aspectName
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
validate(aspectClass); // 校验切面类
// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
// so that it will only instantiate once. 只会实例化切面对象一次
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new ArrayList<>();
// getAdvisorMethods 用于返回不包含 @Pointcut 注解的方法
for (Method method : getAdvisorMethods(aspectClass)) {
// 为每个方法分别调用 getAdvisor 方法,返回InstantiationModelAwarePointcutAdvisorImpl实例
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
// If it's a per target aspect, emit the dummy instantiating aspect.
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
// Find introduction fields.
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field); // @DeclareParents注解
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
// 获取AspectJ表达式切点AspectJExpressionPointcut
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
// 创建advisor(包含了创建Advise的逻辑) InstantiationModelAwarePointcutAdvisorImpl实例
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
如上,getAdvisor方法包含两个主要步骤,一个是获取AspectJ表达式切点,另一个是创建Advisor实现类。在第二个步骤中,包含一个隐藏步骤——创建Advice。下面将按顺序依次分析这两个步骤,先看获取AspectJ表达式切点的过程,如下:
private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
// 获取方法上的AspectJ相关注解,包括@Before,@After等,包装成AspectJAnnotation
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// 创建一个AspectJExpressionPointcut对象
AspectJExpressionPointcut ajexp =
new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
ajexp.setExpression(aspectJAnnotation.getPointcutExpression()); // 设置切点表达式
if (this.beanFactory != null) {
ajexp.setBeanFactory(this.beanFactory);
}
return ajexp;
}
// ASPECTJ_ANNOTATION_CLASSES中的注解是大家熟悉的
private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {
for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) {
// 查找注解
AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz);
if (foundAnnotation != null) {
return foundAnnotation;
}
}
return null;
}
获取切点的过程并不复杂,不过需要注意的是,目前获取到的切点可能还只是个半成品,需要再次处理一下才行。比如下面的代码:
@Aspect
public class AnnotationAopCode {
@Pointcut("execution(* xyz.coolblog.aop.*.world*(..))")
public void pointcut() {}
@Before("pointcut()")
public void before() {
System.out.println("AnnotationAopCode`s before");
}
}
@Before 注解中的表达式是pointcut()
,也就是说 ajexp 设置的表达式只是一个中间值,不是最终值,即execution(* xyz.coolblog.aop.*.world*(..))
。所以后续还需要将 ajexp 中的表达式进行转换,关于这个转换的过程,不再分析。
说完切点的获取过程,下面再来看看Advisor实现类的创建过程。如下:
public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
this.declaredPointcut = declaredPointcut; // 切点
this.declaringClass = aspectJAdviceMethod.getDeclaringClass(); // 声明类
this.methodName = aspectJAdviceMethod.getName(); // advice方法
this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
this.aspectJAdviceMethod = aspectJAdviceMethod; // advice方法
this.aspectJAdvisorFactory = aspectJAdvisorFactory;
this.aspectInstanceFactory = aspectInstanceFactory;
this.declarationOrder = declarationOrder;
this.aspectName = aspectName; // 切面名
// aspectInstanceFactory.getAspectMetadata(): BeanFactoryAspectInstanceFactory
// 切面是否懒初始化
if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
// Static part of the pointcut is a lazy type.
Pointcut preInstantiationPointcut = Pointcuts.union( // 切点并集
aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
// Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
// If it's not a dynamic pointcut, it may be optimized out
// by the Spring AOP infrastructure after the first evaluation.
this.pointcut = new PerTargetInstantiationModelPointcut(
this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
this.lazy = true;
}
else {
// A singleton aspect. 单例的切面
this.pointcut = this.declaredPointcut;
this.lazy = false;
// 按照Advice方法上的注解解析Advice
this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
}
}
上面是InstantiationModelAwarePointcutAdvisorImpl的构造方法,不过无需太关心这个方法中的一些初始化逻辑。把目光移到构造方法的最后一行代码,即instantiateAdvice(this.declaredPointcut),这个方法用于创建通知 Advice。通知器Advisor是通知Advice的持有者,所以在Advisor实现类的构造方法中创建通知也是合适的。下面就来看看构建通知的过程是怎样的,如下:
private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
return (advice != null ? advice : EMPTY_ADVICE);
}
public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass(); // 切面类
validate(candidateAspectClass); // 验证切面类
// 获取Advice注解
AspectJAnnotation<?> aspectJAnnotation =
AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
if (aspectJAnnotation == null) {
return null;
}
// If we get here, we know we have an AspectJ method.
// Check that it's an AspectJ-annotated class
if (!isAspect(candidateAspectClass)) {
throw new AopConfigException("Advice must be declared inside an aspect type: " +
"Offending method '" + candidateAdviceMethod + "' in class [" +
candidateAspectClass.getName() + "]");
}
if (logger.isDebugEnabled()) {
logger.debug("Found AspectJ method: " + candidateAdviceMethod); // debug
}
AbstractAspectJAdvice springAdvice;
// 按照注解类型生成相应的Advice实现类
switch (aspectJAnnotation.getAnnotationType()) {
case AtPointcut: // @Pointcut -> null
if (logger.isDebugEnabled()) {
logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
}
return null;
case AtAround: // @Around -> AspectJAroundAdvice
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtBefore: // @Before -> AspectJMethodBeforeAdvice
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfter: // @After -> AspectJAfterAdvice
springAdvice = new AspectJAfterAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtAfterReturning: // @AfterReturning -> AspectJAfterReturningAdvice
springAdvice = new AspectJAfterReturningAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterReturningAnnotation.returning())) {
// 设置返回值参数名
springAdvice.setReturningName(afterReturningAnnotation.returning());
}
break;
case AtAfterThrowing: // @AfterThrowing -> AspectJAfterThrowingAdvice
springAdvice = new AspectJAfterThrowingAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
springAdvice.setThrowingName(afterThrowingAnnotation.throwing()); // 设置异常名
}
break;
default:
throw new UnsupportedOperationException(
"Unsupported advice type on method: " + candidateAdviceMethod);
}
// Now to configure the advice... 配置advice
springAdvice.setAspectName(aspectName);
springAdvice.setDeclarationOrder(declarationOrder);
// 获取方法的参数列表名称,比如方法 int sum(int numX, int numY),
// getParameterNames(sum) 得到 argNames = [numX, numY]
String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
if (argNames != null) {
springAdvice.setArgumentNamesFromStringArray(argNames); // 设置参数名
}
springAdvice.calculateArgumentBindings();
return springAdvice;
}
上面的代码逻辑不是很复杂,主要的逻辑就是根据注解类型生成与之对应的通知对象。下面来总结一下获取通知器(getAdvisors)整个过程的逻辑,如下:
- 从目标bean中获取不包含Pointcut注解的方法列表;
- 遍历上一步获取的方法列表,并调用getAdvisor获取当前方法对应的Advisor;
- 创建AspectJExpressionPointcut对象,并从方法的注解中获取表达式,最后设置到切点对象中;
- 创建Advisor实现类对象InstantiationModelAwarePointcutAdvisorImpl;
- 调用instantiateAdvice方法构建通知;
- 调用getAdvice方法,并根据注解类型创建相应的通知。
- 调用instantiateAdvice方法构建通知;
如上所示,上面的步骤做了一定的简化。总的来说,获取通知器的过程还是比较复杂的。下面来分析一下 AspectJMethodBeforeAdvice,也就是@Before注解对应的通知实现类。
####### (3) AspectJMethodBeforeAdvice分析
public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {
public AspectJMethodBeforeAdvice(
Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
super(aspectJBeforeAdviceMethod, pointcut, aif);
}
@Override
public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
// 调用通知方法
invokeAdviceMethod(getJoinPointMatch(), null, null);
}
@Override
public boolean isBeforeAdvice() {
return true;
}
@Override
public boolean isAfterAdvice() {
return false;
}
}
protected Object invokeAdviceMethod(
@Nullable JoinPointMatch jpMatch, @Nullable Object returnValue, @Nullable Throwable ex)
throws Throwable {
// 调用通知方法,并向其传递参数
return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
Object[] actualArgs = args;
if (this.aspectJAdviceMethod.getParameterCount() == 0) {
actualArgs = null;
}
try {
ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
// TODO AopUtils.invokeJoinpointUsingReflection
// 通过反射调用通知方法
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("Mismatch on arguments to advice method [" +
this.aspectJAdviceMethod + "]; pointcut expression [" +
this.pointcut.getPointcutExpression() + "]", ex);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
如上,AspectJMethodBeforeAdvice 的源码比较简单,这里仅关注before方法。这个方法调用了父类中的 invokeAdviceMethod,然后 invokeAdviceMethod 在调用 invokeAdviceMethodWithGivenArgs,最后在 invokeAdviceMethodWithGivenArgs 通过反射执行通知方法。
2.2 筛选合适的通知器
查找出所有的通知器,整个流程还没算完,接下来还要对这些通知器进行筛选。适合应用在当前目标bean上的通知器留下,不适合的就过滤掉。那下面我就来分析一下通知器筛选的过程,如下:
protected List<Advisor> findAdvisorsThatCanApply(
List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
ProxyCreationContext.setCurrentProxiedBeanName(beanName);
try {
// 筛选合适的通知器
return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
}
finally {
ProxyCreationContext.setCurrentProxiedBeanName(null);
}
}
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new ArrayList<>();
for (Advisor candidate : candidateAdvisors) {
// 筛选IntroductionAdvisor类型的通知器
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
// 筛选普通类型的通知器
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
// 从通知器中获取类型过滤器ClassFilter,并调用matches方法进行匹配。
// 以ClassFilter接口的实现类AspectJExpressionPointcut为例,该类的
// 匹配工作由AspectJ表达式相关的机制负责
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;
}
}
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
// 1.使用ClassFilter匹配class
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;
}
// 查找当前类及其父类(以及父类的父类等等)所实现的接口,由于接口中的方法是public,
// 所以当前类可以继承其父类,和父类的父类中所有的接口方法
Set<Class<?>> classes = new LinkedHashSet<>();
if (!Proxy.isProxyClass(targetClass)) {
classes.add(ClassUtils.getUserClass(targetClass));
}
classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
for (Class<?> clazz : classes) {
// 获取clazz的方法列表,包括从父类中继承的方法
Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
for (Method method : methods) {
if (introductionAwareMethodMatcher != null ?
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
// 2.使用methodMatcher匹配方法,匹配成功即可立即返回
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
以上是筛选通知器的过程,筛选的工作主要由ClassFilter和MethodMatcher完成。关于ClassFilter和MethodMatcher,在 AOP 中,切点Pointcut是用来匹配连接点的,以AspectJExpressionPointcut类型的切点为例。该类型切点实现了ClassFilter和MethodMatcher接口,匹配的工作则是由AspectJ表达式相关的机制负责。除了使用AspectJ表达式进行匹配,Spring还提供了基于正则表达式的切点类,以及更简单的根据方法名进行匹配的切点类。
在完成通知器的查找和筛选过程后,还需要进行最后一步处理——对通知器列表进行拓展。
3. 拓展筛选出的通知器列表
拓展方法extendAdvisors做的事情并不多,逻辑也比较简单如下:
protected void extendAdvisors(List<Advisor> candidateAdvisors) {
AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}
public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
// Don't add advisors to an empty list; may indicate that proxying is just not required
// 如果通知器列表是一个空列表,则啥都不做
if (!advisors.isEmpty()) {
boolean foundAspectJAdvice = false;
// 下面的for循环用于检测advisors列表中是否存在
// AspectJ类型的Advisor或Advice
for (Advisor advisor : advisors) {
// Be careful not to get the Advice without a guard, as
// this might eagerly instantiate a non-singleton AspectJ aspect
if (isAspectJAdvice(advisor)) {
foundAspectJAdvice = true;
}
}
// 向 advisors 列表的首部添加 DefaultPointcutAdvisor,
if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
return true;
}
}
return false;
}
private static boolean isAspectJAdvice(Advisor advisor) {
return (advisor instanceof InstantiationModelAwarePointcutAdvisor ||
advisor.getAdvice() instanceof AbstractAspectJAdvice ||
(advisor instanceof PointcutAdvisor &&
((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut));
}
由上面的源码可以看出,extendAdvisors方法调用了AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary方法。至于 makeAdvisorChainAspectJCapableIfNecessary 这个方法,该方法主要的目的是向通知器列表首部添加 DefaultPointcutAdvisor类型的通知器,也就是ExposeInvocationInterceptor.ADVISOR。至于添加此种类型通知器的原因,会在后面文章里说明,这里不便展开。
三、总结
至此,AOP代理创建的第一步——筛选合适的通知器分析结束。