Spring源码分析十三:@Aspect方式的AOP中篇 - getAdvicesAndAdvisorsForBean

文章目录

  • 一、前言
  • 二、寻找所有Advisors - findCandidateAdvisors
    • 1. super.findCandidateAdvisors();
    • 2. this.aspectJAdvisorsBuilder.buildAspectJAdvisors()
      • 2.1. this.advisorFactory.getAdvisors(factory);
        • 2.1.1. 切点信息的获取 - getPointcut
        • 2.1.2. 根据切点信息封装成增强器 - InstantiationModelAwarePointcutAdvisorImpl
  • 三、筛选合适的Advisors - findAdvisorsThatCanApply
  • 四、总结

一、前言

本文是笔者阅读Spring源码的记录文章,由于本人技术水平有限,在文章中难免出现错误,如有发现,感谢各位指正。在阅读过程中也创建了一些衍生文章,衍生文章的意义是因为自己在看源码的过程中,部分知识点并不了解或者对某些知识点产生了兴趣,所以为了更好的阅读源码,所以开设了衍生篇的文章来更好的对这些知识点进行进一步的学习。

全集目录:Spring源码分析:全集整理


本文系列:

  1. Spring源码分析十一:@Aspect方式的AOP上篇 - @EnableAspectJAutoProxy
  2. Spring源码分析十二:@Aspect方式的AOP中篇 - getAdvicesAndAdvisorsForBean
  3. Spring源码分析十三:@Aspect方式的AOP下篇 - createProxy
  4. Spring源码分析二十四:cglib 的代理过程

本文衍生篇:

  1. Spring 源码分析衍生篇九 : AOP源码分析 - 基础篇
  2. Spring 源码分析衍生篇十二 :AOP 中的引介增强

补充篇:

  1. Spring 源码分析补充篇三 :Spring Aop 的关键类

在上篇中我们概述了Aop 实现的逻辑,但是由于篇幅原因,我们将一部分内容拆成了中篇和下篇内容。本篇即中篇,内容主要是讲述 在 Bean创建过程中Aop 挑选适用于当前Bean的增强Advisor。准备用于代理使用。由于篇幅连贯性,建议看完上篇后再来看本篇内容。

getAdvicesAndAdvisorsForBean 的实现在 AbstractAdvisorAutoProxyCreator 类中。getAdvicesAndAdvisorsForBean 的作用是获取所有适用于当前Bean 的 Advisors 。因为并不是所有的规则都适用于当前bean,所有会有一个筛选的过程。

这个方法的逻辑分为两步:

  1. 寻找所有的顾问(Advisors), 这个方法在 AnnotationAwareAspectJAutoProxyCreator 中被重写了,为了可以的动态生成 Advisor – findCandidateAdvisors
  2. 寻找所有顾问(Advisors) 中适用于当前 bean 的增强并应用 – findAdvisorsThatCanApply

下面我们来看详细代码:

	@Override
	@Nullable
	protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
		// 主要逻辑还是在 findEligibleAdvisors 中完成。
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		//如果没有增强点则不需要代理。
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}

   	...
	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
		// 1. 寻找所有的增强
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		// 2. 寻找所有增强中适用于bean 的增强并应用
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}

可以看到两个核心方法 findCandidateAdvisorsfindAdvisorsThatCanApply 。下面我们一个一个来分析。

二、寻找所有Advisors - findCandidateAdvisors

