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

news2024/11/16 11:28:40

一、前言

文章目录: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/83234.html

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

相关文章

html简洁风格的个人博客网站模板(源码)

文章目录1.设计来源1.1 博客首界面1.2 个人简介界面1.3 日常记录界面1.4 文章列表界面1.5 文章信息界面2.结构源码2.1 目录结构2.2 源代码源码下载作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/128288153 html简洁风格…

qtday1:2、重新手动实现对象树模型

代码段加注释 #include <iostream> #include<list> using namespace std; class A { public:list<A *> child; //创建一个链表 public:A(A *parent nullptr){if(parent !nullptr) //说明有父组件{parent->child.push_back(this); //有父组件就将该…

数商云SRM供应商系统询比价采购业务流程介绍,重塑汽修企业核心竞争力

众所周知&#xff0c;采购供应是关系企业经营效益的重要工作。在汽修行业&#xff0c;由于汽车配件的车型规格繁多&#xff0c;技术业务性强&#xff0c;各种类商品采购过程的艰难性、销售状况的复杂性等等&#xff0c;汽配采购往往容易陷入种种困境&#xff0c;极大降低汽修企…

Qt-Web混合开发-QWebEngineView加载网页最小示例(1)

Qt-Web混合开发-QWebEngineView加载网页最小示例&#x1f4a5; 文章目录Qt-Web混合开发-QWebEngineView加载网页最小示例&#x1f4a5;1、概述&#x1f4af;2、实现效果&#x1f4a6;3、实现功能&#x1f4ac;4、关键代码&#x1f4a4;5、源代码&#x1f648;更多精彩内容&…

【20天快速掌握Python】day01-Python入门

1、什么是Python&#xff1f; Python是一门解释型的编程语言&#xff0c;而且是现在世界上最流行的编程语言之一。 2、Python优缺点 优点 简单&#xff1a;Python是一种代表简单主义思想的语言。阅读一个良好的Python程序就感觉像是在读英语一样&#xff0c;尽管这个英语的要…

driftingblues靶机(0ok编码)

环境准备 靶机链接&#xff1a;百度网盘 请输入提取码 提取码&#xff1a;oylc 虚拟机网络链接模式&#xff1a;桥接模式 攻击机系统&#xff1a;kali linux 2021.1 信息收集 1.探测目标靶机ip arp-scan -l 2.探测目标靶机开放端口和服务情况 nmap -A -sV -p- 192.168.…

SpringCloud之微服务环境搭建

目录 1、微服务介绍 1.1.微服务架构介绍 1.2.微服务架构的常见问题 1.3.微服务架构的常见解决方案 1.3.1.ServiceComb ​ 1.3.2.SpringCloud 1.3.3.SpringCloud Alibaba 2、微服务环境搭建 2.1案例准备 技术选项&#xff1a; 模块设计&#xff1a; …

pyqt5打包后的exe文件在网吧windoes7系统运行报错问题

方案一 用录屏软件录个视频&#xff0c;然后用播放软件打开逐帧查看&#xff0c;找到报错原因&#xff0c;一般是某个包导入错误&#xff0c;重新安装下对应的包。(忒麻烦) 方案二 生成的exe文件你们执行的话&#xff0c;cmd黑框肯定会一闪而过&#xff0c;但是没有关系&…

[附源码]Nodejs计算机毕业设计基于Java的在线点餐系统Express(程序+LW)

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

使用 VADER 对股票新闻进行情绪分析

什么是情感分析&#xff1f; 提示&#xff1a;情感分析 定义&#xff1a; 情感分析或意见挖掘是自然语言处理 (NLP)的一个子领域&#xff0c;它试图在给定文本中识别和提取意见。情感分析的目的是根据文本中主观性的计算处理来衡量说话者/作者的态度、情绪、评价、态度和情绪…

WIN10 共享文件夹并取消密码访问

目录 一、前言 二、共享文件过程 1、选择需要共享的文件夹右键-授予访问权限-特定用户 2、选择共享用户Everyone并点击添加 3、再点击右下角共享&#xff0c;一个共享目录就生成了 4、但是别人访问还需要提供密码&#xff0c;非常不方便&#xff0c;此时需要关闭密码 一、…

DataHub Docker安装 PostreSQL元数据集成

install docker & docker-compose&#xff0c;包括docker-compose升级 curl -L https://github.com/docker/compose/releases/download/v2.14.0/docker-compose-uname -s-uname -m-o /usr/local/bin/docker-compose install jq wget http://dl.fedoraproject.org/pub/epel…

前端大文件上传及切片上传-提升上传效率

一、使用场景&#xff1a; 1.大文件上传 2.网络环境环境不好&#xff0c;存在需要重传风险的场景 二、名词解释&#xff1a; 切片上传&#xff1a;也叫分片上传&#xff0c;就是将所要上传的文件&#xff0c;按照一定的大小&#xff0c;将整个文件分隔成多个数据块来进行分…

【Docker】Docker如何构建自己的镜像?从镜像构建到推送远程镜像仓库图文教程

专栏往期文章 《Docker是什么&#xff1f;Docker从介绍到Linux安装图文详细教程》《30条Docker常用命令图文举例总结》 本期目录专栏往期文章1. 构建镜像2. 本地镜像发布到公有云3. 本地镜像发布到私有云1. 构建镜像 提交构建镜像的命令如下&#xff1a; $ docker commit -m…

大二学生《web课程设计》中华英雄人物介绍袁隆平HTML+CSS+JavaScript(期末考核大作业)

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

2021年网络安全省赛--服务器内部信息获取解析(中职组)

2021年省赛服务器内部信息获取解析 任务环境说明:Linux20210510 服务器场景操作系统:未知 (关闭连接) 服务器场景操作系统:Linux(封闭靶机) 用户名:test密码:123456 1.收集服务器场景中的服务信息。并获取服务器中开放的端口号信息,将服务器端口号作为flag提交…

GCN解读并附数据处理代码

此文GCN不是之前提到的lightGCN&#xff0c;而是真正的GCN图卷积&#xff0c;这个问题源于paper分类&#xff0c;同样是GAT所用的数据&#xff0c;其中paper之前的引用关系构成了图的边信息&#xff0c;之所以称之为半监督,并不是因为部分paper没有label及embedding信息&#x…

Nacos--命名空间、分组、ID的概念及用法

原文网址&#xff1a;Nacos--命名空间、分组、ID的概念及用法_IT利刃出鞘的博客-CSDN博客 简介 本文介绍Nacos的命名空间、分组、ID的概念及用法。 Nacos通过命名空间&#xff08;Namespace&#xff09;分组&#xff08;Group&#xff09;应用&#xff08;Data ID或Name&#…

在Maix duino开发板上实现LED闪烁

文章目录简单介绍编程实现效果展示后简单介绍 如果你还不知道如何点亮LED&#xff0c;请看&#xff1a;点亮LED 今天开始上手在开发板上运行程序了&#xff0c;学习点亮LED灯就像是学习编程语言的Hello,worldHello, worldHello,world。学会电亮一盏LED灯之后&#xff0c;我便…

深度学习中计算量和参数量介绍、实现代码、例子

计算量 参数量 模型内存前言1 计算量和参数量2 统计计算量、参数量和模型内存3 源码分享3.1 thop实现3.2 ptflops实现pytorch_model_summary实现各层参数量统计4 总结前言 理清FLOPS和FLOPs&#xff0c;大写S代表的是显卡的运算性能&#xff0c;小写s代表的是模型的运算次数&a…