Spring源码深度解析:十五、@Aspect方式的AOP中篇 - getAdvicesAndAdvisorsForBean

news2024/11/28 0:56:20

一、前言

文章目录:Spring源码深度解析:文章目录

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

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

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

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

下面我们来看详细代码:

AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean()

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

AbstractAdvisorAutoProxyCreator#findEligibleAdvisors()

	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的代码如下:

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
    调用this.aspectJAdvisorsBuilder.buildAspectJAdvisors()方法来获取Advisor

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

  • super.findCandidateAdvisors(); : 一般获取的都是通过直接注册的Advisors。比如事务的顾问,直接通过@Bean注入到Spring容器中。
    在这里插入图片描述
  • 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

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.isDebugEnabled()) {
						logger.debug("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.isDebugEnabled()) {
									logger.debug("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. 将提取的结果保存到缓存中。
    BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors()
	 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()中。下面我们具体来看代码

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方法 中,下面我们来详细分析:
ReflectiveAspectJAdvisorFactory#getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,int declarationOrderInAspect, String aspectName)

	// 筛选出合适的方法,并封装成 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系列的注解,有则封装。

@Nullable
	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中存在的PointCutAdvice两大属性,PointCut代表切入点,Advice代表增强的具体操作,根据不同的注解类型,加载不同的Advice实现类。如下
    在这里插入图片描述
  4. 最终会封装成两个InstantiationModelAwarePointcutAdvisorImplAfter方法对应是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匹配的需要满足下面两个条件:

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

四、总结

getAdvicesAndAdvisorsForBean()方法的作用就是筛选出适用于当前beanAdvisor
简单来说就是两步

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

以上:内容部分参考
《Spring源码深度解析》
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/85685.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

尚医通-MyBatistPlus:修改和自动填充-乐观锁(三)

目录&#xff1a; &#xff08;1&#xff09;MyBatistPlus&#xff1a;修改和自动填充 &#xff08;2&#xff09;MyBatis-Plus-乐观锁 &#xff08;1&#xff09;MyBatistPlus&#xff1a;修改和自动填充 1、更新操作 注意&#xff1a;update时生成的sql自动是动态sql&…

14 - 局部段描述符表

---- 整理自狄泰软件唐佐林老师课程 文章目录1. 什么是局部段描述符表&#xff08;LDT&#xff09;1.1 局部段描述符选择子1.2 局部段描述符表LDT1.3 问题1.4 LDT的定义和使用2. 编程实验&#xff1a;使用LDT实现新功能3. 多任务程序设计的实现思路4. 待解决的问题&#xff1a;…

第51篇 Qt 5.5全新的开始

导语 时间转眼而逝&#xff0c;看一下上次发的教程&#xff0c;已经是一年前的事情了。这一年发生了很多事情&#xff0c;包括自己也包括Qt。当然&#xff0c;自己很忙或者说为了编写《Qt 5编程入门》这些理由&#xff0c;并不能为一年的搁置进行开脱&#xff0c;所以这里首先…

人工智能前沿——6款AI绘画生成工具

>>>深度学习Tricks&#xff0c;第一时间送达<<< 目录 一、【前言】 二、【6款AI绘画生成工具】 1.DeepAI 2.NightCafe 3.Deep Dream Generator 4.StarryAI 5.Fotor 6.Pixso 一、【前言】 AI不仅影响商业和医疗保健等行业&#xff0c;还在创意产业中…

Flutter Web CORS解决方案2-代理转发协议

Flutter Web CORS解决方案2local-cors-proxyshelf_proxydart run shelf_proxyflutter run --dart-definewrap with shellhelp & usagemode & confrun & debuglaunch with proxyvscodeAndroid Studio遗留问题本文介绍第二种解决FlutterWeb CORS问题的方案&#xff1…

[附源码]Nodejs计算机毕业设计基于web的企业人事管理系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…

Centos7下Samba服务器配置

环境 vm下centos7.6&#xff0c;IP地址&#xff1a;192.168.139.200 Samba概述 Samba是在Linux和UNIX系统上实现SMB协议的一个免费软件&#xff0c;由服务器及客户端程序构成。SMB&#xff08;Server Messages Block&#xff0c;信息服务块&#xff09;是一种在局域网上共享文…

STM32H747AGI6技术、STM32H747AII6规格、STM32H747BGT6产品概述

产品概述&#xff1a;STM32H7高性能MCU基于高性能Arm Cortex-M7 32位RISC内核&#xff0c;工作频率高达400MHz。Cortex-M7内核具有浮点单元 (FPU) 精度&#xff0c;支持Arm双精度&#xff08;符合IEEE 754标准&#xff09;和单精度数据处理指令与数据类型。STM32H7 MCU支持全套…

第48篇 进阶(八) 3D绘图简介

导语 OpenGL是一个跨平台的用来渲染3D图形的标准API。在Qt中提供了QtOpenGL模块&#xff0c;从而很轻松地实现了在Qt应用程序中使用OpenGL&#xff0c;这主要是在QGLWidget类中完成的。因为3D绘图涉及到了专业方面的内容&#xff0c;我们下面只是讲解最简单的使用&#xff0c;…

Java基础之LinkedList

Java基础之LinkedList一、介绍二、add()一、介绍 底层是一个双向链表实现的List&#xff0c;内部每一个节点采用内部类Node表示&#xff0c;通过first、last引用分别指向链表的第一和最后一个元素非线程安全&#xff0c;可以用Collections.synchronizedList()方法对其进行包装…

Android入门第44天-Android里使用动态BroadCast

BroadCast是什么 BroadcastReceiver就是应用程序间的全局大喇叭&#xff0c;即通信的一个手段&#xff0c; 系统自己在很多时候都会发送广播&#xff0c;比如电量低或者充足&#xff0c;刚启动完&#xff0c;插入耳机&#xff0c;你有一条新的微信消息。。。这种都是使用Broad…

Windows实时运动控制软核(二):LOCAL高速接口测试之Qt

今天&#xff0c;正运动小助手给大家分享一下MotionRT7的安装和使用&#xff0c;以及使用Qt对MotionRT7开发的前期准备。 01 MotionRT7简介 MotionRT7是深圳市正运动技术推出的跨平台运动控制实时内核&#xff0c;也是国内首家完全自主自研&#xff0c; 自主可控的Windows运动…

redis的客户端

关系型数据库存储在磁盘当中&#xff0c;非关系型数据库存储在内存中 Jedis 第一步&#xff1a;导入依赖包 <dependencies><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.7.0</vers…

操作系统学习笔记

1.1 操作系统的概念、功能和目标 1.1.1 操作系统的功能和目标–系统资源的管理者 进程是一个程序的执行过程。执行前需要将该程序放到内存中&#xff0c;才能被CPU处理。 1.1.2 用户和计算机硬件之间的接口 联机命令接口交互式命令接口 脱机命令接口批处理命令接口 1.1.3…

点击化学标记试剂N3-PEG-DBCO,DBCO-PEG-azide,二苯并环辛炔聚乙二醇叠氮

N3-PEG-DBCO &#xff0c;DBCO PEG N3 &#xff0c;DBCO-PEG-azide&#xff0c;叠氮聚乙二醇环辛炔&#xff0c;二苯并环辛炔聚乙二醇叠氮产品规格&#xff1a; 1.分子量&#xff1a;1k&#xff0c;2k&#xff0c;3.4k&#xff0c;5k&#xff0c;10k&#xff0c;20k&#xff08…

百度地图三维盒子模型标注

效果图 利用百度地图mapv实现地图盒子模型标准 代码 function box() {var griddata [];for (var i 0; i < arr.length; i) {griddata.push({geometry: {type: Point,coordinates: arr[i]},properties: {count: Math.random() * (i 1) * 10000}});}var grid new mapvgl…

MySQL索引下推

1.什么是索引下推? 索引下推(Index Condition PushDown,简称ICP)是从MySQL5.6开始引入的一个特性,索引下推通过减少回表的次数来提高数据库的查询效率; 2.案例 准备: ①.为了演示索引下推,需要安装MySQL5.5和MySQL5.7两个版本的MySQL,因为索引下推是MySQL5.6版本中开始引入…

Java学习之断点调试

目录 应用场景 重要提示 定义 快捷键 运行图 案例1 案例2 案例3 案例4 练习题 第一题 题目 代码 toString方法 第二题 题目 应用场景 查找错误时&#xff0c;用断点调试一步一步的看源码执行的过程&#xff0c;从而发现错误所在 重要提示 在断点调试&#xff08;…

搭建hadoop单机环境

hadoop 笔记 sbin: 一些启动脚本 【服务端的 server bin】logs: 存放 hadoop 相关日志bin: 客户端的脚本etc: hadoop 相关的配置文件 格式化文件系统 配置免密码登录 ssh-keygen -t rsa -P -f ~/.ssh/id_rsa cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys# 启动…

Android DataBinding之布局中(layout)事件、运算逻辑、资源、工具类的使用与详解(七)

一、介绍 如果你学习了我前面的六篇文章&#xff0c;基本能够熟悉的处理data binding在开发过程的大多数业务&#xff0c;但是由于layout中的数据不一定满足UI上的效果&#xff0c;正常都是提前处理好数据&#xff0c;如果我们在layout引入中事件、运算逻辑、资源、工具类&…