SpringAOP源码解析之advice构建排序(二)

上一章我们知道Spring开启AOP之后会注册AnnotationAwareAspectJAutoProxyCreator类的定义信息,所以在属性注入之后initializeBean的applyBeanPostProcessorsAfterInitialization方法执行的时候调用AnnotationAwareAspectJAutoProxyCreator父类(AbstractAutoProxyCreator)的postProcessAfterInitialization方法来创建AOP的代理。

java 复制代码
// AbstractAutoProxyCreator类
@Override
	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				// 真正进行处理的地方,里面有代码很明显是用来创建代理对象的
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

然后我们创建一个Aspect,方便我们这章的分析

java 复制代码
@Component
@Aspect
public class ThamNotVeryUsefulAspect {
	@Pointcut("execution(* com.qhyu.cloud.aop.service.QhyuAspectService.*(..))") // the pointcut expression
	private void thamAnyOldTransfer() {} // the pointcut signature

	@Before("thamAnyOldTransfer()")
	public void before(){
		System.out.println("tham Before 方法调用前");
	}

	@After("thamAnyOldTransfer()")
	public void after(){
		System.out.println("tham After 方法调用前");
	}
	
	@AfterReturning("thamAnyOldTransfer()")
	public void afterReturning(){
		System.out.println("tham afterReturning");
	}
	
	@AfterThrowing("thamAnyOldTransfer()")
	public void afterThrowing(){
		System.out.println("tham AfterThrowing");
	}
	
	@Around("thamAnyOldTransfer()")
	public Object  around(ProceedingJoinPoint pjp) throws Throwable{
		// start stopwatch
		System.out.println("tham around before");
		Object retVal = pjp.proceed();
		// stop stopwatch
		System.out.println("tham around after");
		return retVal;
	}
}

放一张整体的流程图,方便我们查看知通构建的整体流程。

wrapIfNecessary方法的实现流程

1、首先判断bean是否需要被代理,如果不需要,直接返回原始bean实例 。

2、如果需要代理,则获取bean所有的advisor,并根据advisor的pointcout对bean进行匹配,得到所有需要拦截的方法 。

3、根据bean的类型和配置信息,决定使用哪种类型的代理对象,CGLIB或者JDK动态代理 。

4、将advisor和代理对象绑定,并将代理对象返回。

java 复制代码
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		// targetSource是干嘛得
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		// advisedBeans不会进行代理
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		// 什么情况会shouldSkip,提前解析切面
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// Create proxy if we have advice.
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			// 创建代理对象
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

findCandidateAdvisors获取候选的Advisors

获取候选的Advisors是使用的子类(AnnotationAwareAspectJAutoProxuCreator)的实现,然后我们的基于注解的Aspect(ThamNotVeryUsefulAspect)会执行this.aspectJAdvisorsBuilder.buildAspectJAdvisors()来添加

