SpringAop source code 1: Notification method priority

I plan to divide the source code of AOP into four parts to learn

  1. Notification method priority source code
  2. Aspect execution order priority
  3. Determine which methods need to generate proxy objects + dynamic proxy object generation
  4. The execution of the notification method (interceptor interception)

In aop, as we all know, the priority of the notification method is: Around> Before> After> AfterReturning> AfterThrowing:
This note mainly records why the priority is like this, and what is the principle.

Notification method priority

org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory
static {
	/**
	 * 这里应该是定义aop通知的先后顺序
	 *
	 * 从前到后,优先级依次降低
	 */
	Comparator<Method> adviceKindComparator = new ConvertingComparator<>(
			new InstanceComparator<>(
					Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
			(Converter<Method, Annotation>) method -> {
				AspectJAnnotation<?> annotation =
					AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
				return (annotation != null ? annotation.getAnnotation() : null);
			});
	Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
	METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);
}

In ReflectiveAspectJAdvisorFactory, there is a static code block that defines the priority of the notification method, mainly declaring a comparator object.
In fact, this comparator can be seen where it is called. Search the entire class and you will find that it is in the org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisorMethods
method Is called in org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisorsthis method , and the getAdvisorMethods method is called in this method. Let’s stop here first, let’s take a look at the specific processing logic.

/**
 * 获取到切面的所有通知方法,并根据定义好的优先级,对通知方法进行排序;
 * 需要注意:这里是对一个切面中的通知方法先进行排序
 * 并将排好序之后的切面返回
 * @param aspectInstanceFactory the aspect instance factory
 * (not the aspect instance itself in order to avoid eager instantiation)
 * @return
 */
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
	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<>();
	/**
	 * 1、getAdvisorMethods()该方法获取到的所有advice通知方法已经是排好序的通知方法了,只需要按照优先级
	 * 往集合中add即可
	 *
	 * 这里获取到的所有方法是切面中除了@PointCut修饰的所有方法
	 */
	for (Method method : getAdvisorMethods(aspectClass)) {
		/**
		 */2、advisors.size():这个参数就是通知方法的优先级,这里用advisors的长度作为优先级,用的比较妙
		 * 因为这个for循环的方法,是排序之后的方法,所以依次从0开始,优先级最高,后面优先级逐渐变低
		 */
		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);
		if (advisor != null) {
			advisors.add(advisor);
		}
	}

	return advisors;
}

Let's not say where the above method is called. We only look at the corresponding processing logic.
1. Get all the notification methods according to the aspect and sort them.
2. According to the sorted notification methods, generate the corresponding ones in turn advisor object, and then return

/**
 * 这个方法,会获取到切面所有的通知方法,然后进行排序
 * @param aspectClass
 * @return
 */
private List<Method> getAdvisorMethods(Class<?> aspectClass) {
	final List<Method> methods = new ArrayList<>();
	ReflectionUtils.doWithMethods(aspectClass, method -> {
		// Exclude pointcuts
		/**
		 * 如果没有被pointCut注解修饰,就添加到methods中,最后统一根据优先级进行排序
		 * 这里之所以这么判断,和入参的class有关系,这里入参的class一定是一个切面
		 */
		if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
			methods.add(method);
		}
	});
	methods.sort(METHOD_COMPARATOR);
	return methods;
}

In the second point of the getAdvisors method, there is a very detailed point here. The aspect priority is determined according to the length of the advisors list array. Why can this be done? Because all notification methods are sorted according to priority, here we directly use the array length as the priority to initialize the Advisor object

When is called

Speaking of this, you need to know first, for aop, you need to use an annotation @EnableAspectJAutoProxy, in which a post processor is injectedAnnotationAwareAspectJAutoProxyCreator

In the post-processor org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation
method, first of all, this method is the first post-processor method to be called during the life cycle of the initialization bean. If the method returns a proxy object, it will not call subsequent Process, we need to know the timing of being called

In the post-processor method, 1. It will judge whether the bean needs to be proxied. 2. The analysis of the aspect
is the second step:

org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#shouldSkip
	org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
		org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors

In the buildAspectJAdvisors method, it is the key to analyze the aspect we declared

/**
 * Look for AspectJ-annotated aspect beans in the current bean factory,
 * and return to a list of Spring AOP Advisors representing them.
 * <p>Creates a Spring Advisor for each AspectJ advice method.
 * @return the list of {@link org.springframework.aop.Advisor} beans
 * @see #isEligibleBean
 * 这个方法很重要,这里和aop有关系
 * 在第一个后置处理器执行的时候,会调用到这里
 * 然后在这里找到所有的切面以及对应的通知方法
 *
 * aspectBeanNames = aspectNames:存放的是所有加了@Aspect注解的beanName
 * advisors:存放的是所有通知方法(多个切面的所有通知方法)
 * advisorsCache:这个map中,key是切面的beanName,value是一个list,存储的是当前切面对应的所有通知方法
 *
 */
public List<Advisor> buildAspectJAdvisors() {
	List<String> aspectNames = this.aspectBeanNames;

	/**
	 * 在第一次进入到这里的时候,这里的aspectBeanNames是null,
	 * 会执行下面的解析逻辑,解析之后,会将aspectBeanNames进行赋值
	 * 第二次再调用该方法的时候,就不会解析了,而是直接从缓存中获取,这里获取到的所有advice方法
	 * 是已经排好序的(按照通知方法优先级进行排序的)
	 */
	if (aspectNames == null) {
		synchronized (this) {
			aspectNames = this.aspectBeanNames;
			if (aspectNames == null) {
				List<Advisor> advisors = new ArrayList<>();
				aspectNames = new ArrayList<>();
				/**
				 * 2.这里会找到所有的对象,Object的实现类,其实就是所有的bean了
				 * 然后进行遍历
				 */
				String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
						this.beanFactory, Object.class, true, false);
				for (String beanName : 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.
					/**
					 * 2.1 根据beanName获取到对应的beanType
					 */
					Class<?> beanType = this.beanFactory.getType(beanName);
					if (beanType == null) {
						continue;
					}
					/**
					 * 2.2 判断bean是否添加了@Aspect注解,如果添加了,就表示当前bean
					 * 是一个切面,放入到一个变量中
					 */
					if (this.advisorFactory.isAspect(beanType)) {
						aspectNames.add(beanName);
						AspectMetadata amd = new AspectMetadata(beanType, beanName);
						if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
							MetadataAwareAspectInstanceFactory factory =
									new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
							/**
							 * 2.3 会尝试找到当前切面所有的通知方法,这里返回的通知方法,是已经排过序的,但是只是是
							 * 通知方法的排序,并没有对切面做排序,因为这里入参就是切面的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 model is not singleton");
							}
							MetadataAwareAspectInstanceFactory factory =
									new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
							this.aspectFactoryCache.put(beanName, factory);
							advisors.addAll(this.advisorFactory.getAdvisors(factory));
						}
					}
				}
				/**
				 * 3.将这个临时变量付给aspectBeanName
				 */
				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;
}

Here 2.3 is the point of calling all the notification methods in the search aspect.
For this method, the following operations are mainly completed:
1. Find all beans from the beanDefinitionMap
2. Then parse them in turn to determine whether @Aspect annotations are added.
3. Yes The bean with the aspect annotation is parsed. In the 2.3 part of the annotation, the getAdvisors method will be called to parse all the notification methods, and then the corresponding advisor object
will be generated 4. Then the parsed advisor object will be stored in the memory In the map of, the aspect will be stored in the list collection at the same time, private volatile List aspectBeanNames; In this way, when the method is called next time, there is no need to parse it again

in conclusion

Therefore, through the above analysis, the following conclusions can be obtained:
1. When each bean is initialized, the first post-processor method will be called to analyze all the aspects, and after the analysis is completed, it will be stored in memory
2. , When the method is called again next time, there is no need to parse it again, just get it directly from the memory.
3. When parsing the notification method, the priority of the aspect will be specified

Guess you like

Origin blog.csdn.net/CPLASF_/article/details/112301589