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 {
}

写在最后

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

相关推荐
kinlon.liu10 分钟前
零信任安全架构--持续验证
java·安全·安全架构·mfa·持续验证
哈喽,树先生16 分钟前
1.Seata 1.5.2 seata-server搭建
spring·springcloud
王哲晓31 分钟前
Linux通过yum安装Docker
java·linux·docker
java66666888835 分钟前
如何在Java中实现高效的对象映射:Dozer与MapStruct的比较与优化
java·开发语言
Violet永存36 分钟前
源码分析:LinkedList
java·开发语言
执键行天涯37 分钟前
【经验帖】JAVA中同方法,两次调用Mybatis,一次更新,一次查询,同一事务,第一次修改对第二次的可见性如何
java·数据库·mybatis
Adolf_19931 小时前
Flask-JWT-Extended登录验证, 不用自定义
后端·python·flask
Jarlen1 小时前
将本地离线Jar包上传到Maven远程私库上,供项目编译使用
java·maven·jar
蓑 羽1 小时前
力扣438 找到字符串中所有字母异位词 Java版本
java·算法·leetcode
叫我:松哥1 小时前
基于Python flask的医院管理学院,医生能够增加/删除/修改/删除病人的数据信息,有可视化分析
javascript·后端·python·mysql·信息可视化·flask·bootstrap