> 文章列表 > SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程

一、SpringAop Aspect 切面方法的查找匹配过程

SpringAop 是在项目中经常需要使用的框架,可以用来实现无侵入的逻辑增强。在使用 Aop 时,只需定义一个 Aspect 类,并加上相应的注解Spring 内部已经帮我们封装好了代理过程,我们只需将精力放在对应的通知方法中即可。

比如有下面 bean 类:

@Component
public class TestAop {public void test(){System.out.println("test...");}
}

然后再定义一个 Aspect 类:

@Aspect
@Component
@EnableAspectJAutoProxy(exposeProxy = true)
public class GlobAop {/* 定义切入点*/@Pointcut("execution(public * com.demo.test.*.*(..)))")public void BrokerAspect(){}@Before("BrokerAspect()")public void doBeforeGame(JoinPoint jp){String name = jp.getSignature().getName();System.out.println(name+"方法开始执行!");}@After("BrokerAspect()")public void doAfterGame(JoinPoint jp){String name = jp.getSignature().getName();System.out.println(name+"方法执行结束!");}@AfterReturning(value = "BrokerAspect()",returning = "result")public void doAfterReturningGame(JoinPoint jp, Object result){String name = jp.getSignature().getName();System.out.println(name+"方法返回 = " + result);}@AfterThrowing(value = "BrokerAspect()",throwing = "e")public void doAfterThrowingGame(JoinPoint jp, Exception e){String name = jp.getSignature().getName();System.out.println(name+"方法异常通知:"+e.toString());}@Around("BrokerAspect()")public Object around(ProceedingJoinPoint pjp) throws Throwable {return pjp.proceed();}
}

使用时也只关心需要的 bean:

public class App {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext("com.demo.test");TestAop testAop = context.getBean("testAop", TestAop.class);testAop.test();}}

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程

从这个案例中可以看出 SpringAop 使用起来完全无侵入,只需定好 Aspect 类和对应的切面方法即可生效,下面一起从源码的角度分析下Aspect 切面方法的查找和匹配过程。

在源码分析的过程涉及到 Spring bean 加载的过程 和 BeanPostProcessor 扩展接口,两个知识点,不了解的可以参考下面两篇博客:

Spring 源码解析 - Bean创建过程 以及 解决循环依赖

Spring 源码解析 - BeanPostProcessor 扩展接口

二、@EnableAspectJAutoProxy 做了什么

在使用 Aop 前,需要使用 @EnableAspectJAutoProxy 注解开启 Aop 的支持,这里从该注解着手开始分析,先看下该注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 开启 Aspectj自动代理
// 在springboot中,默认是会配置这个注解,并且默认用的是 cglib的代理,与之相对的是,spring默认用的是 jdk接口代理
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {/* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed* to standard Java interface-based proxies. The default is {@code false}.*///是否使用CGLIB代理和@AspectJ自动代理支持,该属性为true时,代表需要支持boolean proxyTargetClass() default false;/* Indicate that the proxy should be exposed by the AOP framework as a {@code ThreadLocal}* for retrieval via the {@link org.springframework.aop.framework.AopContext} class.* Off by default, i.e. no guarantees that {@code AopContext} access will work.* @since 4.3.1*///是否对切面进行曝光boolean exposeProxy() default false;}

该注解使用 @Import 引入了 AspectJAutoProxyRegistrar 类,这个类其实是个动态代理的 bean生成处理器,看到该类下:

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {/* Register, escalate, and configure the AspectJ auto proxy creator based on the value* of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing* {@code @Configuration} class.*/@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 这个方法在工厂中,注册了一个 AspectJ注解的自动代理生成器AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);AnnotationAttributes enableAspectJAutoProxy =AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);// 这里类似于工厂后置方法,根据注解,修改bean定义if (enableAspectJAutoProxy != null) {if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}}}

由于该类实现了 ImportBeanDefinitionRegistrar 接口,因此 Spring 启动时会触发 registerBeanDefinitions 方法,在该方法中会触发了 AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary 方法,其实是在注册动态代理的 bean生成处理器,进到该方法下:

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程

这里实际触发的 registerOrEscalateApcAsRequired 方法,并传入了 AnnotationAwareAspectJAutoProxyCreator.class,这个就是动态代理的 bean生成处理器,看到该方法下注册的过程:

