上文中解析了Aop中的匹配规则是怎样的,我定义一个Advisor,是如何可以切到我想要的方法或者Bean类从而去生成代理对象,对原生代码进行横向的逻辑插入
本文来解析Spring是如何支持Aop的,因为我们开发中常用的@Before,@After这些注解并不是Spring的东西,而是org.aspectj.lang.annotation包下的,那么Spring是如何引入并且支持这些注解的
我们通常在启动Spring项目的时候,会在启动类上加上@EnableAspectJAutoProxy注解来标明开启Aop功能,支持Aspectj模块
这个注解里面引入了一个AspectJAutoProxyRegistrar.class类,这个类实现了ImportBeanDefinitionRegistrar接口,(ImportBeanDefinitionRegistrar的注册逻辑可以参考Spring配置类源码解析(上)),在ImportBeanDefinition方法中注册了AnnotationAwareAspectJAutoProxyCreator类型的BeanDefinition,这个玩意继承了AbstractAutoProxyCreator,他的父类是一个BeanPostProcessor,在其初始化后的方法中(AbstractAutoProxyCreator.postProcessAfterInitialization),有着支持Aspectj的功能,源码如下
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 找到所有的Advisor,调用子类的方法AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()
//第一步找Spring中的Advisor
//第二步找切面类中和Aspectj有关的注解,转换成Advisor再加进去
List<Advisor> candidateAdvisors = findCandidateAdvisors();
// 进行筛选
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
extendAdvisors(eligibleAdvisors);
// 对Advisor进行排序,按Ordered接口、@Order注解进行排序
if (!eligibleAdvisors.isEmpty()) {
eligibleAdvisors = sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
第一步,先找到所有的Advisor(包括Spring中的Advisor,以及Aspectj定义的那些注解)
第二步,对这些Advisor进行筛选,根据我们上文讲的筛选规则(类和方法匹配)去筛选合适的Advisor
第三步,排序这些Advisor,每一个Advisor都有其实现的顺序,到底该在何时插入代理逻辑
下面是查找所有Advisor的逻辑(源码中的代码只取精简部分,额外逻辑可以自己去研究下)
1.找到Spring容器中现有的Advisor(BeanFactoryAdvisorRetrievalHelper.findAdvisorBeans())
public List<Advisor> findAdvisorBeans() {
//获取容器中所有的Advisor的names
String[] advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Advisor.class, true, false);
this.cachedAdvisorBeanNames = advisorNames;
List<Advisor> advisors = new ArrayList<>();
for (String name : advisorNames) {
advisors.add(this.beanFactory.getBean(name, Advisor.class));
}
return advisors;
}
2.找到切面类中定义的那些注解,并且将其转换成Advisor(BeanFactoryAspectJAdvisorsBuilder.buildAspectJAdvisors)
public List<Advisor> buildAspectJAdvisors() {
// aspectBeanNames是用来缓存BeanFactory中所存在的切面beanName的,第一次为null,后面就不为null了,不为null表示之前就已经找到过BeanFactory中的切面了
synchronized (this) {
List<Advisor> advisors = new ArrayList<>();
// 把所有beanNames拿出来遍历,判断某个bean的类型是否是Aspect
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
this.beanFactory, Object.class, true, false);
for (String beanName : beanNames) {
Class<?> beanType = this.beanFactory.getType(beanName, false);
//是否是切面。加了@Aspect注解的Bean
if (this.advisorFactory.isAspect(beanType)) {
// 利用BeanFactoryAspectInstanceFactory来解析Aspect类
MetadataAwareAspectInstanceFactory factory =
new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
//核心逻辑,获取切面Bean中的Advisor
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
// 并且会将该切面中所对应的Advisor对象进行缓存
this.advisorsCache.put(beanName, classAdvisors);
advisors.addAll(classAdvisors);
}
}
return advisors;
}
}
结合源码可以得知,想要获取切面中的Advisor,首先他会把Spring单例池中所有的BeanName全部拿出来,然后挨个判断是否是切面Bean,如果是,那么根据这个BeanName构造一个MetadataAwareAspectInstanceFactory ,然后调用getAdvisor方法去获取切面中的Advisor,下面是getAdvisor中的核心源码
//切面中是否含有@PointCut注解
for (Method method : getAdvisorMethods(aspectClass)) {
//获取Advisor
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
1.找到没有PointCut的方法
private List<Method> getAdvisorMethods(Class<?> aspectClass) {
List<Method> methods = new ArrayList<>();
// 拿到切面类中所没有加@Pointcut的方法(注意,是没有加Pointcut的所有方法)
//adviceMethodFilter:pointCut.class == null
//然后将这些方法加到methods这个list里面
ReflectionUtils.doWithMethods(aspectClass, methods::add, adviceMethodFilter);
// 对方法进行排序,按注解和方法名字进行排序
if (methods.size() > 1) {
//adviceMethodComparator:Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class
methods.sort(adviceMethodComparator);
}
return methods;
}
2.上面的那个methods中接着过滤只加了Aspectj的那些方法
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
//getPointcut里面会匹配加了Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class
//上面这些注解的方法才会被匹配出来,然后生成PointCut
// 拿到当前方法所对应的Pointcut对象,但是注意:如果当前方法上是这么写的@After("pointcut()"),那么此时得到的Pointcut并没有去解析pointcut()得到对应的表达式
AspectJExpressionPointcut expressionPointcut = getPointcut(
candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
// expressionPointcut是pointcut
// candidateAdviceMethod承载了advice
//将method和Advice合成一个Advisor
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
}
上面的代码总共分为两部分
1.getAdvisorMethods中先把没有加PointCut的注解的方法过滤出来,这时候里面就相当于把Aspectj中大部分的普通方法这些也都加进去了
2.getAdvisors中的getPointCut方法中,会将这些方法进行下一步的过滤,会将只加了Aspecj相关注解的那些方法过滤出来,然后将该方法转换成Advice,接着和PointCut拼凑成完整的Advisor,但是这里method还没有转换成Advice,在InstantiationModelAwarePointcutAdvisorImpl的构造方法中我们可以找到Aspectj注解转换成Advisor的逻辑,路径如下InstantiationModelAwarePointcutAdvisorImpl的构造方法->InstantiationModelAwarePointcutAdvisorImpl.instantiateAdvice->ReflectiveAspectJAdvisorFactory.getAdvice,我们截取部分源码即可看到,拿到这些Advisor之后,就会回到初始化后的方法中,然后根据他的class和method去匹配我需要的Advisor,得到这些Advisor之后(上文Spring-Aop源码解析(中)在方法执行的时候会留有疑惑,这些Advisor是怎么来的,本文这里就解答了),会调用ProxyFactory.getProxy去为正在生成的Bean生成一个代理对象,然后放到Spring的单例池中
case AtAround:
// @Around
springAdvice = new AspectJAroundAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
case AtBefore:
springAdvice = new AspectJMethodBeforeAdvice(
candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
break;
总结:
Spring支持开启Aop需要在启动类上加上EnableAspectJAutoProxy注解,这个注解里面注册了一个BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator),Spring中的所有Bean在生成的时候都会经过这里面的初始化后方法,会拿着得到的Advisor去匹配我正在生成的Bean是否需要代理,如果匹配到了至少一个Advisor,那么就会生成代理对象,然后注意,在方法执行的时候,也会去匹配一遍Advisor,再次对过滤得到的Advisor进行一遍过滤,就比如我在UserService类中的A方法加了@Transactional的注解,B方法没有加,那么我的UserService就会生成一个代理对象,我在执行B方法的时候不需要事务,所以执行B方法的时候不需要额外的代理逻辑,所以还得再匹配一遍,更多的场景读者可以自己思考。