前文讲过,Spring aop 注入的自动代理创建器是 AnnotationAwareAspectJAutoProxyCreator,所以AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors 的代码如下:

	@Override
	protected List<Advisor> findCandidateAdvisors() {
		// Add all the Spring advisors found according to superclass rules.
		// 1. 这里是从BeanFactory 中找出来 所有 Advisor 类型的bean。即找到所有配置的Advisor。
		List<Advisor> advisors = super.findCandidateAdvisors();
		// Build Advisors for all AspectJ aspects in the bean factory.
		if (this.aspectJAdvisorsBuilder != null) {
			// 2. buildAspectJAdvisors() 从代码中动态找到了需要的增强点
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}

这里可以看到,AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors 中通过

  1. super.findCandidateAdvisors(); 调用了父类的 AbstractAdvisorAutoProxyCreator#findCandidateAdvisors 的方法来获取 Advisor
  2. 调用 this.aspectJAdvisorsBuilder.buildAspectJAdvisors() 方法来获取Advisor

这两个方法都是为了获取 Advisor,区别在于

  • super.findCandidateAdvisors(); : 一般获取的都是通过直接注册的 Advisors。比如事务的顾问,直接通过 @Bean 注入到Spring容器中。
    Spring源码分析十三:@Aspect方式的AOP中篇 - getAdvicesAndAdvisorsForBean_第1张图片
  • this.aspectJAdvisorsBuilder.buildAspectJAdvisors() : 主要获取我们通过注解方式动态注册的 Advisors。比如 在 Aop 中根据不同的表达式,每个@Pointcut 注解的切点不同,也就会对不同的Bean起作用,并且对于每个@Pointcut来说都有@Before、@After 等不同的操作,那么每个@Pointcut 以及其对应的操作都会被封装成一个一个的Advisor 返回。下面会有详细解读。

1. super.findCandidateAdvisors();

super.findCandidateAdvisors(); 说白了就是直接获取 容器中的 Advisor 类型的Bean。

super.findCandidateAdvisors(); 这里调用的实际上是AbstractAdvisorAutoProxyCreator 中的findCandidateAdvisors 方法。这一步最终会调用如下的findAdvisorBeans 方法。其作用根据注释也能明白。获取所有合格的 Advisor Bean(合格并不代表适用当前bean),忽略了FactoryBean 和创建中的bean。

	 // org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans
	public List<Advisor> findAdvisorBeans() {
		// Determine list of advisor bean names, if not cached already.
		// 从缓存中获取 advisorNames。因为每个Bean创建的时候都会进行一次获取,所以对增强的缓存是必须的
		String[] advisorNames = this.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!
			// 注释: 不要在这里初始化FactoryBeans:我们需要保留所有未初始化的常规bean,以使自动代理创建者对其应用!  个人理解是防止有的FactoryBean可能会被增强代理,而在这里初始化,则会没有办法进行代理
			// 从 Spring 中获取 Advisor 类型的 beanname 。这里获取到的一般都是硬编码注入的 Advisors 
			advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
					this.beanFactory, Advisor.class, true, false);
			this.cachedAdvisorBeanNames = advisorNames;
		}
		// 如果没有获取到 Advisors  ,直接返回
		if (advisorNames.length == 0) {
			return new ArrayList<>();
		}
		// 到这一步必定有Advisors ,我们需要通过name来获取到bean的实例 
		List<Advisor> advisors = new ArrayList<>();
		for (String name : advisorNames) {
			// 当前Bean 是否合格,这里调用的是 AbstractAdvisorAutoProxyCreator#isEligibleAdvisorBean 直接返回true,供子类扩展。
			if (isEligibleBean(name)) {
				// 如果 name 指向的 bean 正在创建中则跳过
				if (this.beanFactory.isCurrentlyInCreation(name)) {
					if (logger.isTraceEnabled()) {
						logger.trace("Skipping currently created advisor '" + name + "'");
					}
				}
				else {
					try {
						// 否则从容器中根据name 和 类型获取到 Advisor 实例,添加到 advisors 集合中
						advisors.add(this.beanFactory.getBean(name, Advisor.class));
					}
					catch (BeanCreationException ex) {
						Throwable rootCause = ex.getMostSpecificCause();
						// 如果是异常时因为bean正在创建引起的在,则 continue
						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;
					}
				}
			}
		}
		// 返回得到的合格的 Advisor 集合
		return advisors;
	}