private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {//断言判断注册容器是否为空Assert.notNull(registry, "BeanDefinitionRegistry must not be null");//如果AUTO_PROXY_CREATOR_BEAN_NAME已经注册过了if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {//取出已经注册过的AUTO_PROXY_CREATOR_BEAN_NAMEBeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);//判断是不是同一个Class对象if (!cls.getName().equals(apcDefinition.getBeanClassName())) {//如果不是同一个对象,那就需要根据优先级来判断到底需要使用哪一个了//获取当前AOP自动代理创建器的优先级int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());//获取指定的AOP自动代理创建器的优先级int requiredPriority = findPriorityForClass(cls);//进行优先级比较if (currentPriority < requiredPriority) {//如果指定的大于现在的,apcDefinition改变AOP自动代理创建器的class类型//相当于进行升级动作//由此可见,此时AOP自动代理创建器还没有实例出来,//这里只是将Aop自动代理创建其的BeanDefinition的className进行修改apcDefinition.setBeanClassName(cls.getName());}}//如果是同一个Class对象,证明是同一个处理器//返回,不需要进行注册return null;}//如果还没进行注册//使用Class去创建RootBeanDefinition,这里仅仅只是注入Class//从上面代码中可以看到,是可以进行更改的RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);//封装一些信息beanDefinition.setSource(source);beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);//注册进容器中,并且以AUTO_PROXY_CREATOR_BEAN_NAME为keyregistry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);return beanDefinition;
}

就是通过 BeanDefinitionRegistrySpring IOC 容器中注入了 AnnotationAwareAspectJAutoProxyCreator 类。

三、AnnotationAwareAspectJAutoProxyCreator 做了什么

上一步已经得出 @EnableAspectJAutoProxy,就是向 Spring IOC 中注入了 AnnotationAwareAspectJAutoProxyCreator 类,这里看下 AnnotationAwareAspectJAutoProxyCreator 的继承关系:

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程

从继承树中可以看出 AnnotationAwareAspectJAutoProxyCreator 通过 BeanPostProcessor 获取 bean 初始化前后通知能力,从 SmartInstantiationAwareBeanPostProcessor 获得循环依赖时早期实例曝光能力,通过 ProxyProcessorSupport 获得代理能力。

这里关注下 BeanPostProcessor 扩展接口,在 BeanPostProcessor 中有 postProcessBeforeInitializationpostProcessAfterInitialization 两个重要的前后通知方法,而这两个方法和 Spring bean 初始化过程紧密相关,方法如下:

public interface BeanPostProcessor {/*  实例化及依赖注入完成后、bean 初始化方法触发之前执行*/@Nullabledefault Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}/*  bean 初始化方法触发后执行*/@Nullabledefault Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}}

如果还不了解 BeanPostProcessor 这里也简单提一下, BeanPostProcessor 的前后两个通知方法在 Spring 实例化过程中 AbstractAutowireCapableBeanFactoryinitializeBean 方法中会被触发,如果通知方法返回的 bean 不为空就替换掉原先的 bean ,因此 BeanPostProcessor 有控制 bean 生成实例的能力,这也是为什么主要对他分析:

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程

由于 AnnotationAwareAspectJAutoProxyCreator 类及其父类并没有对 postProcessBeforeInitialization 方法进行重写,而 postProcessAfterInitialization 方法在父类 AbstractAutoProxyCreator 类下有重写,因此这里可以猜测在postProcessAfterInitialization 方法中生成了代理对象,可以 debugAbstractAutowireCapableBeanFactory 类的 initializeBean 方法验证一下:

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程

debug 的过程可以看出在 postProcessAfterInitialization 方法确实创建了一个代理对象,并且最后使用的也是代理对象,下面就主要分析下 AbstractAutoProxyCreator 类下的 postProcessAfterInitialization 方法。

进入到 AbstractAutoProxyCreator 类的 postProcessAfterInitialization 方法中:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {//某个bean 已经实例化后if (bean != null) {//获取缓存的键名,这里的形式是beanClassName_beanNameObject cacheKey = getCacheKey(bean.getClass(), beanName);//判断是否已经提前曝光代理过if (this.earlyProxyReferences.remove(cacheKey) != bean) {//如果没被代理过,执行wrapIfNecessartreturn wrapIfNecessary(bean, beanName, cacheKey);}}//如果bean为Null,直接返回,如果不为null,但已经被曝光出来,直接返回bean//解决了循环依赖的问题return bean;
}