java 复制代码
@Override
	protected List<Advisor> findCandidateAdvisors() {
		// Add all the Spring advisors found according to superclass rules.
		List<Advisor> advisors = super.findCandidateAdvisors();
		// Build Advisors for all AspectJ aspects in the bean factory.
		if (this.aspectJAdvisorsBuilder != null) {
			advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}

构建AspectJ的Advisors

首先在BeanFactoryAspectJAdvisorsBuilder的this.advisorFactory.getAdvisors(factory)打个断点

然后我断点进来之后我就发现了这个其实是有顺序的,也就是说明在这个内部实现了第一次排序。

第一次排序

接下来会进入ReflectiveAspectJAdvisorFactory的getAdvisors方法。其中核心就是for循环中的getAdvisorMethods方法。

java 复制代码
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<>();
		//遍历切面方法,这里会把标注了@Pointcut 注解的排除掉,只剩下通知注解,如@Before,@After
		for (Method method : getAdvisorMethods(aspectClass)) {
			Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, 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;
	}
	// 遍历切面方法时进行排序,可以理解为第一次排序
	private List<Method> getAdvisorMethods(Class<?> aspectClass) {
		List<Method> methods = new ArrayList<>();
		ReflectionUtils.doWithMethods(aspectClass, methods::add, adviceMethodFilter);
		if (methods.size() > 1) {
			methods.sort(adviceMethodComparator);
		}
		return methods;
	}

首先会排除@PointCut的方法

java 复制代码
private static final MethodFilter adviceMethodFilter = ReflectionUtils.USER_DECLARED_METHODS
			.and(method -> (AnnotationUtils.getAnnotation(method, Pointcut.class) == null));

然后创建了一个比较器

java 复制代码
private static final Comparator<Method> adviceMethodComparator;

	static {
		// Note: although @After is ordered before @AfterReturning and @AfterThrowing,
		// an @After advice method will actually be invoked after @AfterReturning and
		// @AfterThrowing methods due to the fact that AspectJAfterAdvice.invoke(MethodInvocation)
		// invokes proceed() in a `try` block and only invokes the @After advice method
		// in a corresponding `finally` block.
		Comparator<Method> adviceKindComparator = new ConvertingComparator<>(
				new InstanceComparator<>(
						Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
				(Converter<Method, Annotation>) method -> {
					AspectJAnnotation<?> ann = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
					return (ann != null ? ann.getAnnotation() : null);
				});
		Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
		adviceMethodComparator = adviceKindComparator.thenComparing(methodNameComparator);
	}

很明显是按照Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class的顺序进行顺序的构建。

第二次排序

根据AbstractAdvisorAutoProxyCreator的findEligibleAdvisors的代码可知,第一次排序发生在findCandidateAdvisors方法。第二次排序则发生在次方法中的sortAdvisors。

java 复制代码
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
	
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
		// Around before after afterReturing afterThrowing
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			// 这里是通过order进行排序的
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}

第一次排序是对一个Aspect中的所有advice进行排序,第二次排序是对Aspect进行排序,可以通过实现Order接口或者@Order注解来设置顺序,如果没有实现order的话,是以加载的顺序来的。一般情况下加载的顺序可能不可控,所以如果有必要的话需要实现order。

创建两个Aspect,然后都没有实现@Order

顺序是ThamNotVersyUsefulAspect在NotVeryUsefulAspect之前

但是如果我们把两个放一起,这个时候NotVeryUsefulAspect先加载就会在前面。

如果我们设置ThamNotVeryUsefulAspect的Order(98),NotVeryUsefulAspect的Order(99),Order小的将排在前面。

java 复制代码
@Component
@Aspect
@Order(99)
public class NotVeryUsefulAspect {
}

@Component
@Aspect
@Order(98)
public class ThamNotVeryUsefulAspect {
}

写在最后

本章主要描述构建通知的顺序,正在的执行过程将在下一章节进行分析。

相关推荐
qq_441996059 分钟前
Mybatis官方生成器使用示例
java·mybatis
巨大八爪鱼15 分钟前
XP系统下用mod_jk 1.2.40整合apache2.2.16和tomcat 6.0.29,让apache可以同时访问php和jsp页面
java·tomcat·apache·mod_jk
码上一元2 小时前
SpringBoot自动装配原理解析
java·spring boot·后端
计算机-秋大田2 小时前
基于微信小程序的养老院管理系统的设计与实现,LW+源码+讲解
java·spring boot·微信小程序·小程序·vue
魔道不误砍柴功4 小时前
简单叙述 Spring Boot 启动过程
java·数据库·spring boot
失落的香蕉4 小时前
C语言串讲-2之指针和结构体
java·c语言·开发语言
枫叶_v4 小时前
【SpringBoot】22 Txt、Csv文件的读取和写入
java·spring boot·后端
wclass-zhengge4 小时前
SpringCloud篇(配置中心 - Nacos)
java·spring·spring cloud
路在脚下@4 小时前
Springboot 的Servlet Web 应用、响应式 Web 应用(Reactive)以及非 Web 应用(None)的特点和适用场景
java·spring boot·servlet
黑马师兄4 小时前
SpringBoot
java·spring