2. this.aspectJAdvisorsBuilder.buildAspectJAdvisors()

this.aspectJAdvisorsBuilder.buildAspectJAdvisors() 的作用就是 在当前的bean工厂中查找带有AspectJ注解的 Aspect bean,并封装成代表他们的Spring Aop Advisor,注入到Spring 中

基本的思路如下:

  1. 获取所有beanName,这一步所有在beanFactory中注册的bean都会被提取出来
  2. 遍历所有的beanName, 找出声明AspectJ注解的类,进行进一步处理
  3. 对标记为AspectJ注解的类进行Advisors 提取
  4. 将提取的结果保存到缓存中。
	public List<Advisor> buildAspectJAdvisors() {
		// aspectBeanNames 中缓存了被 @Aspect 修饰的 bean的name
		List<String> aspectNames = this.aspectBeanNames;
		// 如果为空表示尚未缓存,进行缓存解析。这里用了DLC 方式来进行判断
		if (aspectNames == null) {
			synchronized (this) {
				aspectNames = this.aspectBeanNames;
				if (aspectNames == null) {
					List<Advisor> advisors = new ArrayList<>();
					aspectNames = new ArrayList<>();
					// 1. 获取所有的beanName。从容器中获取所有的BeanName
					String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
							this.beanFactory, Object.class, true, false);
					// 遍历beanname, 找出对应的增强方法
					for (String beanName : beanNames) {
						// 不合法的bean略过,由子类定义规则,默认true
						if (!isEligibleBean(beanName)) {
							continue;
						}
						// 注释 :我们必须小心,不要急于实例化bean,因为在这种情况下,它们将由Spring容器缓存,但不会被编织。
						// 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.
						// 获取对应 bean 的类型
						Class<?> beanType = this.beanFactory.getType(beanName);
						if (beanType == null) {
							continue;
						}
						// 2. 如果bean 被 @AspectJ 注解修饰 且不是Ajc 编译, 则进一步处理
						if (this.advisorFactory.isAspect(beanType)) {
							// 添加到缓存中
							aspectNames.add(beanName);
							// 封装成AspectMetadata 
							AspectMetadata amd = new AspectMetadata(beanType, beanName);
							// aspect 存在 SINGLETON、PERTHIS、PERTARGET、PERCFLOW、PERCFLOWBELOW、PERTYPEWITHIN模式。默认为SINGLETON 。暂不明白意义
							if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
								MetadataAwareAspectInstanceFactory factory =
										new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
								// 3. 解析标记AspectJ注解中的增强方法,也就是被 @Before、@Around 等注解修饰的方法,并将其封装成 Advisor
								List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
								if (this.beanFactory.isSingleton(beanName)) {
									this.advisorsCache.put(beanName, classAdvisors);
								}
								else {
									this.aspectFactoryCache.put(beanName, factory);
								}
								// 保存 Advisor 
								advisors.addAll(classAdvisors);
							}
							else {
								// Per target or per this.
								// 如果当前Bean是单例,但是 Aspect 不是单例则抛出异常
								if (this.beanFactory.isSingleton(beanName)) {
									throw new IllegalArgumentException("Bean with name '" + beanName +
											"' is a singleton, but aspect instantiation 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();
		}
		// 4. 将所有的增强方法保存到缓存中。
		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;
	}

上面的方法一句话总结 : 获取容器中所有被 @Aspect 修饰 && 不是 Ajc 编译 的类,动态解析内容,封装成 Advisor保存到对应集合中。

下面我们来详细看看具体实现:

2.1. this.advisorFactory.getAdvisors(factory);

在上述代码中,最为复杂的就是增强器(Advisors)获取,也就是 this.advisorFactory.getAdvisors(factory); 这一步,
具体的实现是在 ReflectiveAspectJAdvisorFactory#getAdvisors中。下面我们具体来看代码

	@Override
	public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
		// 获取标记为 AspectJ 的类
		Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
		// 获取标记为 AspectJ 的名字
		String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
		// 进行合法性验证
		validate(aspectClass);

		// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
		// so that it will only instantiate once.
		// 这里需要 MetadataAwareAspectInstanceFactory  ,所以这里初始化了一次
		MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
				new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

		List<Advisor> advisors = new ArrayList<>();
		// getAdvisorMethods(aspectClass) 获取 aspectClass 中没有被 @PointCut 注解修饰的方法
		for (Method method : getAdvisorMethods(aspectClass)) {
			// 将方法封装成 Advisor 。如果找不到@PointCut 的信息,则会返回 null。下面详解
			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.
		// 获取 DeclaredParents 注解并处理。@DeclaredParents  注解可以实现指定某些代理类是某些接口的实现。
		for (Field field : aspectClass.getDeclaredFields()) {
			Advisor advisor = getDeclareParentsAdvisor(field);
			if (advisor != null) {
				advisors.add(advisor);
			}
		}

		return advisors;
	}


这里根据 切点信息来动态生成了增强器,也就是 Advisor。是根据AOP 的注解解析来的动态生成的。 可以看到,封装的关键的操作还是在 getAdvisor 方法 中,下面我们来详细分析:

	// 筛选出合适的方法,并封装成 Advisor 。这里返回的都是 InstantiationModelAwarePointcutAdvisorImpl
	public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
			int declarationOrderInAspect, String aspectName) {
		// 又进行一次合法性校验
		validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
		// 1. 切点信息的获取。这里如果没有被Aspect 系列注解(Pointcut、Around、Before等)修饰会返回null
		AspectJExpressionPointcut expressionPointcut = getPointcut(
				candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
		// 如果获取不到相关信息直接返回null
		if (expressionPointcut == null) {
			return null;
		}
		// 2. 根据切点信息封装成增强器
		return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
				this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
	}

可以看到在 getAdvisor 方法中的关键两步: 切点信息的获取根据切点信息封装成增强器。下面我们来继续分析

2.1.1. 切点信息的获取 - getPointcut

getPointcut 方法的实现很简单,就是判断方法上是否有 AspectJ系列的注解,有则封装。

	private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
		// 获取方法上的注解,包括 Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class
		AspectJAnnotation<?> aspectJAnnotation =
				AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
		if (aspectJAnnotation == null) {
			return null;
		}
		// 到这里必然有 AspectJ系列的注解了
		// 使用 AspectJExpressionPointcut 实例封装获取的信息
		AspectJExpressionPointcut ajexp =
				new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
		// 提取出的得到的注解中的表达式
		// 如  @Pointcut("execution(* com.kingfish.aopdemo.controller.AopController.hello(String))") 中的 execution(* com.kingfish.aopdemo.controller.AopController.hello(String))
		// 对于 @Before("pointCut()") 获取的则是 pointCut()
		ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
		if (this.beanFactory != null) {
			ajexp.setBeanFactory(this.beanFactory);
		}
		return ajexp;
	}

其中 AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); 方法如下:

	private static final Class<?>[] ASPECTJ_ANNOTATION_CLASSES = new Class<?>[] {
			Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
	
	// 获取指定方法上的注解并使用  AspectJAnnotation 封装
	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;
	}

2.1.2. 根据切点信息封装成增强器 - InstantiationModelAwarePointcutAdvisorImpl

在上面的代码中我们看到,ReflectiveAspectJAdvisorFactory#getAdvisor 最终封装成了一个InstantiationModelAwarePointcutAdvisorImpl 返回。实际上,在 Aop 中所有的增强都由 Advisor 的实现类InstantiationModelAwarePointcutAdvisorImpl 统一封装。我们来看看InstantiationModelAwarePointcutAdvisorImpl 中做了什么事

	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();
		this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
		this.aspectJAdviceMethod = aspectJAdviceMethod;
		this.aspectJAdvisorFactory = aspectJAdvisorFactory;
		this.aspectInstanceFactory = aspectInstanceFactory;
		this.declarationOrder = declarationOrder;
		this.aspectName = aspectName;
		// 懒加载实例
		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;
			this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
		}
	}

	...
	
	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);
	}