这里判断了 earlyProxyReferences 是否有缓存过该 bean ,这样做的目的是在循环依赖的情况下是否已经提前曝光代理对象了,看过Spring 源码的应该知道,三级缓存中存储的是一个 ObjectFactory,通过 getObject 获取早期实例,在Spring 放入三级缓存时,实际getObject 方法会触发 AbstractAutowireCapableBeanFactory 类的 getEarlyBeanReference 方法,逻辑如下:

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程

这里会使用 SmartInstantiationAwareBeanPostProcessor 类型的通知器的 getEarlyBeanReference 方法获取一个早期实例,而AnnotationAwareAspectJAutoProxyCreator 也实现了 SmartInstantiationAwareBeanPostProcessor 因此会调用 getEarlyBeanReference 方法尝试获取一个早期对象,逻辑如下:

public Object getEarlyBeanReference(Object bean, String beanName) {Object cacheKey = getCacheKey(bean.getClass(), beanName);this.earlyProxyReferences.put(cacheKey, bean);return wrapIfNecessary(bean, beanName, cacheKey);
}

这里先对 earlyProxyReferences 容器中加入了缓存,然后调用 wrapIfNecessary 方法生成代理对象,同样在上面的 postProcessAfterInitialization 方法中,如果 earlyProxyReferences 中不存在或者存在的和当前不是一个实例也会执行 wrapIfNecessary 方法获取代理对象。

下面就来分析 wrapIfNecessary 方法,进到该方法下:

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {//判断是不是已经增强过,根据 targetSourcedBeans 容器里面已经包含了这个 beanNameif (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {//如果已经增强过,直接返回return bean;}//判断是否需要增强if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {//如果不需要增强,返回结果return bean;}//判断是否为基础设施类,如果是基础设施类并且需要跳过if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {//在 advusedBeans 容器增加标识,后面再出现直接中断this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// Create proxy if we have advice.// 根据beanName拿到需要增强的方法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));//保存代理的Class类型this.proxyTypes.put(cacheKey, proxy.getClass());//返回代理实例return proxy;}//如果不存在增强方法,则不需要进行代理//写入标识,后面再出现直接中断this.advisedBeans.put(cacheKey, Boolean.FALSE);// 返回当前实例return bean;
}

这里主要通过 getAdvicesAndAdvisorsForBean 方法,根据 beanName 获取到所有匹配的增强方法,也就是Aspect 中的切面方法,本篇文章重点分析的逻辑就在这里。

getAdvicesAndAdvisorsForBean 方法是个抽象方法,具体实现在 AbstractAdvisorAutoProxyCreator 类下,看该方法下:

protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {//获取增强方法List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);//判断是否为空if (advisors.isEmpty()) {//如果为空,返回一个空数组return DO_NOT_PROXY;}//如果不为空,转化为数组然后返回return advisors.toArray();
}

这里又调用 findEligibleAdvisors 方法获取当前 bean 匹配的增强方法,如果是空的话,则返回一个固定的空数组,在 wrapIfNecessary 方法中如果是这个固定的空数组,则标记直接返回,后续该类也直接返回不做处理。

下面进到 findEligibleAdvisors 方法中:

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {//获取所有的增强方法List<Advisor> candidateAdvisors = findCandidateAdvisors();//从所有的增强方法中匹配适合该 bean 的增强方法List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);//对匹配后的增强方法进行扩展extendAdvisors(eligibleAdvisors);//判断是否为空if (!eligibleAdvisors.isEmpty()) {//如果不为空,进行排序eligibleAdvisors = sortAdvisors(eligibleAdvisors);}//返回处理后的增强方法return eligibleAdvisors;
}

这里使用 findAdvisorsThatCanApply 查找全部的增强方法,使用findAdvisorsThatCanApply 匹配出适合当前类的增强方法,这两个方法也是本篇的核心,下面挨个来分析下。

三、查找 Aspect 切面方法

findCandidateAdvisors 方法主要看 AnnotationAwareAspectJAutoProxyCreator 类下的重载:

protected List<Advisor> findCandidateAdvisors() {// Add all the Spring advisors found according to superclass rules.// 调用父类的查找方法,父类是对配置文件中的信息进行解析,在Spring2.0之前仅支持配置文件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;
}

