1、基础概念
在扒SpringAOP的diku之前,咱们还是老生常谈的介绍一下关于AOP的一些基础概念:
- 目标对象(target):被增强的对象
- 连接点(JoinPoint):被增强方法的增强点,即被增强方法的before、after return、throw exception位置。
- 通知(Advice):增强逻辑
- 切入点(Pointcut ):指定增强逻辑需要增强在哪个对象的哪个方法上。可以简单理解为pointcut = target + joinpoint。
- 顾问(Advisor):Advisor其实是Spring AOP代码结构上的概念,Advisor是Pointcut和Advice的封装,一个Advisor可以清楚的描述需要在哪个地方(Pointcut)进行什么增强(Advice)。Advisor = Pointcut + Advice。
- 切面(Aspect):相当于Advisor,也是Pointcut + Advice,另一种概念而已。一个切面能够清楚的描述需要在哪个地方(Pointcut)进行什么增强(Advice)。
- 代理对象(Proxy):最终生成的增强对象
2、AOP使用方式
网络上的博客大部分都说AOP的使用方法有:基于经典代理AOP实现、基于纯POJO切面式的实现等等。从使用层面上分析,确实可以分为这些使用类型。
而我们今天要说的,是从底层原理上分析,这么看其实AOP的实现方式仅有三种情况,这三种情况分别对应三个实现类ProxyFactory、ProxyFactoryBean、AspectJProxyFactory。这三个类与父类的继承关系如下:
classDiagram
class ProxyFactory
class ProxyFactoryBean
class AspectJProxyFactory
class ProxyCreatorSupport
class AdvisedSupport
class ProxyConfig
class Advised
ProxyFactory --|> ProxyCreatorSupport
ProxyFactoryBean--|> ProxyCreatorSupport
AspectJProxyFactory--|> ProxyCreatorSupport
ProxyCreatorSupport --|> AdvisedSupport
AdvisedSupport --|> ProxyConfig
AdvisedSupport ..|> Advised
下面,我们从上往下分别简单介绍下这些类或接口的作用:
ProxyFactory:允许用户完全脱离Spring容器构造动态代理类。即手动构造Pointcut、Advice以及Advisor。ProxyFactoryBean:依赖Spring容器,用户要将切点、通知、顾问等AOP相关的类注入Spring中,然后依赖ProxyFactoryBean构造单个动态代理类。AspectJProxyFactory:以切面的视角构造动态代理类,我们只需要创建简单的pojo,然后通过aop:config标签或者AspectJ注解实现大范围动态代理。ProxyCreatorSupport:ProxyFactory、ProxyFactoryBean、AspectJProxyFactory的公共父类,其中包含了一些公共属性和方法,例如aopProxyFactory、createAopProxy()等。AdvisedSupport:ProxyCreatorSupport的父类,其中包含了advice、advisor的配置与操作方法,根据名字也可以很容易了解,AdvisedSupport是关于advice操作支持的类。ProxyConfig:AdvisedSupport的父类,主要存储了代理相关的配置,比如是否冻结、是否暴露到当前线程中等。Advised:AdvisedSupport的接口,规范了AdvisedSupport的一系列行为。
对于ProxyFactory、ProxyFactoryBean、AspectJProxyFactory三类代理工厂,从作者的视角去看,这三类工厂具备有不同的上层和相同的下层。
不同的上层表示这三种类关于获取Pointcut、Advice等重要AOP配置的方式是不同的。比如ProxyFactory完全是用户手动构建然后通过addxxx方法将配置注入。而ProxyFactoryBean和AspectJProxyFactory便依赖了Spring容器强大的IOC功能,帮助用户自动注入Pointcut和Advice等,用户只需要关注核心切面逻辑即可。
相同的下层表示Spring的AOP代理最终还是两种实现,一种是JDK的动态代理实现;另外一种是Cglib的动态代理实现。因此,上述三种AOP的使用方式最终都会创建出CglibAopProxy/JdkDynamicAopProxy,进而通过这些AopProxy最终获得代理类。
我们可以用下图来形象描述上面的意思。
接下来便会对这三种AOP的使用方式中的ProxyFactory、JdkDynamicAopProxy进行源码层面(扒diku)的分析。
3、ProxyFactory底层原理
ProxyFactory的使用一般过程如下所示,主要使用步骤分为这几步:
- 创建代理工厂
- 配置需要代理类
- 配置advice或者advisor,前者会默认使用不过滤的pointcut,后者我们可以配置自定义的pointcut
- 获取代理类,执行相应方法,观察是否代理成功
// ProxyFactoryTest.java
public class ProxyFactoryTest {
public static void main(String[] args) {
// 1、创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
// 2、配置需要代理类
proxyFactory.setTarget(new TargetClass());
// 3、配置advice。proxyFactory中最终仅保存了advisor,直接添加advice最终也会转化成advisor,只不过其中的pointcut不过进行任何过滤
proxyFactory.addAdvice(new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("MethodBeforeAdvice before");
}
});
// 4、配置advisor。我们可以使用PointcutAdvisor接口配置匿名advisor,在实现的方法中,我们可以指定advice增强和pointcut切点
proxyFactory.addAdvisor(new PointcutAdvisor() {
@Override
public Advice getAdvice() {
return new AfterReturningAdvice() {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("returning");
}
};
}
@Override
public boolean isPerInstance() {
return true;
}
@Override
public Pointcut getPointcut() {
NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
nameMatchMethodPointcut.setMappedName("print2");
return nameMatchMethodPointcut;
}
});
// 5、完成配置后,可以使用getProxy()方法获取代理类
TargetClass proxy = (TargetClass) proxyFactory.getProxy();
// 6、执行该方法便能够看到已经被增强了
proxy.print();
System.out.println("----------------------");
proxy.print2();
}
}
class TargetClass {
public void print() {
System.out.println("TargetClass print");
}
public void print2() {
System.out.println("TargetClass print2");
}
}
大家可以把这个demo拷贝到本地运行试试,运行结果和下面展示的应该没有差别。
MethodBeforeAdvice before
TargetClass print
----------------------
MethodBeforeAdvice before
TargetClass print2
returning
下面我们将针对其中的advice、advisor配置,proxy代理获取的核心步骤进行详细的源码分析,其余部分都非常简单,大家自行查阅即可。
3.1、Advice的配置
Advice配置的基本代码如下所示,我们可以配置各种功能的增强,比如在进入方法前的增强(MethodBeforeAdvice)、方法返回后的增强(AfterReturningAdvice)等。这些增强之后会根据pointcut被横切到需要代理的目标类上。
// ProxyFactoryTest.java
proxyFactory.addAdvice(new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("MethodBeforeAdvice before");
}
});
addAdvice()方法调用链路如下所示。主要都在AdvisedSupport这个类中,上面我们分析继承关系时也说到了AdvisedSupport主要包含了advice、advisor的配置与操作方法。
@Override
public void addAdvice(Advice advice) throws AopConfigException {
// 获取当前已经配置的advisor数量
int pos = this.advisors.size();
// 添加advice
addAdvice(pos, advice);
}
@Override
public void addAdvice(int pos, Advice advice) throws AopConfigException {
Assert.notNull(advice, "Advice must not be null");
if (advice instanceof IntroductionInfo) {
// We don't need an IntroductionAdvisor for this kind of introduction:
// It's fully self-describing.
addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
} else if (advice instanceof DynamicIntroductionAdvice) {
// We need an IntroductionAdvisor for this kind of introduction.
throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
} else {
// [1] 大部分情况都是将advice封装成DefaultPointcutAdvisor,然后添加
addAdvisor(pos, new DefaultPointcutAdvisor(advice));
}
}
@Override
public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
if (advisor instanceof IntroductionAdvisor) {
validateIntroductionAdvisor((IntroductionAdvisor) advisor);
}
// 具体的添加方法
addAdvisorInternal(pos, advisor);
}
private List<Advisor> advisors = new ArrayList<>();
private void addAdvisorInternal(int pos, Advisor advisor) throws AopConfigException {
Assert.notNull(advisor, "Advisor must not be null");
// [2] 被冻结的ProxyFactory不允许添加advisor
if (isFrozen()) {
throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
}
// outbond异常
if (pos > this.advisors.size()) {
throw new IllegalArgumentException(
"Illegal position " + pos + " in advisor list with size " + this.advisors.size());
}
// 添加进advisors列表中
this.advisors.add(pos, advisor);
// [3] 清理缓存
adviceChanged();
}
private transient Map<MethodCacheKey, List<Object>> methodCache;
protected void adviceChanged() {
// 清空map
this.methodCache.clear();
}
这个调用链路其实就是把用户添加的advice封装成DefaultPointcutAdvisor,然后添加到ProxyFactory里的advisors列表中。advisors列表最终会依次增强到目标类,也就是target中。
这里有三点补充下:
- [1] DefaultPointcutAdvisor:这是我们使用最为频繁的advisor,从命名中就可以看出,这个advisor持有pointcut(切向哪)和advice(增强什么)两个重要配置类。**这个advisor(顾问,也可以理解为切面)能够详细描述出在哪个切点上进行怎样的增强。**如果我们在构造时仅传入advice配置,那么pointcut配置就默认使用
Pointcut.TRUE,表示能够匹配所有的类和方法,即能够增强任何需要增强类和方法,不进行过滤。 - [2][3] 冻结和缓存要一起搭配来看,因为只有被冻结的ProxyFactory,methodCache才能保持不变,不然每有一个advisor添加,缓存都会被清空。被冻结后,ProxyFactory会做一个小优化,即缓存method与advisor的映射,后期代理类每一次调用增强方法时,会直接从缓存中获取增强逻辑,不需要每次遍历advisors列表,判断advisor持有的pointcut是否满足method的过滤条件。
最后再总结下:Advice可以使用addAdvice()方法进行配置,这会向ProxyFactory中的advisors列表中append一个DefaultPointcutAdvisor,这个顾问的pointcut采用Pointcut.TRUE,支持任何类和方法的增强。
此时ProxyFactory的结构类似下图所示。
至此,Advice的配置流程我们已经梳理完了。
3.2、Advisor配置
Advisor基本配置的代码如下所示,本例我们使用PointcutAdvisor接口构造匿名内部类,并且分别实现了getAdvice()和getPointcut()方法用来定义Advisor所持有的advice和pointcut配置,这种方式比较灵活可控。当然,Advisor还有许多具备特殊功能的实现类,大家可以自行探索。
// ProxyFactoryTest.java
proxyFactory.addAdvisor(new PointcutAdvisor() {
@Override
public Advice getAdvice() {
return new AfterReturningAdvice() {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("returning");
}
};
}
@Override
public boolean isPerInstance() {
return true;
}
@Override
public Pointcut getPointcut() {
NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
nameMatchMethodPointcut.setMappedName("print2");
return nameMatchMethodPointcut;
}
});
addAdvisor()方法的调用链路如下所示:
// AdvisedSupport.java
@Override
public void addAdvisor(Advisor advisor) {
int pos = this.advisors.size();
addAdvisor(pos, advisor);
}
@Override
public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
if (advisor instanceof IntroductionAdvisor) {
validateIntroductionAdvisor((IntroductionAdvisor) advisor);
}
addAdvisorInternal(pos, advisor);
}
// ...
咦,调用链路貌似短了很多,哈哈哈,这是因为调用到addAdvisor(int pos, Advisor advisor)后,其实就与3.1重合了,接下来调用的方法一模一样。这也说明了,ProxyFactory无论是添加advice或者advisor,到最后其实都是添加advisor到advisors列表中。因为添加advice时,缺少pointcut的配置,所以就使用默认的Pointcut.TRUE匹配所有类和方法;而添加advisor时,是需要用户指定pointcut的,而这两类逻辑后半段其实是重合的,因此可以复用一部分。
graph LR;
添加配置 --> 添加advice
添加配置 --> 添加advisor
添加advice --> 使用默认Pointcut.TRUE封装DefaultPointcutAdvisor
添加advisor --> 直接封装DefaultPointcutAdvisor
使用默认Pointcut.TRUE封装DefaultPointcutAdvisor --> append至advisors列表中
直接封装DefaultPointcutAdvisor --> append至advisors列表中
至此,Advisor配置也说完了。
3.3、Proxy代理获取
有了以上的准备,我们有了需要被代理的目标类(TargetClass)、顾问列表(Advisors),其中顾问列表中的每个顾问还包含切点(pointcut)和增强逻辑(Advice)。因此,我们有了所有能够实现AOP的前置条件,接下来我们就要开始进行代理实现了。
代理的获取只需要调用如下语句,从中我们可知getProxy()是获取代理的核心方法。
// ProxyFactoryTest.java
TargetClass proxy = (TargetClass) proxyFactory.getProxy();
getProxy()方法源码如下所示,首先会创建一个AopProxy,然后再从AopProxy中获取真正的代理对象。
// ProxyFactory.java
public Object getProxy() {
return createAopProxy().getProxy();
}
createAopProxy()方法源码如下所示,它首先获取AopProxyFactory,通过工厂创建AopProxy。AopProxyFactory的实现只有一个,即DefaultAopProxyFactory,在DefaultAopProxyFactory中我们可以看到获取的AopProxy最终只可能有两类:JdkDynamicAopProxy&ObjenesisCglibAopProxy(即CglibAopProxy),最后获取的Proxy只可能从这两个类中产生。
// ProxyCreatorSupport.java
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
// 如果存在增强监听器的话,会在这里一一激活。由于监听器不是重点,这里不过多叙述
activate();
}
// getAopProxyFactory()一般只返回DefaultAopProxyFactory,只有这一个实现类
return getAopProxyFactory().createAopProxy(this);
}
// DefaultAopProxyFactory.java
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!NativeDetector.inNativeImage() &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass) || ClassUtils.isLambdaClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
3.4、小结
上面我们介绍了ProxyFactory获取代理过程中与其他两者的不同之处,也是第二节所说的不同的上层,即配置advice、advisor,获取代理。最终我们来到了DefaultAopProxyFactory#createAopProxy方法处,这里可能会返回两种类型的AopProxy:JdkDynamicAopProxy&ObjenesisCglibAopProxy(即CglibAopProxy)。这里便是在第二节说的相同的下层,无论是ProxyFactory、ProxyFactoryBean还是AspectJProxyFactory,他们最后都会来到此处获取两类中的其中一类,然后再通过AopProxy获取具体的代理对象。下面,我们将会具体介绍JdkDynamicAopProxy获取代理的具体逻辑。
4、JdkDynamicAopProxy获取代理
JdkDynamicAopProxy获取代理的过程非常简单,源码如下所示:
// JdkDynamicAopProxy.java
@Override
public Object getProxy() {
return getProxy(ClassUtils.getDefaultClassLoader());
}
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
看到Proxy.newProxyInstance是不是非常熟悉,其实就是JDK动态代理的过程。但这里要注意的是,传入的InvocationHandler是this,说明JdkDynamicAopProxy也是一个InvocationHandler,即继承了InvocationHandler,那么就必然要实现代理逻辑的invoke()方法。到这我们就明白了,真正的代理逻辑肯定封装在JdkDynamicAopProxy#invoke方法中。
JdkDynamicAopProxy#invoke源码如下所示:
// JdkDynamicAopProxy.java
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// [4.1详细介绍] 预定义变量,注意到this.advised这个全局变量,它其实就是ProxyFactory
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
// 处理一些非增强的方法,不是重点,可以忽略
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
return equals(args[0]);
} else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
return hashCode();
} else if (method.getDeclaringClass() == DecoratingProxy.class) {
return AopProxyUtils.ultimateTargetClass(this.advised);
} else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
Object retVal;
if (this.advised.exposeProxy) {
// [4.2详细介绍] 将代理暴露到当前ThreadLocal中
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// [4.3详细介绍] 获取拦截链,这个拦截链由所有advisor组成
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// [4.4详细介绍] 如果拦截链为空,说明用户并未配置增强(advise或者advisor),此时直接调用AopUtils.invokeJoinpointUsingReflection()方法反射执行原方法
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
} else {
// [4.5详细介绍] 拦截链不为空,首先执行拦截链,类似于责任链的执行
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
// 如果需要,处理返回值的类型
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
retVal = proxy;
} else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// 将原来threadlocal中的对象设置回去。
AopContext.setCurrentProxy(oldProxy);
}
}
}
invoke()方法看上去很多,但其实主流程其实非常易懂。就是从用户配置的advice、advisor中创建拦截链(每一个advisor都是一个拦截点),然后依照责任链模式执行下去,直到执行待增强的方法。
大家有了主流程的执行概念后,下面对其中重要的部分进行深入分析。
4.1、this.advised变量介绍
预定义变量中的this.advised值得我们注意,他其实就是我们最开始创建的ProxyFactory,大家可以返回去看下3.3小节,在构造JdkDynamicAopProxy时,我们将this作为config传入到JdkDynamicAopProxy,JdkDynamicAopProxy使用的就是advised变量进行接收。因此,this.advised===ProxyFactory。这里面包含了用户配置的advisor列表(即pointcut和advice),后期用户配置拦截链也要强依赖this.advised。
4.2、暴露代理到ThreadLocal
我们来看下setCurrentProxy()这个方法,将代理类暴露到当前线程的ThreadLocal中。
为什么要把这个单独抽出来介绍呢?不知道大家还记不记得在声明式事务中,如果一个类的非事务方法调用了本类的事务方法,该事务并不会生效。此时我们可以在非事务方法中使用AopContext.currentProxy()获取当前的类代理,然后再用该代理调用事务方法即可生效。而AopContext.currentProxy()获取的代理类则是在此时set到threadlocal中的。
// AopContext.java
private static final ThreadLocal<Object> currentProxy = new NamedThreadLocal<>("Current AOP proxy");
@Nullable
static Object setCurrentProxy(@Nullable Object proxy) {
Object old = currentProxy.get();
if (proxy != null) {
currentProxy.set(proxy);
}
else {
currentProxy.remove();
}
return old;
}
4.3、拦截链的构造
拦截链构造的代码如下所示,构造时主要还是依赖this.advised这个变量。
// JdkDynamicAopProxy.java
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
我们进入到方法内部,这里主要对缓存进行操作,因为构造拦截链比较耗时,使用缓存能够大幅度提高效率。
这里多嘴一句,记得上面我们添加advice或advisor时会清空缓存嘛?如果想要固定整个拦截链,提高cache hit比例,我们可以在配置完advice和advisor后frozen ProxyFactory,禁止用户配置其他增强,保证缓存不被清空。
// AdvisedSupport.java
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
// 将method封装成MethodCacheKey
MethodCacheKey cacheKey = new MethodCacheKey(method);
// 尝试从缓存中获取,如果缓存存在直接返回
List<Object> cached = this.methodCache.get(cacheKey);
if (cached == null) {
// 缓存中不存在,构造拦截链
cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
this, method, targetClass);
// 添加进缓存
this.methodCache.put(cacheKey, cached);
}
return cached;
}
真正的构造拦截链的方法还是在advisorChainFactory中,advisorChainFactory只有一个默认的实现类,DefaultAdvisorChainFactory,我们进入该类的方法里面看看。
// DefaultAdvisorChainFactory.java
@Override
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
// 获取advisors列表
Advisor[] advisors = config.getAdvisors();
// 用于存储拦截链中的节点
List<Object> interceptorList = new ArrayList<>(advisors.length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
Boolean hasIntroductions = null;
// 遍历advisors列表
for (Advisor advisor : advisors) {
// 判断advisor的类型,之前说过,大部分advisor都是DefaultPointcutAdvisor,因此我们主要分析PointcutAdvisor的情况
if (advisor instanceof PointcutAdvisor) {
// 强转一下
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
// 首先使用advisor中pointcut的ClassFilter判断一下增强的class是否能够匹配的上
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
// 能够匹配,再获取pointcut中MethodMatcher,判断增强的方法是否能够匹配
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
boolean match;
if (mm instanceof IntroductionAwareMethodMatcher) {
if (hasIntroductions == null) {
hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
}
match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
} else {
match = mm.matches(method, actualClass);
}
// 如果方法也能够匹配上,说明已经找到了切点,此时可以获取方法的增强。这里将advisor中的adivce装换成MethodInterceptor,然后全部添加进interceptorList再返回。
if (match) {
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
if (mm.isRuntime()) {
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
} else if (advisor instanceof IntroductionAdvisor) {
// ...
} else {
// ...
}
}
// 返回构造好的拦截链
return interceptorList;
}
这个方法的详细作用已经在每行代码的注释上说明出来了,其实主要就执行了两个逻辑:
- 通过pointcut判断class和method是否能够匹配
- 如果能够匹配,将advisor和MethodMatcher包装成InterceptorAndDynamicMethodMatcher,然后添加进拦截列表中并返回
pointcut匹配的过程大家感兴趣可以去深入了解下,我们这里介绍下advisor的包装过程。
// 这里的registry实现类为 DefaultAdvisorAdapterRegistry
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
// DefaultAdvisorAdapterRegistry.java
private final List<AdvisorAdapter> adapters = new ArrayList<AdvisorAdapter>(3);
// 预置adapter
public DefaultAdvisorAdapterRegistry() {
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
Advice advice = advisor.getAdvice();
// [1] 如果 advice 属于 MethodInterceptor,强转一下直接添加即可
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
// [2] 如果 advice 不属于 MethodInterceptor,使用 AdviceAdapter 对 advice 进行包装,包装成 MethodInterceptor
for (AdvisorAdapter adapter : this.adapters) {
if (adapter.supportsAdvice(advice)) {
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}
- [1] 如果我们当初配置的advice已经继承了MethodInterceptor的话(类似MethodBeforeAdviceInterceptor),直接强转添加进列表即可
- [2] 如果未继承,会通过提前预置的adapter对advice进行转化,转化过程也非常简单,就是通过构造器进行包装:
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
// 判断是否支持其实就是判断是否继承某个父类
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
// 如果继承,则进行包装
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
至此,拦截链已经完成构造了。
4.4、反射执行原方法
上面介绍了拦截链的构造。但大家可以想一想,如果我们配置的pointcut并未匹配任何一个类或者方法,拦截链最终为空,那岂不是任何增强都没有?Spring也帮我们想到了这一点,在没有任何增强的场景下,会调用AopUtils#invokeJoinpointUsingReflection方法直接反射执行对应的方法。
// JdkDynamicAopProxy.java
if (chain.isEmpty()) {
// 如果拦截链为空,首先获取方法的对应的参数
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
// 然后直接反射执行
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
AopUtils#invokeJoinpointUsingReflection源码如下所示:
// AopUtils.java
public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
throws Throwable {
try {
//
ReflectionUtils.makeAccessible(method);
return method.invoke(target, args);
} // ...省略掉一大堆catch
4.5、拦截链的执行
如果拦截链不为空,则开始执行具体增强过程。
// JdkDynamicAopProxy.java
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
// ReflectiveMethodInvocation.java
@Override
@Nullable
public Object proceed() throws Throwable {
// [1] 如果当前执行拦截点的索引为最后一个,说明之前所有的拦截点已经执行完成,也即所有的增强过程已经执行完成,此时需要执行连接点、即被增强的方法,invokeJoinpoint也是调用AopUtils#invokeJoinpointUsingReflection反射执行被增强的方法。
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
return invokeJoinpoint();
}
// [2] 如果索引不是最后一个,获取拦截链指定索引处的增强InterceptorAndDynamicMethodMatcher,并自增索引
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
// 判断方法和类是否匹配
if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
// 如果匹配的话,则执行InterceptorAndDynamicMethodMatcher中methodInterceptor中的invoke方法,注意这次将this(ReflectiveMethodInvocation)传递进了invoke方法中,下面我们将用MethodBeforeAdviceInterceptor举例分析invoke方法。
return dm.interceptor.invoke(this);
} else {
// 递归调用本方法
return proceed();
}
} else {
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
// MethodBeforeAdviceInterceptor.java
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
// 首先执行增强逻辑
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
// 这里的mi是ReflectiveMethodInvocation,执行proceed其实就是执行上面的proceed方法,上面的proceed方法便会继续从拦截链中获取下一个增强继续执行。
return mi.proceed();
}
执行增强过程的逻辑注释中已经分析的非常详细了,这里有几个小点补充下:
- [1] 这里一定是正常执行增强链的最后一步,即执行被增强(连接点)方法的地方,其实这么做也非常合理。对于拦截链来说,总得执行完前面的增强方法才能执行被增强方法啊,不然配置这些增强的意义又在哪呢。但是,对于不同的methodInterceptor来说,增强点会由自己控制,比如AfterReturningAdviceInterceptor,是先执行proceed方法,即执行到增强链的最后一步,获取被增强方法的返回值后再执行增强。
- [2] 这里获取的是InterceptorAndDynamicMethodMatcher,这是因为我们之前在构造拦截链的时候,将最终的matcher和MethodInterceptor封装成了InterceptorAndDynamicMethodMatcher。因此,拦截链上的每一个拦截点其实都是InterceptorAndDynamicMethodMatcher。
拦截链执行完成后,AOP(基于JDK动态代理)的执行也就差不多完成了,后面就是一些处理返回结果的逻辑,大家感兴趣可以自行了解。
4.6、小结
这里我们总结下JdkDynamicAopProxy获取代理的过程。
SpringAOP基于JDK动态代理获取代理类最终是由JdkDynamicAopProxy实现的。JdkDynamicAopProxy里封装了getProxy()方法获取目标类的代理类proxy,而每次执行代理类的方法时,都会被JdkDynamicAopProxy#invoke进行拦截(这里是基本的JDK动态代理内容,不了解的可以网上自行google)。
在invoke方法中,会从缓存中获取执行方法对应的拦截链,如果不存在则会立即构建拦截链并存放进缓存中。拦截链其实就是一个InterceptorAndDynamicMethodMatcher列表。每个InterceptorAndDynamicMethodMatcher都封装由MethodMatcher和MethodInterceptor,前者用于判断当前执行方法是否需要拦截,后者则是具体执行的拦截内容。而在该拦截链的末尾处则是被代理类的方法。
执行拦截链的过程就是遍历拦截列表,判断是否需要拦截增强,直至执行到末尾。