本文主要分析了 InstantiationModelAwarePointcutAdvisorImpl 的getAdvice
方法,如何从 Advisor 中获取 Advice 对象的过程。
基于 Spring Framework v5.2.6.RELEASE
相关阅读:Spring 源码阅读 61:基于 JDK 动态代理的 AOP 代理回调方法 invoke 分析
接上一篇:Spring 源码阅读 64:基于 JDK 的 AOP 代理如何获取拦截器链(2)- Advisor 与目标方法的匹配
概述
上一篇介绍了如何为被调用的目标方法匹配 Advisor,匹配到的 Advisor 会通过 DefaultAdvisorAdapterRegistry 的getInterceptors
方法会去到对应的拦截器,并添加到最终的拦截器链中。本文来深入分析getInterceptors
方法。
获取 Advice
以下是定义在 DefaultAdvisorAdapterRegistry 类中的getInterceptors
方法源码。
// org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry#getInterceptorspublicMethodInterceptor[] getInterceptors(Advisoradvisor) throwsUnknownAdviceTypeException { List<MethodInterceptor>interceptors=newArrayList<>(3); Adviceadvice=advisor.getAdvice(); if (adviceinstanceofMethodInterceptor) { interceptors.add((MethodInterceptor) advice); } for (AdvisorAdapteradapter : this.adapters) { if (adapter.supportsAdvice(advice)) { interceptors.add(adapter.getInterceptor(advisor)); } } if (interceptors.isEmpty()) { thrownewUnknownAdviceTypeException(advisor.getAdvice()); } returninterceptors.toArray(newMethodInterceptor[0]); }
方法的开头声明了一个 MethodInterceptor 类型的列表interceptors
,它会在方法的结尾被转换为数组后作为结果返回。
然后,调用了advisor
的getAdvice
方法。这里需要留意一下,Advisor 是一个接口,getAdvice
方法是在 Advisor 接口中定义的方法,Advisor 的实现类并非都有advice属性。结合以上两点,所以这里的getAdvice
方法不能被看成一个普通的 getter 方法。
要找到getAdvice
方法的逻辑首先要知道这里的 Advisor 具体是什么类型。
Advisor 的类型
我们之前分析过了加载切面配置和查找 Advisor 的代码,从中可以知道 Advisor 的具体类型,这里做一个简单的回顾。
如果是通过 XML 配置文件进行的切面配置,那么,在加载配置的时候,创建的 Advisor 类型是 AspectJPointcutAdvisor,其中有一个advice
属性,在创建 AspectJPointcutAdvisor 对象的时候会作为构造方法的参数传入,因此,这种情况下可以直接通过getAdvice
方法获取到 Advice 对象。
而如果是通过注解的方式配置的切面,Advisor 的类型是 InstantiationModelAwarePointcutAdvisorImpl,其中并没有 Advice 类型的属性,当它的getAdvice
方法被调用的时候,才会封装 Advice。
下面看一下具体封装的逻辑。
获取 Advice
找到 InstantiationModelAwarePointcutAdvisorImpl 的getAdvice
方法。
// org.springframework.aop.aspectj.annotation.InstantiationModelAwarePointcutAdvisorImpl#getAdvicepublicsynchronizedAdvicegetAdvice() { if (this.instantiatedAdvice==null) { this.instantiatedAdvice=instantiateAdvice(this.declaredPointcut); } returnthis.instantiatedAdvice; }
主要的逻辑由instantiateAdvice
方法完成。
// org.springframework.aop.aspectj.annotation.InstantiationModelAwarePointcutAdvisorImpl#instantiateAdviceprivateAdviceinstantiateAdvice(AspectJExpressionPointcutpointcut) { Adviceadvice=this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName); return (advice!=null?advice : EMPTY_ADVICE); }
再进入aspectJAdvisorFactory
的getAdvice
方法,这个方法的具体实现在 ReflectiveAspectJAdvisorFactory 类型中。
这个方法代码比较多,但是并不复杂。
Class<?>candidateAspectClass=aspectInstanceFactory.getAspectMetadata().getAspectClass(); validate(candidateAspectClass);
首先,获取并验证了candidateAspectClass
,也就是配置切面的类。
AspectJAnnotation<?>aspectJAnnotation=AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod); if (aspectJAnnotation==null) { returnnull; }
然后,在方法信息上查找切面配置相关的注解,findAspectJAnnotationOnMethod
方法的源码如下:
// org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory.AspectJAnnotationprotectedstaticAspectJAnnotation<?>findAspectJAnnotationOnMethod(Methodmethod) { for (Class<?>clazz : ASPECTJ_ANNOTATION_CLASSES) { AspectJAnnotation<?>foundAnnotation=findAnnotation(method, (Class<Annotation>) clazz); if (foundAnnotation!=null) { returnfoundAnnotation; } } returnnull; }
逻辑比较简单,就是遍历ASPECTJ_ANNOTATION_CLASSES
中的类型信息,然后在方法上查找该类型的注解,如果找到了,就直接返回结果。ASPECTJ_ANNOTATION_CLASSES
包含的注解类型就是切面配置类中可以在方法上标记的几个注解类型。
privatestaticfinalClass<?>[] ASPECTJ_ANNOTATION_CLASSES=newClass<?>[] { Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class};
回到getAdvice
中,如果在方法上找不到这些注解,那么说明这个方法与切面的配置无关,则直接返回空。
接着往下看。
// If we get here, we know we have an AspectJ method.// Check that it's an AspectJ-annotated classif (!isAspect(candidateAspectClass)) { thrownewAopConfigException("Advice must be declared inside an aspect type: "+"Offending method '"+candidateAdviceMethod+"' in class ["+candidateAspectClass.getName() +"]"); }
到这里,其实已经找到了方法上的注解,这一步是为了确保这个方法是在一个切面配置类中。以上这些都是准备工作。
下面开始创建 Advice,这个过程对应方法体中switch
语句的部分。其中,针对方法上的注解,分别做了不同的处理。我们依次分析。
对于 PointCut 注解,这里不需要处理,因为它标记的方法并不是一个增强方法。
对于 Around 注解,会创建一个 AspectJAroundAdvice 类型的 Advice 对象。我们进入构造方法查看。
publicAspectJAroundAdvice( MethodaspectJAroundAdviceMethod, AspectJExpressionPointcutpointcut, AspectInstanceFactoryaif) { super(aspectJAroundAdviceMethod, pointcut, aif); }
直接调用了父类 AbstractAspectJAdvice 的构造方法。
publicAbstractAspectJAdvice( MethodaspectJAdviceMethod, AspectJExpressionPointcutpointcut, AspectInstanceFactoryaspectInstanceFactory) { Assert.notNull(aspectJAdviceMethod, "Advice method must not be null"); this.declaringClass=aspectJAdviceMethod.getDeclaringClass(); this.methodName=aspectJAdviceMethod.getName(); this.parameterTypes=aspectJAdviceMethod.getParameterTypes(); this.aspectJAdviceMethod=aspectJAdviceMethod; this.pointcut=pointcut; this.aspectInstanceFactory=aspectInstanceFactory; }
构造方法中也没有复杂的逻辑,就是一些成员变量的初始化。构造完的 AspectJAroundAdvice 对象会被赋值到springAdvice
变量。
对于后面的 Before / After / AfterReturning / AfterThrowing 这四个注解,也都有上述相同的逻辑,它们都会通过构造方法初始化,而构造方法中也都调用了父类 AbstractAspectJAdvice 的构造方法,因此,它们的创建逻辑相同,只是最终的对象类型不同而已。
不过 AfterReturning 和 AfterThrowing 注解在创建完之后,还给springAdvice
设置了额外的属性,这个额外的属性分别对应 AfterReturning 注解的returning
属性以及 AfterThrowing 注解的throwing
属性,这样,配置在注解上的属性值,就会保存在最终创建的 Advice 对象中。
最后还有一种情况,就是不属于以上任何注解的情况,会抛出异常。
现在就得到了一个 Advice 对象springAdvice
。
配置 Advice
// 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(); returnspringAdvice;
方法返回结果之前,再对springAdvice
对象做一些必要的配置,包括aspectName
和declarationOrder
两个属性的设置,以及增强方法的参数绑定。
增强方法的参数中可以包含一个 JoinPoint 类型的参数,表示当前被增强的接入点,对于 AfterReturning 和 AfterThrowing 类型的增强方法,还可以在方法参数中包含一个表示当前目标方法调用的异常或者返回值的参数,然后在注解的对应属性上指定相应的参数名,这里就会对这些参数进行绑定,使得增强方法中可以获取到目标方法跑出的一场或者返回的结果。
最后将springAdvice
返回即可。
至此,InstantiationModelAwarePointcutAdvisorImpl 的getAdvice
方法的流程就分析完了。
回到 DefaultAdvisorAdapterRegistry 的getInterceptors
方法中。
// org.springframework.aop.framework.adapter.DefaultAdvisorAdapterRegistry#getInterceptorspublicMethodInterceptor[] getInterceptors(Advisoradvisor) throwsUnknownAdviceTypeException { List<MethodInterceptor>interceptors=newArrayList<>(3); Adviceadvice=advisor.getAdvice(); if (adviceinstanceofMethodInterceptor) { interceptors.add((MethodInterceptor) advice); } for (AdvisorAdapteradapter : this.adapters) { if (adapter.supportsAdvice(advice)) { interceptors.add(adapter.getInterceptor(advisor)); } } if (interceptors.isEmpty()) { thrownewUnknownAdviceTypeException(advisor.getAdvice()); } returninterceptors.toArray(newMethodInterceptor[0]); }
第一步获取 Advice 的过程就分析完了,后面的流程就是将 Advice 对象,封装成拦截器。
总结
本文主要分析了 InstantiationModelAwarePointcutAdvisorImpl 的getAdvice
方法,如何从 Advisor 中获取 Advice 对象的过程。对于通过注解配置的切面信息中的增强方法,其对应的 Advice 对象需要在获取的时候进行创建,不过这个创建的过程只会执行一次,之后会被缓存在 InstantiationModelAwarePointcutAdvisorImpl 的instantiatedAdvice
成员变量中。
下一篇讲分析getInterceptors
方法之后的流程,如何将 Advice 对象封装成对应的拦截器。