这里也会调用父类 AbstractAdvisorAutoProxyCreator 下的 findCandidateAdvisors 方法,不过父类中的方法是对配置文件进行解析处理的,而下面的 aspectJAdvisorsBuilder.buildAspectJAdvisors 则是对注解形式处理的,由于现在基本都使用注解形式,所以这里直接看注解形式的处理,进到 BeanFactoryAspectJAdvisorsBuilder 类下的buildAspectJAdvisors 方法中:

public List<Advisor> buildAspectJAdvisors() {//获取缓存中包含@Aspect注解的 BeanName,取之前缓存的数据List<String> aspectNames = this.aspectBeanNames;//如果缓存中不存在,则是第一次初始化if (aspectNames == null) {//加锁synchronized (this) {// 在获取一次缓存中的数据aspectNames = this.aspectBeanNames;// 判断是否还为空if (aspectNames == null) {//创建一个集合,存储解析后的 AdvisorList<Advisor> advisors = new ArrayList<>();// 给 aspectNames 赋初始值aspectNames = new ArrayList<>();//获取所有的 beanNameString[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);// 遍历所有的 beanNamefor (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.// 检查bean的类型是否为空了Class<?> beanType = this.beanFactory.getType(beanName, false);// 如果bean的类型为空直接跳过了if (beanType == null) {continue;}//判断类上有无 @Aspect 注解if (this.advisorFactory.isAspect(beanType)) {//将 beanName 写入 aspectNames 集合中aspectNames.add(beanName);// 创建一个AspectMetadata,主要存储切面元数据,构造方法注入了 beanName 与 beanTypeAspectMetadata amd = new AspectMetadata(beanType, beanName);//判断切面的实例化模式if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {//如果是SINGLETON模式的话//创建切面元数据的实例化工厂MetadataAwareAspectInstanceFactory factory =new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);//进行解析,取出里面的增强方法List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);//判断当前的beanName是不是单例的if (this.beanFactory.isSingleton(beanName)) {//如果是单例的,写入缓存,缓存的就是解析出来的增强方法//key 为beanName,value 为classAdvisorsthis.advisorsCache.put(beanName, classAdvisors);}else {// 不是单例// 缓存到切面工厂里// key 为beanName,value为 factorythis.aspectFactoryCache.put(beanName, factory);}//将增强方法添加到 advisors 集合中advisors.addAll(classAdvisors);}//如果实例化代理不是SINGLETON方式else {// Per target or per this.// 判断当前beanName是否为单例模式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 集合中advisors.addAll(this.advisorFactory.getAdvisors(factory));}}}// 将带有 @Aspect 注解的 beanName 记到 aspectBeanNames 中this.aspectBeanNames = aspectNames;//返回解析出来的增强方法return advisors;}}}// aspectBeanNames 不为空,代表已经进行初始化过了,要考虑从缓存中取了//判断aspectNames是否为空if (aspectNames.isEmpty()) {//如果为空的话,代表没有切面,直接返回一个空集合return Collections.emptyList();}//如果aspectNames不为空//那么证明已经初始化过了,先考虑从缓存中取List<Advisor> advisors = new ArrayList<>();//遍历aspectNames里面所有的切面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;
}

这个方法内容比较长,主要分了两个处理方向,第一个是缓存中不存在的情况,需要进行初始化,第二个是缓存中存在直接取数据使用,这里看到缓存中不存在的情况下:

首先对当前对象进行加锁,防止并发情况下 aspectBeanNames 出现线程安全问题。aspectBeanNames主要缓存包含@Aspect 注解的 BeanName

然后从 Spring IOC 工厂中获取所有的 beanName 信息,并通过工厂拿到相应的 Class 类型,通过 advisorFactory.isAspect 方法判断是否为 Aspect 类,其实就是判断类上是否带有 @Aspect 注解,逻辑如下:

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程

