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
类和对应的切面方法即可生效,下面一起从源码的角度分析下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生成处理器
,进到该方法下:
这里实际触发的 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;
}
就是通过 BeanDefinitionRegistry
向 Spring IOC
容器中注入了 AnnotationAwareAspectJAutoProxyCreator
类。
三、AnnotationAwareAspectJAutoProxyCreator 做了什么
上一步已经得出 @EnableAspectJAutoProxy
,就是向 Spring IOC
中注入了 AnnotationAwareAspectJAutoProxyCreator
类,这里看下 AnnotationAwareAspectJAutoProxyCreator
的继承关系:
从继承树中可以看出 AnnotationAwareAspectJAutoProxyCreator
通过 BeanPostProcessor
获取 bean
初始化前后通知能力,从 SmartInstantiationAwareBeanPostProcessor
获得循环依赖时早期实例曝光能力,通过 ProxyProcessorSupport
获得代理能力。
这里关注下 BeanPostProcessor
扩展接口,在 BeanPostProcessor
中有 postProcessBeforeInitialization
和 postProcessAfterInitialization
两个重要的前后通知方法,而这两个方法和 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
实例化过程中 AbstractAutowireCapableBeanFactory
类 initializeBean
方法中会被触发,如果通知方法返回的 bean
不为空就替换掉原先的 bean
,因此 BeanPostProcessor
有控制 bean
生成实例的能力,这也是为什么主要对他分析:
由于 AnnotationAwareAspectJAutoProxyCreator
类及其父类并没有对 postProcessBeforeInitialization
方法进行重写,而 postProcessAfterInitialization
方法在父类 AbstractAutoProxyCreator
类下有重写,因此这里可以猜测在postProcessAfterInitialization
方法中生成了代理对象,可以 debug
下AbstractAutowireCapableBeanFactory
类的 initializeBean
方法验证一下:
从 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
方法,逻辑如下:
这里会使用 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
注解,逻辑如下:
如果存在就存入 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
数组,该数组的内容如下:
其实就是挨个判断是否是 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
对象的构造方法中初始化可那些内容:
主要将切点表达式,以及 argNames
参数进行了保存,这里可以看下 debug
的内容:
就是 Aspect
类注解中配置的内容,再回到前面 getPointcut
方法中,第一步获取到注解信息后,如果存在则创建了一个 AspectJExpressionPointcut
对象,并将解析出来的 AspectJAnnotation
对象添加进去,同时也将Spring
工厂也添加进去,最后返回出去。
再回到 getAdvisor
方法中,获取到 AspectJExpressionPointcut
对象后,如果不为空就新建一个 InstantiationModelAwarePointcutAdvisorImpl
实例,该实例是 Advisor
的子类。
这里简单介绍下 Advisor
,它通常与Pointcut
和 Advice
组合使用,Pointcut
用于定义切入点,即确定需要被增强的代码位置。而 Advice
则是实际的增强逻辑,它可以在目标代码执行前、执行后或出现异常时进行增强。Advisor
则是将 Pointcut
和 Advice
进行组合的组件。
这里看到 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
增强包装类,AbstractAspectJAdvice
是 Advice
的一个子类,这里以 @Before
类型为例,可以看下 AspectJMethodBeforeAdvice
类的实现:
上一步生成对应的 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
,这里也主要看下这种类型下的匹配过程,看下该类的继承关系:
属于 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
方法中。