在 SpringBoot2 | Spring AOP 原理源码深度剖析(八)一文中,介绍了 Spring AOP 的多种实现机制,原理大同小异。本篇来继续介绍一款开源的 AOP 框架:Nepxion Matrix
,该框架致力于对 Spring AOP 的扩展和增强,灵活而且易用。
GitHub:https://github.com/Nepxion/Matrix
Matrix 框架主要对 Spring 做了三个模块的扩展:Spring AutoProxy,Spring Registrar,Spring Registrar。
本篇主要分析 AOP相关的功能,也就是 AutoProxy 模块。主要围绕以下几个方面:
Nepxion Matrix AutoProxy
框架有什么特性?Nepxion Matrix AutoProxy
AOP 增强框架的扩展点什么?如何扩展?Spring AOP
异同点大多数的项目中,使用Spring AOP
的方式都是采用注解形式,方法上面加个自定义注解即可实现,这种方式注解只能加在类方法上,不能加在接口或接口方法上。Nepxion Matrix AutoProxy
主要针对这些问题,特性如下:
这里要介绍一下上面提到了两种代理方式:
通用代理是指通过AbstractAutoProxyCreator
中的变量interceptorNames
,来设置具体的通知名称。
额外代理是指通过实现自定义逻辑,来选择性设置通知(这里的通知也就是拦截方法)。
要理解该框架的实现方式,首先要知道该框架的扩展点是什么。
在SpringBoot2 | Spring AOP 原理源码深度剖析(八)中也提到了,AbstractAutoProxyCreator
抽象类为Spring AOP
暴露的抽象扩展类,其每一个实现类都代表着一种实现机制。Nepxion Matrix
也正是基于此类做的扩展,分别来看一下涉及到核心类:
AbstractAutoScanProxy
:Nepxion Matrix
提供的核心抽象类,封装了获取顾问advisor
的方法,并暴露了一些抽象方法,如获取通知,注解等方法。该类同 Spring 内置的代理机制AbstractAdvisorAutoProxyCreator
平级,默认先执行Spring AOP
内置代理机制。DefaultAutoScanProxy
:提供了一些默认为空的实现,不能直接使用。MyAutoScanProxyForClass
:类代理机制,提供通用代理实现。MyAutoScanProxyForMethod
:方法代理机制,提供额外代理。MyAutoScanProxy
:混合代理,提供通用代理和额外代理。这里就针对类代理的方式,进行源码分析。先来看源码中的示例:
@MyAnnotation1(name = "MyAnnotation1", label = "MyAnnotation1", description = "MyAnnotation1")
public interface MyService1 {
void doA(String id);
void doB(String id);
}
@Service
public class MyService1Impl implements MyService1 {
@Override
public void doA(String id) {
System.out.println("doA");
}
@Override
public void doB(String id) {
System.out.println("doB");
}
}
示例中在接口上添加注解@MyAnnotation1
,两个实现方法都会走代理方法。
首先来看一下AbstractAutoScanProxy
的构造方法:
public AbstractAutoScanProxy(String[] scanPackages, ProxyMode proxyMode, ScanMode scanMode, boolean exposeProxy) {
//设置代理目录,非指定目录直接返回
this.scanPackages = scanPackages;
//Spring提供的是否暴露代理对象标识。
this.setExposeProxy(exposeProxy);
//代理模式,类代理或是方法代理。
this.proxyMode = proxyMode;
this.scanMode = scanMode;
//......
// 设定全局拦截器,通过名称指定。
// 如果同时设置了全局和额外的拦截器,那么它们都同时工作,全局拦截器先运行,额外拦截器后运行
Class extends MethodInterceptor>[] commonInterceptorClasses = getCommonInterceptors();
String[] commonInterceptorNames = getCommonInterceptorNames();
String[] interceptorNames = ArrayUtils.addAll(commonInterceptorNames, convertInterceptorNames(commonInterceptorClasses));
if (ArrayUtils.isNotEmpty(interceptorNames)) {
setInterceptorNames(interceptorNames);
}
}
构造方法中有两个变量比较重要:
exposeProxy
:默认为false,这里设置为 true,支持在同一个类中,一个方法调用另一个方法走代理拦截方法。
比如,类中方法1调用方法2,开启该变量,则不会直接调用方法2,而是从 threadLocal 中取出提前存入的代理类发起调用。
interceptorNames
:通知名称,也就是通用代理,通过构造方法设置。在后面生成代理类的方法中会根据该变量取出所有拦截器实例。
我们来看一下代理执行入口。因为该类继承beanPostProcessor
,所以最终会执行扩展接口postProcessAfterInitialization
,在该方法中调用模板方法getAdvicesAndAdvisorsForBean
,来看一下Nepxion Matrix
对该方法的实现:
protected Object[] getAdvicesAndAdvisorsForBean(Class> beanClass, String beanName, TargetSource targetSource) {
boolean scanPackagesEnabled = scanPackagesEnabled();
// scanPackagesEnabled=false,表示“只扫描指定目录”的方式未开启,则不会对扫描到的bean进行代理预先判断
if (scanPackagesEnabled) {
boolean scanPackagesContained = scanPackagesContained(beanClass);
// 如果beanClass的类路径,未包含在扫描目录中,返回DO_NOT_PROXY
if (!scanPackagesContained) {
return DO_NOT_PROXY;
}
}
// 根据Bean名称获取Bean对象
Object bean = beanMap.get(beanName);
// 获取最终目标类,
Class> targetClass = null;
if (bean != null /* && AopUtils.isCglibProxy(bean) */) {
targetClass = AopProxyUtils.ultimateTargetClass(bean);
} else {
targetClass = beanClass;
}
// Spring容器扫描实现类
if (!targetClass.isInterface()) {
// 扫描接口(从实现类找到它的所有接口)
if (targetClass.getInterfaces() != null) {
for (Class> targetInterface : targetClass.getInterfaces()) {
Object[] proxyInterceptors = scanAndProxyForTarget(targetInterface, beanName, false);
if (proxyInterceptors != DO_NOT_PROXY) {
return proxyInterceptors;
}
}
}
// 扫描实现类(如果接口上没找到注解, 就找实现类的注解)
Object[] proxyInterceptors = scanAndProxyForTarget(targetClass, beanName, true);
if (proxyInterceptors != DO_NOT_PROXY) {
return proxyInterceptors;
}
}
return DO_NOT_PROXY;
}
上面逻辑中调用了AopProxyUtils.ultimateTargetClass(bean)
来获取对应的 class 对象,而不是使用参数中的beanClass
。因为方法传进来的 class 对象有可能是被代理过的 class,所以这里要获取最初的 class 对象。
继续跟进scanAndProxyForTarget
方法:
protected Object[] scanAndProxyForTarget(Class> targetClass, String beanName, boolean proxyTargetClass) {
String targetClassName = targetClass.getCanonicalName();
//这里获取额外代理
Object[] interceptors = getInterceptors(targetClass);
// 排除java开头的接口,例如java.io.Serializable,java.io.Closeable等,执行不被代理
if (StringUtils.isNotEmpty(targetClassName) && !targetClassName.startsWith("java.")) {
// 避免对同一个接口或者类扫描多次
Boolean proxied = proxyMap.get(targetClassName);
if (proxied != null) {
if (proxied) {
return interceptors;
}
} else {
Object[] proxyInterceptors = null;
switch (proxyMode) {
// 只通过扫描到接口名或者类名上的注解后,来确定是否要代理
case BY_CLASS_ANNOTATION_ONLY:
proxyInterceptors = scanAndProxyForClass(targetClass, targetClassName, beanName, interceptors, proxyTargetClass);
break;
// 只通过扫描到接口或者类方法上的注解后,来确定是否要代理
case BY_METHOD_ANNOTATION_ONLY:
proxyInterceptors = scanAndProxyForMethod(targetClass, targetClassName, beanName, interceptors, proxyTargetClass);
break;
// 上述两者都可以
case BY_CLASS_OR_METHOD_ANNOTATION:
Object[] classProxyInterceptors = scanAndProxyForClass(targetClass, targetClassName, beanName, interceptors, proxyTargetClass);
// 没有接口或者类名上扫描到目标注解,那么扫描接口或者类的方法上的目标注解
Object[] methodProxyInterceptors = scanAndProxyForMethod(targetClass, targetClassName, beanName, interceptors, proxyTargetClass);
if (classProxyInterceptors != DO_NOT_PROXY || methodProxyInterceptors != DO_NOT_PROXY) {
proxyInterceptors = interceptors;
} else {
proxyInterceptors = DO_NOT_PROXY;
}
break;
}
// 是否需要代理
proxyMap.put(targetClassName, Boolean.valueOf(proxyInterceptors != DO_NOT_PROXY));
return proxyInterceptors;
}
}
return DO_NOT_PROXY;
}
大致的思路:根据MyService1Impl
获取到接口MyService1
,然后判断接口上是否有指定的注解@MyAnnotation1
,判断条件符合,然后调用getInterceptors
方法获取拦截器,传递到父类AbstractAutoProxyCreator
中的方法createProxy
中,完成代理。
跟进getInterceptors
方法来看一下:
protected Object[] getInterceptors(Class> targetClass) {
Object[] interceptors = getAdditionalInterceptors(targetClass);
if (ArrayUtils.isNotEmpty(interceptors)) {
return interceptors;
}
Class extends MethodInterceptor>[] commonInterceptorClasses = getCommonInterceptors();
String[] commonInterceptorNames = getCommonInterceptorNames();
if (ArrayUtils.isNotEmpty(commonInterceptorClasses) || ArrayUtils.isNotEmpty(commonInterceptorNames)) {
return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS;
}
return DO_NOT_PROXY;
}
这里先获取所有的额外代理拦截器,如果有直接返回。如果为空,则返回一个是否有通用代理拦截器的标识,具体拦截器的名称上面已经通过构造方法传入。
再来看一下在createProxy
方法:
protected Object createProxy(
Class> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
//判断代理生成方式
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
//获取拦截器,包括通用代理和额外代理
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());
}
Nepxion Matrix
重写了上面的shouldProxyTargetClass(beanClass, beanName)
方法,重写逻辑如下:
protected boolean shouldProxyTargetClass(Class> beanClass, String beanName) {
// 设置不同场景下的接口代理,还是类代理
Boolean proxyTargetClass = proxyTargetClassMap.get(beanName);
if (proxyTargetClass != null) {
return proxyTargetClass;
}
return super.shouldProxyTargetClass(beanClass, beanName);
}
需要注意的是,上述重写方式只在SpringBoot 1.x
中生效,因为在 2.x
版本中,proxyFactory.isProxyTargetClass()
默认为 true,默认走 cglib 代理,上述重写的方法不会执行。
继续跟进获取拦截器的方法buildAdvisors
:
protected Advisor[] buildAdvisors(String beanName, Object[] specificInterceptors) {
//解析通用代理拦截器
Advisor[] commonInterceptors = resolveInterceptorNames();
List
通过调用resolveInterceptorNames
,根据interceptorNames
中设置的拦截器名称,从Spring
容器中取出所有的通用代理拦截器,结合指定拦截器specificInterceptors
,一起织入代理类。
Nepxion Matrix AutoProxy
中的方法代理这里就不展开了,原理类似。
1)代理机制原理一样,都是AbstractAutoScanProxy
的实现类,只是代理功能点不同。
2)两种代理机制可同时使用。如果同时使用,一定保证Spring AOP
先代理,Nepxion Matrix AutoProxy
后代理。这也是默认的代理顺序。尽量不要通过重写Ordered
接口的方式改变先后顺序。
原因是采用Spring AOP
注解形式时需要获取代理类最初的 Class 对象,如果Nepxion Matrix AutoProxy
先执行,那么在执行Spring AOP
代理逻辑时获取到的当前 Class 对象就是被代理过重新生成的 Class 对象,这时就无法获取自定义的切面注解了。