Spring AOP 增强框架 Nepxion Matrix 详解

56次阅读

共计 8710 个字符,预计需要花费 22 分钟才能阅读完成。

概述
在《深入聊一聊 Spring AOP 实现机制》一文中,介绍了 Spring AOP 的多种实现机制,原理大同小异。本篇来继续介绍一款开源的 AOP 框架:Nepxion Matrix,该框架致力于对 Spring AOP 的扩展和增强,灵活而且易用。
Matrix 框架主要对 Spring 做了三个模块的扩展:Spring AutoProxy,Spring Registrar,Spring Selectror。
本篇主要分析 AOP 相关的功能,也就是 AutoProxy 模块。主要围绕以下几个方面:
Nepxion Matrix AutoProxy 框架有什么特性?Nepxion Matrix AutoProxyAOP 增强框架的扩展点什么?如何扩展?源码分析。该框架和 Spring AOP 异同点。
一:Nepxion Matrix AutoProxy 特性
大多数的项目中,使用 Spring AOP 的方式都是采用注解形式,方法上面加个自定义注解即可实现,这种方式注解只能加在类方法上,不能加在接口或接口方法上。Nepxion Matrix AutoProxy 主要针对这些问题,特性如下:
支持通用代理和额外代理支持接口代理和类代理支持接口方法代理这里要介绍一下上面提到了两种代理方式:
通用代理是指通过 AbstractAutoProxyCreator 中的变量 interceptorNames,来设置具体的通知名称。额外代理是指通过实现自定义逻辑,来选择性设置通知(这里的通知也就是拦截方法)。
二:Nepxion Matrix AutoProxy 扩展点要理解该框架的实现方式,首先要知道该框架的扩展点是什么。先来看一下代理机制相关的 UML 图:

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,即可满足两个实现方法都会走代理方法。
源码中还有其他几种使用示例,这里就不列举了,具体可以参考项目 wiki。
首先来看一下 AbstractAutoScanProxy 的构造方法:
public AbstractAutoScanProxy(String[] scanPackages, ProxyModeproxyMode, 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, StringbeanName, 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 中,完成代理。
推荐一个 Java 进阶架构学习交流:952124565,群内有分布式架构、高性能、高并发、性能优化、Spring boot、Redis、ActiveMQ、Nginx、Netty、Jvm 等视频资料提供学习参考。
跟进 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); } returnproxyFactory.getProxy(getProxyClassLoader()); }
Nepxion Matrix 重写了上面的 shouldProxyTargetClass(beanClass, beanName) 方法,重写逻辑如下:
protected boolean shouldProxyTargetClass(Class<?> beanClass, StringbeanName) {// 设置不同场景下的接口代理,还是类代理 Boolean proxyTargetClass =proxyTargetClassMap.get(beanName); if (proxyTargetClass != null) {return proxyTargetClass;} returnsuper.shouldProxyTargetClass(beanClass, beanName); }
需要注意的是,上述重写方式只在 SpringBoot 1.x 中生效,因为在 2.x 版本中,proxyFactory.isProxyTargetClass() 默认为 true,默认走 cglib 代理,所以默认情况下上述重写的方法不会执行。
继续跟进获取拦截器的方法 buildAdvisors:
protected Advisor[] buildAdvisors(String beanName, Object[]specificInterceptors) {// 解析通用代理拦截器 Advisor[] commonInterceptors =resolveInterceptorNames(); List<Object> allInterceptors = newArrayList<Object>(); // 判断是否需要额外代理,是否有指定拦截器 if (specificInterceptors!= null) {allInterceptors.addAll(Arrays.asList(specificInterceptors)); if(commonInterceptors.length > 0) {if(this.applyCommonInterceptorsFirst) {allInterceptors.addAll(0,Arrays.asList(commonInterceptors)); } else {allInterceptors.addAll(Arrays.asList(commonInterceptors)); } } } Advisor[] advisors = new Advisor[allInterceptors.size()]; for (int i= 0; i < allInterceptors.size(); i++) {advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i)); } returnadvisors; }
通过调用 resolveInterceptorNames,根据 interceptorNames 中设置的拦截器名称,从 Spring 容器中取出所有的通用代理拦截器,结合指定拦截器 specificInterceptors,一起织入代理类。
Nepxion Matrix AutoProxy 中的方法代理这里就不展开了,原理类似。
四:Nepxion Matrix AutoProxy 和 Spring AOP 异同点
1)代理机制原理一样,都是 AbstractAutoScanProxy 的实现类,只是代理功能点不同。
2)两种代理机制可同时使用。如果同时使用,一定保证 Spring AOP 先代理,Nepxion Matrix AutoProxy 后代理。这也是默认的代理顺序。尽量不要通过重写 Ordered 接口的方式改变先后顺序。
原因是采用 Spring AOP 注解形式时需要获取代理类最初的 Class 对象,如果 Nepxion Matrix AutoProxy 先执行,那么在执行 Spring AOP 代理逻辑时获取到的当前 Class 对象就是被代理过重新生成的 Class 对象,这时就无法获取自定义的切面注解了。

正文完
 0