如果存在就存入 aspectNames 缓存中,再向下主要看到 this.advisorFactory.getAdvisors 方法,是一个抽象方法,具体逻辑由子类实现,其作用是用来对每个 Class 进行解析,取出里面的增强方法也就是切面方法,并封装成 Advisor 形式,这里看到 ReflectiveAspectJAdvisorFactory 类下的 getAdvisors 方法中:

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {//获取 Class 类型Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();//获取 beanNameString 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<>();// 反射获取aspectClass中的方法// 遍历获取到的方法for (Method method : getAdvisorMethods(aspectClass)) {// Prior to Spring Framework 5.2.7, advisors.size() was supplied as the declarationOrderInAspect// to getAdvisor(...) to represent the "current position" in the declared methods list.// However, since Java 7 the "current position" is not valid since the JDK no longer// returns declared methods in the order in which they are declared in the source code.// Thus, we now hard code the declarationOrderInAspect to 0 for all advice methods// discovered via reflection in order to support reliable advice ordering across JVM launches.// Specifically, a value of 0 aligns with the default value used in// AspectJPrecedenceComparator.getAspectDeclarationOrder(Advisor).// 检验这个方法是不是增强方法,如果是则将方法转化成AdvisorAdvisor 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结果集的第 0 个位置advisors.add(0, instantiationAdvisor);}// Find introduction fields.//获取aspectClass的所有被 @DeclaredParents 修饰的字段for (Field field : aspectClass.getDeclaredFields()) {//同样进行转化成Advisor对象,然后过滤掉一些不符合条件的字段Advisor advisor = getDeclareParentsAdvisor(field);//如果不为null,添加进结果集里面if (advisor != null) {advisors.add(advisor);}}//最终返回结果集return advisors;
}

首先获取到前面封装的 Class 类型和 beanName,下面使用反射获取到 Class 中的 Method,然后对获取到的 Method 使用 getAdvisor 方法检验是不是增强方法也就是切面方法,如果是话则包装成 Advisor 类型,下面看到 getAdvisor 方法中:

public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,int declarationOrderInAspect, String aspectName) {//对标识的类进行校验validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());//获取切点表达式,如果具有切点表达式,才算合法的 通知器,否则不合法AspectJExpressionPointcut expressionPointcut = getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());//判断切入点是否为空if (expressionPointcut == null) {//如果没有切入点,直接返回nullreturn null;}//如果有切入点,就根据切点信息生成增强器return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

这里再次对标识的类进行校验,比如看类上是否还带有@Aspect注解等,下面则调用 getPointcut 方法获取到 一个 AspectJExpressionPointcut对象,其作用主要是判断 Method 是否是切面方法,如果是则封装成 AspectJExpressionPointcut 对象,AspectJExpressionPointcut 从命名就可以看出和 Expression 有关 ,主要用来对切点表达式的解析和匹配,下面在匹配切面方法时会使用到该类。

下面看到 getPointcut 方法:

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {//寻找方法上有无对应的注解AspectJAnnotation<?> aspectJAnnotation =AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);//判断有没有匹配的注解if (aspectJAnnotation == null) {//没有匹配的注解直接返回Null,return null;}//使用 AspectJExpressionPointCut 来封装数据AspectJExpressionPointcut ajexp =new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);// 注入切入点的表达式,也就是规则拦截的表达式ajexp.setExpression(aspectJAnnotation.getPointcutExpression());if (this.beanFactory != null) {//注入beanFactoryajexp.setBeanFactory(this.beanFactory);}return ajexp;
}

第一步通过 AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod 寻找有无对应的注解,进到该方法中:

protected static AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {//遍历要匹配的注解,分别会处理: @Pointcut, @Around, @Before, @After, @AfterReturning, @AfterThrowing注解for (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) {// 查找是否有当前注解AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz);//如果有就直接返回,可以看到如果给方法加上了多个注解,会根据 ASPECTJ_ANNOTATION_CLASSES 中的顺序前面的生效if (foundAnnotation != null) {return foundAnnotation;}}return null;
}

遍历了 ASPECTJ_ANNOTATION_CLASSES 数组,该数组的内容如下:

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程

其实就是挨个判断是否是 Aspect 中的注解,如果有包装成 AspectJAnnotation 对象返回,这里看到 findAnnotation 方法中是如何判断的:

private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) {// 查找注解A result = AnnotationUtils.findAnnotation(method, toLookFor);if (result != null) {return new AspectJAnnotation<>(result);}else {return null;}
}

使用 AnnotationUtils.findAnnotation 工具找寻对应的注解,如果存在则新建一个 AspectJAnnotation 对象,再看下 AspectJAnnotation 对象的构造方法中初始化可那些内容:

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程