可以看到,InstantiationModelAwarePointcutAdvisorImpl 在封装过程中只是简单的将信息封装在类的实例中,所有的信息只是单纯的赋值。但是需要注意的是,在信息赋值结束后调用了 instantiateAdvice(this.declaredPointcut); 方法,这个方法完成了对于增强器的处理。

因为不同的增强体现的逻辑是不同的,简单来说就是不同的切点信息的动作是不同的,比如 @Before@After 注解的动作就不同, @Before 需要在切点方法前调用, @After 需要在切点方法后调用。这里根据不同的注解封装成了不同的 Advice,用以区分在适当的时候调用适当的方法。

而根据注解中的信息初始化对应的增强器就是在instantiateAdvice 中实现。而instantiateAdvice 中主要还是调用了 this.aspectJAdvisorFactory.getAdvice,因此我们来看 this.aspectJAdvisorFactory.getAdvice 的代码:

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
			MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

		Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
		validate(candidateAspectClass);

		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);
		}

		AbstractAspectJAdvice springAdvice;
		// 根据不同的注解生成不同的通知(增强)
		switch (aspectJAnnotation.getAnnotationType()) {
			case AtPointcut:
				if (logger.isDebugEnabled()) {
					logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
				}
				return null;
			case AtAround:
				springAdvice = new AspectJAroundAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtBefore:
				springAdvice = new AspectJMethodBeforeAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtAfter:
				springAdvice = new AspectJAfterAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				break;
			case AtAfterReturning:
				springAdvice = new AspectJAfterReturningAdvice(
						candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
				AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
				if (StringUtils.hasText(afterReturningAnnotation.returning())) {
					springAdvice.setReturningName(afterReturningAnnotation.returning());
				}
				break;
			case AtAfterThrowing:
				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...
		springAdvice.setAspectName(aspectName);
		springAdvice.setDeclarationOrder(declarationOrder);
		String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
		if (argNames != null) {
			springAdvice.setArgumentNamesFromStringArray(argNames);
		}
		springAdvice.calculateArgumentBindings();

		return springAdvice;
	}

可以看到,Spring会根据不同的注解生成不同的增强器(通知)。比如 AspectJAroundAdviceAspectJMethodBeforeAdvice 等,从而完成不同的注解所需的动作。

这里举个简单例子总结一下:
如下:

@Component
@Aspect
public class AopDemo {
    
    @Pointcut("execution(* com.kingfish.aopdemo.controller.AopController.hello(String)) && args(msg)")
    public void pointCut(String msg) {
        System.out.println("AopDemo.pointCut : msg = " + msg);
    }

    @After("pointCut(msg)")
    public void after(String msg) {
        System.out.println("after msg = " + msg);
    }


    @Before("pointCut(msg)")
    public void before(String msg) {
        System.out.println("before msg = " + msg);
    }
}

如上一个类,

  1. 容器启动后会加载硬编码注入的Advisor,加载结束后扫描 @Aspect 注解类。
  2. 解析注解类里面的非 @PointCut 方法。即after、before 方法,对其注解进行信息进行解析,封装成 InstantiationModelAwarePointcutAdvisorImpl
  3. InstantiationModelAwarePointcutAdvisorImpl 中存在的 PointCut 和 Advice 两大属性,PointCut 代表切入点,Advice 代表增强的具体操作,根据不同的注解类型,加载不同的Advice 实现类。如下
    Spring源码分析十三:@Aspect方式的AOP中篇 - getAdvicesAndAdvisorsForBean_第2张图片
  4. 最终会封装成两个 InstantiationModelAwarePointcutAdvisorImpl ,After 方法对应 是 AspectJAfterAdvice,Before 方法对应的是 AspectJMethodBeforeAdvice,Pointcut 都是 pointCut(msg)

三、筛选合适的Advisors - findAdvisorsThatCanApply

经历了第一步,也仅仅是将所有的顾问(Advisors),也就是增强器,全部查找出来。但是并非所有的Advisors 都适用于当前bean。所以这一步的目的是为了过滤出适合当前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);
		}
	}

