点击上方 “方志朋”,选择“置顶或者星标”
你的关注意义重大!
概述
在《深入聊一聊 Spring AOP 实现机制》一文中,介绍了 Spring AOP 的多种实现机制,原理大同小异。本篇来继续介绍一款开源的 AOP 框架: Nepxion Matrix,该框架致力于对 Spring AOP 的扩展和增强,灵活而且易用。
GitHub:https://github.com/Nepxion/Matrix
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);}
@Servicepublic 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, 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<Object> allInterceptors = new ArrayList<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)); } return advisors; }
通过调用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 对象,这时就无法获取自定义的切面注解了。
-更多文章-
JVM性能调优监控工具jps、jstack、jmap、jhat、jstat、hprof使用详解
-关注我-