主要将切点表达式,以及 argNames 参数进行了保存,这里可以看下 debug 的内容:

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程
就是 Aspect 类注解中配置的内容,再回到前面 getPointcut 方法中,第一步获取到注解信息后,如果存在则创建了一个 AspectJExpressionPointcut 对象,并将解析出来的 AspectJAnnotation 对象添加进去,同时也将Spring 工厂也添加进去,最后返回出去。

再回到 getAdvisor 方法中,获取到 AspectJExpressionPointcut 对象后,如果不为空就新建一个 InstantiationModelAwarePointcutAdvisorImpl 实例,该实例是 Advisor 的子类。

这里简单介绍下 Advisor ,它通常与PointcutAdvice 组合使用,Pointcut 用于定义切入点,即确定需要被增强的代码位置。而 Advice 则是实际的增强逻辑,它可以在目标代码执行前、执行后或出现异常时进行增强。Advisor 则是将 PointcutAdvice 进行组合的组件。

这里看到 InstantiationModelAwarePointcutAdvisorImpl 的构造方法中:

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {// 当前的切点表达式this.declaredPointcut = declaredPointcut;// 切面的 Classthis.declaringClass = aspectJAdviceMethod.getDeclaringClass();// 切面方法的名称this.methodName = aspectJAdviceMethod.getName();// 切面方法的参数类型this.parameterTypes = aspectJAdviceMethod.getParameterTypes();// 切面方法this.aspectJAdviceMethod = aspectJAdviceMethod;// aspect 通知工厂this.aspectJAdvisorFactory = aspectJAdvisorFactory;// aspect 实例工厂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);}
}

可以看到包含了切点表达式以及切面类信息,再下面判断是否为懒加载情况,如果不是则使用 instantiateAdvice 进行初始化 Advice,前面提到 Advice 就是实际的增强逻辑,也就是切面方法,看到 instantiateAdvice 方法中:

private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {//调用 aspectAdvisorFactory 来获取增强器Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,this.aspectInstanceFactory, this.declarationOrder, this.aspectName);return (advice != null ? advice : EMPTY_ADVICE);
}

调用了 aspectJAdvisorFactory.getAdvice 方法来获得 Advice ,再看到该方法下:

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {//获取切面的classClass<?> 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 classif (!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 其实就是增强器的一个抽象模板AbstractAspectJAdvice springAdvice;//对注解的类型进行判断switch (aspectJAnnotation.getAnnotationType()) {//如果是@PointCut类型case AtPointcut:if (logger.isDebugEnabled()) {logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");}//@PointCut类型没有增强器,其代表的仅仅只是一个切入点return null;//如果是@Around类型case AtAround://返回一个AspectJAroundAdvicespringAdvice = new AspectJAroundAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;//如果是@Before类型case AtBefore://返回一个AspectJMethodBeforeAdvicespringAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;//如果是@After类型case AtAfter://返回一个AspectJAfterAdvicespringAdvice = new AspectJAfterAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);break;//如果是一个@AfterReturning注解case AtAfterReturning://返回一个AspectJAfterReturingAdvicespringAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);//获取@AfterReturing的属性AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();//判断是否为空,不为空就添加进springAdvice中//这里对应的属性是returningNameif (StringUtils.hasText(afterReturningAnnotation.returning())) {springAdvice.setReturningName(afterReturningAnnotation.returning());}break;//判断是不是@AfterThrowing注解case AtAfterThrowing://返回一个AspectJAfterThrowingAdvicespringAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);//获取@AfterThrowing注解上的属性AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();//判断是否为空,不为空就添加进springAdvice中//这里对应的属性是throwingName,if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {springAdvice.setThrowingName(afterThrowingAnnotation.throwing());}break;//如果不是上面的5种类型,抛错处理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;
}

这里对不同类型的 Aspect 注解进行判断,生成不同的 AbstractAspectJAdvice 增强包装类,AbstractAspectJAdviceAdvice 的一个子类,这里以 @Before类型为例,可以看下 AspectJMethodBeforeAdvice 类的实现:

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程

上一步生成对应的 AbstractAspectJAdvice 增强实例后,在回到前面的 getAdvisor 方法中也就是已经声明好了 InstantiationModelAwarePointcutAdvisorImpl Advisor 实例,再回到 getAdvisors 方法中,将获取到的 Advisor 实例放入了 advisors 集合中,最后返回给了前面的 buildAspectJAdvisors 方法中,接着判断如果是单例的话就将结果缓存下来,便于下次使用,最后在将结果返回给 findEligibleAdvisors 方法中。