可以看到关键内容还是在 AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
所以我们继续往下看。AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass); 的代码如下:

	public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
		if (candidateAdvisors.isEmpty()) {
			return candidateAdvisors;
		}
		List<Advisor> eligibleAdvisors = new ArrayList<>();
		// 首先处理引介增强
		for (Advisor candidate : candidateAdvisors) {
			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;
			}
			// 对于普通bean 的处理
			if (canApply(candidate, clazz, hasIntroductions)) {
				eligibleAdvisors.add(candidate);
			}
		}
		return eligibleAdvisors;
	}

引介增强和普通的增强处理是不同的,所以需要分开处理。而通过上面的代码,我们可以看到关键逻辑在 canApply函数中,因此我们直接看这个函数。

	public static boolean canApply(Advisor advisor, Class<?> targetClass) {
		return canApply(advisor, targetClass, false);
	}
	
	....
	
	public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
		// 基础篇曾介绍过,IntroductionAdvisor 和 PointcutAdvisor 的区别在于 PointcutAdvisor 的切入点更细。我们这里的Advisor都是PointcutAdvisor 类型
		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;
		}
	}
	
	....
	
	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  的实现是 AspectJExpressionPointcut
		IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
		if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
			introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
		}

		Set<Class<?>> classes = new LinkedHashSet<>();
		if (!Proxy.isProxyClass(targetClass)) {
			classes.add(ClassUtils.getUserClass(targetClass));
		}
		classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

		for (Class<?> clazz : classes) {
			// 获取当前bean的所有方法
			Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
			for (Method method : methods) {
				// 在这里判断方法是否匹配
				if (introductionAwareMethodMatcher != null ?
						// 这里调用 AspectJExpressionPointcut#matches
						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
						methodMatcher.matches(method, targetClass)) {
					return true;
				}
			}
		}

		return false;
	}

注:introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) 实际上调用的是 AspectJExpressionPointcut#matches,该方法中会通过 AspectJExpressionPointcut#getTargetShadowMatch 调用 AspectJExpressionPointcut#getShadowMatch ,在该方法中对表达式进行了校验,并返回了一个 ShadowMatch 类,包含了校验后的结果信息。


从上面我们可以看到 Pointcut 匹配的需要满足下面两个条件:

  1. pc.getClassFilter().matches(targetClass) 返回true
  2. pc.getMethodMatcher().matches(method, targetClass) 返回true

四、总结

getAdvicesAndAdvisorsForBean 方法的作用就是筛选出适用于当前bean的Advisor。
简单来说就是两步

  1. AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors 挑选出所有的 Advisor。在 其中 通过 super.findCandidateAdvisors() 调用了 AbstractAdvisorAutoProxyCreator#findCandidateAdvisors 来完成了对硬编码注入的Advisor 的获取解析返回。随后通过 this.aspectJAdvisorsBuilder.buildAspectJAdvisors() 方式解析了 Aop 注解方式动态封装的 Advisor 并保存。
  2. findAdvisorsThatCanApply 通过 Advisor 中的Pointcut 筛选出适合当前bean的 Advisor。

以上:内容部分参考
《Spring实战》
《Spring源码深度解析》
https://www.cnblogs.com/cheng21553516/p/12190008.html
https://blog.csdn.net/wyl6019/article/details/80136000
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

你可能感兴趣的:(#,源码分析篇,spring,java)