findEligibleAdvisors 方法中,通过 findCandidateAdvisors 方法已经获取到了所有的增强方法,下面使用 findAdvisorsThatCanApply 进行匹配找出适合该 bean 的增强方法。

四、 匹配 Aspect 切面方法

进到 findAdvisorsThatCanApply 方法中:

protected List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {ProxyCreationContext.setCurrentProxiedBeanName(beanName);try {return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);}finally {ProxyCreationContext.setCurrentProxiedBeanName(null);}
}

第一步现将 beanName 记入ThreadLocal 中标记当前正在匹配的 beanName,然后触发 AopUtils.findAdvisorsThatCanApply 方法进行匹配,看到该方法下:

public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {if (candidateAdvisors.isEmpty()) {return candidateAdvisors;}// 创建一个合适的 Advisor 的集合 eligibleAdvisorsList<Advisor> eligibleAdvisors = new ArrayList<>();//循环所有的Advisorfor (Advisor candidate : candidateAdvisors) {// 判断切面是否匹配//如果Advisor是 IntroductionAdvisor 引介增强 可以为目标类 通过AOP的方式添加一些接口实现if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {eligibleAdvisors.add(candidate);}}//是否有引介增强boolean hasIntroductions = !eligibleAdvisors.isEmpty();for (Advisor candidate : candidateAdvisors) {//如果是IntroductionAdvisor类型的话 则直接跳过if (candidate instanceof IntroductionAdvisor) {// already processedcontinue;}// 判断切面是否匹配if (canApply(candidate, clazz, hasIntroductions)) {eligibleAdvisors.add(candidate);}}return eligibleAdvisors;
}

这里逻辑主要分了两个方向是否为 IntroductionAdvisor 引介增强器,引介增强可以为目标类通过AOP的方式添加一些接口实现,也就是使用 @DeclareParents 注解的情况下,前面我们分析的 Advisor 类型为InstantiationModelAwarePointcutAdvisorImpl ,这里也主要看下这种类型下的匹配过程,看下该类的继承关系:

SpringAop 源码解析 (一) - Aspect 切面方法的查找匹配过程
属于 PointcutAdvisor 类型的 Advisor ,因此会通过 canApply 方法判断是否匹配,继续看到 canApply 方法中:

public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {if (advisor instanceof IntroductionAdvisor) {// IntroductionAdvisor,根据类过滤器,进行匹配//如果是 IntroductionAdvisor 的话,则调用IntroductionAdvisor类型的实例进行类的过滤//这里是直接调用的ClassFilter的matches方法return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);}// 通常情况下 Advisor 都是 PointcutAdvisor 类型else if (advisor instanceof PointcutAdvisor) {PointcutAdvisor pca = (PointcutAdvisor) advisor;// 从Advisor中获取Pointcut的实现类 就是是AspectJExpressionPointcutreturn canApply(pca.getPointcut(), targetClass, hasIntroductions);}else {// It doesn't have a pointcut so we assume it applies.return true;}
}

这里又进行了类型判断,上面已经看到InstantiationModelAwarePointcutAdvisorImpl 属于 PointcutAdvisor 类型的 Advisor,因此再看到 canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) 方法中,注意 Pointcut 参数传递的 pca.getPointcut() 在声明 InstantiationModelAwarePointcutAdvisorImpl 下,其实就是 AspectJExpressionPointcut

public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {Assert.notNull(pc, "Pointcut must not be null");//进行切点表达式的匹配最重要的就是 ClassFilter 和 MethodMatcher这两个方法的实现。//首先进行ClassFilter的matches方法校验//首先这个类要在所匹配的规则下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;}// 如果当前 MethodMatcher 也是IntroductionAwareMethodMatcher类型,则转为该类型IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;if (methodMatcher instanceof IntroductionAwareMethodMatcher) {introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;}Set<Class<?>> classes = new LinkedHashSet<>();if (!Proxy.isProxyClass(targetClass)) {// 目标对象没有采用jdk动态代理,则要么是cglib代理,要么没有代理,获取到没有代理的原始类classes.add(ClassUtils.getUserClass(targetClass));}// 获取到目标类的所有的超类接口classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));for (Class<?> clazz : classes) {// 获取目标类即接口的方法,只要有一个方法满足切点条件,即视为切点可以匹配Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);// 只要有一个方法能匹配到就返回true//MethodMatcher 中有两个 matches 方法。// boolean matches(Method method, Class<?> targetClass) 用于静态的方法匹配// boolean matches(Method method, Class<?> targetClass, Object... args) 用于运行期动态的进行方法匹配for (Method method : methods) {// 如果 MethodMatcher 是IntroductionAwareMethodMatcher类型,则使用该类型的方法进行匹配// 否则使用 MethodMatcher.matches() 方法进行匹配if (introductionAwareMethodMatcher != null ?introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :methodMatcher.matches(method, targetClass)) {return true;}}}return false;
}

这里首先通过 pc.getClassFilter().matches 方法进行匹配,其中 getClassFilter() 方法就是 AspectJExpressionPointcut 类下的 getClassFilter() 方法:

public ClassFilter getClassFilter() {obtainPointcutExpression();return this;
}

最后返回的就是自己,也就是调用了 AspectJExpressionPointcut 类下的 matches(Class<?> targetClass) 进行判断的,这里先看下 obtainPointcutExpression() 方法中做了什么:

private PointcutExpression obtainPointcutExpression() {if (getExpression() == null) { //表达式不存在,直接抛出异常throw new IllegalStateException("Must set property 'expression' before attempting to match");}if (this.pointcutExpression == null) {// 确认类加载器this.pointcutClassLoader = determinePointcutClassLoader();// 创建切点表达式this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);}return this.pointcutExpression;
}

其实是生成了一个切点表达式解析器,注意这个切点表达式解析器不是Spring提供的,是采用的 aspectj 包提供的PointcutExpression ,关于 PointcutExpression 这里不做过多的介绍了,下面主要看到 obtainPointcutExpression() 是怎么创建切点表达式解析器的:

private PointcutExpression buildPointcutExpression(@Nullable ClassLoader classLoader) {// 初始化切点解析器PointcutParser parser = initializePointcutParser(classLoader);PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];for (int i = 0; i < pointcutParameters.length; i++) {pointcutParameters[i] = parser.createPointcutParameter(this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);}// 使用切点解析器进行解析表达式获取切点表达式return parser.parsePointcutExpression(replaceBooleanOperators(resolveExpression()),this.pointcutDeclarationScope, pointcutParameters);
}

第一步初始化了切点解析器,看到 initializePointcutParser 方法中,如何操作的:

private PointcutParser initializePointcutParser(@Nullable ClassLoader classLoader) {// 获得切点解析器PointcutParser parser = PointcutParser.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(SUPPORTED_PRIMITIVES, classLoader);parser.registerPointcutDesignatorHandler(new BeanPointcutDesignatorHandler());return parser;
}

通过 PointcutParser 获取了一个解析器,在回到上一步buildPointcutExpression方法中,这里将切点表达式填充到了切点解析器中生成 PointcutExpression

下面再看到 AspectJExpressionPointcut 下的 matches 方法:

public boolean matches(Class<?> targetClass) {PointcutExpression pointcutExpression = obtainPointcutExpression();try {try {// 使用切点表达式进行粗筛return pointcutExpression.couldMatchJoinPointsInType(targetClass);}catch (ReflectionWorldException ex) {logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);// Actually this is still a "maybe" - treat the pointcut as dynamic if we don't know enough yetPointcutExpression fallbackExpression = getFallbackPointcutExpression(targetClass);if (fallbackExpression != null) {return fallbackExpression.couldMatchJoinPointsInType(targetClass);}}}catch (Throwable ex) {logger.debug("PointcutExpression matching rejected target class", ex);}return false;
}

第一步就是拿到上面已经生成的 PointcutExpression 切点表达式,然后通过 couldMatchJoinPointsInType 判断 targetClass 是否符合。

下面回到前面的 findAdvisorsThatCanApply 方法中,如果 canApply 方法返回 true 也标识匹配成功,就将这个 Advisor 放入 eligibleAdvisors 集合中,最终返回给前面的 findEligibleAdvisors 方法中,在该方法中如果匹配的 Advisor 存在的话排序出先顺序,最终给到 wrapIfNecessary 方法中。