写点什么

深入理解 Spring 框架之 AOP 子框架

用户头像
邱学喆
关注
发布于: 2021 年 04 月 24 日
深入理解Spring框架之AOP子框架

一. 概述

AOP 是面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,简单来讲就是对 Bean 封装成一个代理,当调用 bean 的方法时,会先调用代理类的方法,代理执行本身的逻辑后在执行 Bean 的方法;通过 AOP 技术,可以将重复的代码放到代理类,而 Bean 只关注它本身的业务逻辑,便以开发,高效。在业内,有关 AOP 的技术,主要是 Java 原生提供的 Proxy 类来创建代理类,以及通过 cglib 来进行创建代理类。java 的动态代理主要主要是创建 Proxy 的子类作为代理类。而 cglib 是通过字节码的形式创建子类继承目标对象从而达到代理类。

二. 原理

现在我们分别查看一下动态代理的底层原理是怎么回事 。

1. java 动态代理

java 原生的动态代理只能支持接口。所以首先第一步是先定义一个接口

public interface JavaInterface {    void display();}
复制代码

第二步是实现 InvocationHandler 接口,

public class JavaDynamicInvocationHandlerImpl implements InvocationHandler {    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println(" welcome to java dynamic! ");        return null;    }}
复制代码

第三步,通过 Proxy 类来创建动态代理对象

JavaInterface instance = (JavaInterface) Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[]{JavaInterface.class}, new JavaDynamicInvocationHandlerImpl());instance.display();
复制代码

java 原生的动态代理,是通过 java 原生的生成代理类,接着创建该类的对象,从而达到代理效果。ProxyGenerator 是 java 动态代理生成代理类的主要注入,有关 ProxyGenerator 介绍,可以上网搜索对其的介绍。我们看一下其生成的类文件是什么样的。

//指定输出文件路径String path = "./JavaInterface.class";byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy"/*随便定义一个类名*/,                                                      new Class[]{JavaInterface.class});try (FileOutputStream fos = new FileOutputStream(path)) {  fos.write(classFile);  fos.flush();  System.out.println("代理类class文件写入成功");} catch (Exception e) {  System.out.println("写文件错误");}//.....
复制代码

我们通过反编译出该 class 文件的内容

public final class $Proxy extends Proxy implements JavaInterface {    private static Method m1;    private static Method m3;    private static Method m2;    private static Method m0;
public $Proxy(InvocationHandler var1) throws { super(var1); }
public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } }
public final void display() throws { try { //h -> 我们创建的JavaDynamicInvocationHandlerImpl拦截器 super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m3 = Class.forName("dynamic.JavaInterface").getMethod("display"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } }}
复制代码

2. cglib 动态代理

第一步,先创建一个类:

public class JavaClass {  public void display1() {    System.out.println(" welcome to cglib11111!");  }  public void display2() {    System.out.println(" welcome to cglib222222!");  }}
复制代码

第二步,创建拦截器,只要是 Callback 的实现即可。这里我们创建两个拦截器

public class InvocationHandlerImpl implements InvocationHandler {    private Object target;
public InvocationHandlerImpl(Object target) { this.target = target; }
@Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { System.out.println(" cglib invoke 11111before.... "); Object obj = method.invoke(target, objects); System.out.println(" cglib invoke 11111after.... "); return null; }}
public class InvocationHandlerImpl2 implements InvocationHandler { private Object target;
public InvocationHandlerImpl2(Object target) { this.target = target; }
@Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { System.out.println(" cglib invoke 2222before.... "); Object obj = method.invoke(target, objects); System.out.println(" cglib invoke 22222after.... "); return null; }}
复制代码

第三步,通过 cglib 来创建代理对象

//添加这个系统变量,是为了将cglib生成的类文件,进行查看里面的结构System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "src\\keep\\learning\\cglib");Enhancer enhancer = new Enhancer();enhancer.setSuperclass(JavaClass.class);enhancer.setCallbacks(new Callback[]{new InvocationHandlerImpl(new JavaClass()), new InvocationHandlerImpl2(new JavaClass())});enhancer.setCallbackFilter(new CallbackFilter() {  @Override  public int accept(Method method) {    if(method.getName().equals("display1")){      return 0;    }else{      return 1;    }  }});JavaClass javaClass = (JavaClass) enhancer.create();
复制代码

cglib 生成的类文件如下:

//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//
package dynamic;
import java.lang.reflect.Method;import org.springframework.cglib.proxy.Callback;import org.springframework.cglib.proxy.Factory;import org.springframework.cglib.proxy.InvocationHandler;import org.springframework.cglib.proxy.UndeclaredThrowableException;
public class JavaClass$$EnhancerByCGLIB$$80b8e9ce extends JavaClass implements Factory { private boolean CGLIB$BOUND; public static Object CGLIB$FACTORY_DATA; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private InvocationHandler CGLIB$CALLBACK_0; private InvocationHandler CGLIB$CALLBACK_1; private static Object CGLIB$CALLBACK_FILTER; private static final Method CGLIB$display1$0; private static final Method CGLIB$display2$1; private static final Method CGLIB$equals$2; private static final Method CGLIB$toString$3; private static final Method CGLIB$hashCode$4; private static final Method CGLIB$clone$5;
static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal(); CGLIB$display1$0 = Class.forName("dynamic.JavaClass").getDeclaredMethod("display1"); CGLIB$display2$1 = Class.forName("dynamic.JavaClass").getDeclaredMethod("display2"); CGLIB$equals$2 = Class.forName("java.lang.Object").getDeclaredMethod("equals", Class.forName("java.lang.Object")); CGLIB$toString$3 = Class.forName("java.lang.Object").getDeclaredMethod("toString"); CGLIB$hashCode$4 = Class.forName("java.lang.Object").getDeclaredMethod("hashCode"); CGLIB$clone$5 = Class.forName("java.lang.Object").getDeclaredMethod("clone"); }
public final void display1() { try { InvocationHandler var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; }
var10000.invoke(this, CGLIB$display1$0, new Object[0]); } catch (Error | RuntimeException var1) { throw var1; } catch (Throwable var2) { throw new UndeclaredThrowableException(var2); } }
public final void display2() { try { InvocationHandler var10000 = this.CGLIB$CALLBACK_1; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_1; }
var10000.invoke(this, CGLIB$display2$1, new Object[0]); } catch (Error | RuntimeException var1) { throw var1; } catch (Throwable var2) { throw new UndeclaredThrowableException(var2); } }
public final boolean equals(Object var1) { try { InvocationHandler var10000 = this.CGLIB$CALLBACK_1; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_1; }
return (Boolean)var10000.invoke(this, CGLIB$equals$2, new Object[]{var1}); } catch (Error | RuntimeException var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
//.......
public JavaClass$$EnhancerByCGLIB$$80b8e9ce() { CGLIB$BIND_CALLBACKS(this); }
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) { CGLIB$THREAD_CALLBACKS.set(var0); }
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) { CGLIB$STATIC_CALLBACKS = var0; } //很关键的私有,几个CallBack,通过CallBackFilter进行匹配,从而在对应的方法中选择对应的拦截器。 private static final void CGLIB$BIND_CALLBACKS(Object var0) { JavaClass$$EnhancerByCGLIB$$80b8e9ce var1 = (JavaClass$$EnhancerByCGLIB$$80b8e9ce)var0; if (!var1.CGLIB$BOUND) { var1.CGLIB$BOUND = true; Object var10000 = CGLIB$THREAD_CALLBACKS.get(); if (var10000 == null) { var10000 = CGLIB$STATIC_CALLBACKS; if (var10000 == null) { return; } }
Callback[] var10001 = (Callback[])var10000; var1.CGLIB$CALLBACK_1 = (InvocationHandler)((Callback[])var10000)[1]; var1.CGLIB$CALLBACK_0 = (InvocationHandler)var10001[0]; }
}
public Object newInstance(Callback[] var1) { CGLIB$SET_THREAD_CALLBACKS(var1); JavaClass$$EnhancerByCGLIB$$80b8e9ce var10000 = new JavaClass$$EnhancerByCGLIB$$80b8e9ce(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null); return var10000; }
public Object newInstance(Callback var1) { throw new IllegalStateException("More than one callback object required"); }
public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) { CGLIB$SET_THREAD_CALLBACKS(var3); JavaClass$$EnhancerByCGLIB$$80b8e9ce var10000 = new JavaClass$$EnhancerByCGLIB$$80b8e9ce; switch(var1.length) { case 0: var10000.<init>(); CGLIB$SET_THREAD_CALLBACKS((Callback[])null); return var10000; default: throw new IllegalArgumentException("Constructor not found"); } }
public Callback getCallback(int var1) { CGLIB$BIND_CALLBACKS(this); InvocationHandler var10000; switch(var1) { case 0: var10000 = this.CGLIB$CALLBACK_0; break; case 1: var10000 = this.CGLIB$CALLBACK_1; break; default: var10000 = null; }
return var10000; }
public void setCallback(int var1, Callback var2) { switch(var1) { case 0: this.CGLIB$CALLBACK_0 = (InvocationHandler)var2; break; case 1: this.CGLIB$CALLBACK_1 = (InvocationHandler)var2; }
}
public Callback[] getCallbacks() { CGLIB$BIND_CALLBACKS(this); return new Callback[]{this.CGLIB$CALLBACK_0, this.CGLIB$CALLBACK_1}; }
public void setCallbacks(Callback[] var1) { this.CGLIB$CALLBACK_0 = (InvocationHandler)var1[0]; this.CGLIB$CALLBACK_1 = (InvocationHandler)var1[1]; }
static { CGLIB$STATICHOOK1(); }}
复制代码

为了加深对 cglib 的使用,我们对 Enhancer 暴露出来的方法进行解读

  • setSuperclass 设置继承父类,在我们例子当中,就是 JavaClass 类

  • setInterfaces 设置实现的接口。

  • setCallbacks 设置多个回调对象。

  • setCallbackFilter 设置回调过滤器。如果回调对象是多个,则需要设置回调过滤器,从而进行匹配,哪些方法对应哪个回调对象。如果只有一个,则无需设置回调过滤器。

  • create 创建对象,在例子当中,就是 JavaClass$$EnhancerByCGLIB$$80b8e9ce 类的创建

  • createClass 创建该类,在例子当中,返回的是 JavaClass$$EnhancerByCGLIB$$80b8e9ce 类

3. spring 集成

在 spring 创建代理,主要的入口是 ProxyCreatorSupport 的子类。类图如下

我们主要探讨的 SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference 调用的 ProxyFactory。

  • ProxyConfig 的属性

  • proxyTargerClass 如果目标类是被代理的,则设置为 true,会尽量用 CGLIB 来创建代理对象,除非目标类是接口,则还是依然用 JAVA 的动态代理。代码逻辑如下:

//proxyTargerClass 设置 //AbstractAutoProxyCreator类protected Object createProxy(Class<?> beanClass, @Nullable String beanName,			@Nullable Object[] specificInterceptors, TargetSource targetSource) {  ProxyFactory proxyFactory = new ProxyFactory();  proxyFactory.copyFrom(this);  if (!proxyFactory.isProxyTargetClass()) {    if (shouldProxyTargetClass(beanClass, beanName)) {      //如果目标类是被代理的,则设置为true.      proxyFactory.setProxyTargetClass(true);    }    else {      //否则遍历目标类的接口,如果没有实现的接口,则设置为true。否则不设置      evaluateProxyInterfaces(beanClass, proxyFactory);    }  }  //....  return proxyFactory.getProxy(getProxyClassLoader());}// 代理方式选择//DefaultAopProxyFactory类public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {  if (config.isOptimize() || config.isProxyTargetClass()       || hasNoUserSuppliedProxyInterfaces(config)) {    Class<?> targetClass = config.getTargetClass();    //....    //如果目标类是接口,则用java的动态代理    if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {      return new JdkDynamicAopProxy(config);    }    return new ObjenesisCglibAopProxy(config);  }  else {    return new JdkDynamicAopProxy(config);  }}
复制代码
  • optimize 优化标志,如果为 true,则会尝试用 cglib 来创建代理,除非目标类是接口

  • opaque 是否透明标志,如果设置设置为 true,会添加 advice 接口,暴露 advice 对象。

  • exposeProxy 是否是暴露代理,如果设置为 true,主要的逻辑是将代理对象放到线程局部变量中。在 CGLIB 中,则会选择对应的拦截器 DynamicUnadvisedExposedInterceptor 或者是 StaticUnadvisedExposedInterceptor 类中看到对应的逻辑;如果是 JAVA 原生的,则会添加对应的判断,将代理对象保存到线程局部变量中。AopContext 类就是存放代理代理对象的地方。

  • frozen 冻结标志,配置相关的修改都不会被允许。

  • AdvisedSupport

  • targetSource 目标对象

  • preFiltered 是否已经过滤后的 advisor。如果是 true,则会在拦截器中匹配回调对象时,会跳过对类的资格校验。否则会继续对类的资格进行校验。

  • advisorChainFactory advisor 拦截器链工厂,用来创建 advisor 链。

  • interfaces 实现的接口列表

  • advisors advisor 列表

  • ProxyCreatorSupport

  • aopProxyFactory 代理模式的工厂,用来创建具体的 aop 代理,主要是 java 的动态代理,还是 cglib 动态代理。

  • listeners 监听器,监听当前的对象激活激活事件,以及 advisor 发生变更事件;另外也是对当前对象修改。

  • active 激活标志

具体的代理对象跟上面的 java 的动态代理和 cglib 动态代理的原理差不多,可以去看 cglibAopProxy 和 JdkDynamicAopProxy 类的逻辑。

在 cglibAopProxy 里,重点需要看 ProxyCallbackFilter 的逻辑,该 Filter 主要是对目标方法进行过滤,哪些方法由哪个 callback 来处理。而 callback 接口主要是对目标方法的拦截进行对应的切面。下面列一下关键的实现类。

  • LookupOverrideMethodInterceptor 针对方法含有 @Lookup 注解的拦截,这一块在spring 的 IOC 使用以及原理有讲述

  • EqualsInterceptor 针对 equals 方法的拦截

  • StaticUnadvisedInterceptor 直接调用目标对象的方法,并对返回的对象进行简单的处理

  • DynamicAdvisedInterceptor 主要找出匹配该方法的拦截器,并形成 ReflectiveMethodInvocation 对象,该对象可以理解的职责链模式下逐步执行拦截器,最后才执行目标方法。

  • FixedChainStaticTargetInterceptor 针对的是目标方法是静态且是被冻结住的拦截器。该逻辑与 DynamicAdvisedInterceptor 差不多。

JdkDynamicAopProxy 里,重点看 JdkDynamicAopProxy 的 invoke 方法。主要的逻辑对方法进行判断,从而选择对应的方法。其中比较重要的是会从匹配成功的 Advisor 列表中找出方法匹配到的拦截器 Advice 封装成 MethodInterceptor 接口的子类对象,然后形成 ReflectiveMethodInvocation 对象,并执行拦截处理。

我们先看一下是匹配逻辑将在 aop 框架里进行讲述,而如何封装成 MethodInterceptor 对象的,进行讲解。

主要是通过 AdvicedSupport.getInterceptorsAndDynamicInterceptionAdvice 进入,最终调用 AdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice 方法进行封装。代码如下:

//AdvisorChainFactory类public List<Object> getInterceptorsAndDynamicInterceptionAdvice(			Advised config, Method method, @Nullable Class<?> targetClass) {		List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());		boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();		for (Advisor advisor : config.getAdvisors()) {			if (advisor instanceof PointcutAdvisor) {				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;        //匹配逻辑				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {          //这里是将Advice封装成MethodInterceptor对象。					MethodInterceptor[] interceptors = registry.getInterceptors(advisor);					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();					if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {            //匹配成功						if (mm.isRuntime()) {              //如果是运行时拦截器,则封装成InterceptorAndDynamicMethodMatcher对象.              //是为了对运行时对方法的入参进行校验。							for (MethodInterceptor interceptor : interceptors) {								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));							}						}						else {              //直接添加列表中							interceptorList.addAll(Arrays.asList(interceptors));						}					}				}			}			else if (advisor instanceof IntroductionAdvisor) {				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;        //匹配逻辑				if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {          //将Advice封装成MethodInterceptor对象。					Interceptor[] interceptors = registry.getInterceptors(advisor);					interceptorList.addAll(Arrays.asList(interceptors));				}			}			else {        //将Advice封装成MethodInterceptor对象。				Interceptor[] interceptors = registry.getInterceptors(advisor);				interceptorList.addAll(Arrays.asList(interceptors));			}		}		return interceptorList;	}//具体对Advice封装成MethodInterceptor对象//DefaultAdvisorAdapterRegistry类public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {  List<MethodInterceptor> interceptors = new ArrayList<>(3);  Advice advice = advisor.getAdvice();  if (advice instanceof MethodInterceptor) {    //如果是MethodInterceptor的子类,则无需封装,直接保存    interceptors.add((MethodInterceptor) advice);  }  for (AdvisorAdapter adapter : this.adapters) {    //AdvisorAdapter接口对Advisor进行转成MethodInterceptor子类。    //AdvisorAdapter在spring容器有三种默认的子类。    if (adapter.supportsAdvice(advice)) {      interceptors.add(adapter.getInterceptor(advisor));			    }      }  if (interceptors.isEmpty()) {    throw new UnknownAdviceTypeException(advisor.getAdvice());  }  return interceptors.toArray(new MethodInterceptor[0]);}//MethodBeforeAdviceAdapter类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);  }  }//ThrowsAdviceAdapter类class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {  @Override  public boolean supportsAdvice(Advice advice) {    return (advice instanceof ThrowsAdvice);  }    @Override  public MethodInterceptor getInterceptor(Advisor advisor) {    return new ThrowsAdviceInterceptor(advisor.getAdvice());  }}//AfterReturningAdviceAdapter类class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {	@Override	public boolean supportsAdvice(Advice advice) {		return (advice instanceof AfterReturningAdvice);	}
@Override public MethodInterceptor getInterceptor(Advisor advisor) { AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice(); return new AfterReturningAdviceInterceptor(advice); }}
复制代码

我们对 ReflectiveMethodInvocation 进行解读,其子类就不再进行解读。可自行去查看。

ReflectiveMethodInvocation 的属性

  • proxy 代理对象

  • target 目标对象

  • method 目标对象方法

  • arguments 目标对象方法的入参

  • targetClass 目标对象类

  • userAttributes 保存用户自定属性,

  • interceptorsAndDynamicMethodMatchers 拦截器列表,是 MethodInterceptor 对象列表

  • currentInterceptorIndex 当前拦截器角标

执行拦截器的入口为 process,代码如下:

//ReflectiveMethodInvocation类public Object proceed() throws Throwable {  //如果当前拦截器已经到尾部,则说明所有的拦截器已经执行完,直接调用目标对象方法。  if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {    return invokeJoinpoint();  }  //获取当前拦截器  Object interceptorOrInterceptionAdvice =    this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);  if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {    //如果是动态拦截器,则需要再次判断目标方法是否需要被拦截的。    InterceptorAndDynamicMethodMatcher dm =      (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;    if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {      //如果匹配成功,则调用该动态拦截器      return dm.interceptor.invoke(this);    }    else {      //如果匹配不成功,则递归再次调用下一个拦截器进行处理      return proceed();    }  }  else {    //如果不是动态拦截其,则直接调用该拦截器进行处理    return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);  }}protected Object invokeJoinpoint() throws Throwable {  return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);}//AopUtilspublic static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)			throws Throwable {  //....  ReflectionUtils.makeAccessible(method);  return method.invoke(target, args);  //....}
复制代码

三. AOP 框架

上面讲述了代理创建的原理,以及 spring 对它们的封装。但并没有涉及到拦截器是如何对目标对象、方法进行匹配以及绑定的原理。在 spring 的 AOP 框架里,主要是围绕几个大接口,Pointcut、Advice、Advisor 接口进行对目标对象、方法进行匹配以及绑定。Pointcut 是负责对目标对象、方法进行匹配逻辑。而 Advisor 是将 Advice 与目标对象、方法绑定,而 Advice 是拦截器,在执行目标方法前后执行拦截器的代码。而主入口主要是 AbstractAutoProxyCreator 的 getEarlyBeanReference 方法和 postProcessBeforeInstantiation 方法。

1. Pointcut 接口

Pointcut 接口的主要类图关系。

  • TruePointcut 是比较简单粗暴的,对目标对象、方法不进行校验,一律返回 true。基于这个,可以写通用的拦截器;

  • CacheOperationSourcePointcut 是对缓存 @Cache 等注解进行校验匹配。

  • TransactionAttributeSourcePointcut 是对 @Transaction 注解进行校验匹配

  • AnnotationMatchingPointcut 对目标对象、方法进行校验,是否含有指定的注解

  • AspectJExpressionPointcut 是 spring 集成 aspectj 的校验器,充当 AspectJ 在 spring 的适配器,匹配逻辑最终交给 AspectJ 框架进行校验匹配。

  • NameMatchMethodPointcut 是对方法名进行校验匹配

而 Pointcut 中的 ClassFilter 与 MethodMatcher 接口,是真正的对目标对象、方法进行校验匹配的接口,即入口。Pointcut 只是充当一个门面。想了解 ClassFilter 接口与 MethodMatcher 是如何对对象类以及方法进行匹配的,可以看看对应的实现类。常用的 ClassFilter 的实现类,AnnotationClassFilter 判断是否含有该注解,RootClassFilter 判断目标对象是不是某个类的子类。常用的 MethodMatcher 的实现类,AnnotationMethodMatcher 判断该方法是否含有对应的注解,TransactionAttributeSourcePointcut 与 CacheOperationSourcePointcut 是用额外的一个对象来缓存目标方法的配置信息。如果含有,则匹配成功,否则不成功。

我将讲述两个比较复杂的 Pointcut 逻辑。一个是 TransactionAttributeSourcePointcut 一个是 AspectJExpressionPointcut 类是如何匹配的。

1.1 TransactionAttributeSourcePointcut

相关的主要类图如上,从上面可以看出以及借鉴代码的大体逻辑。对类进行匹配时,永远都是返回 true,既是说事务是针对方法的,并不是针对类的;接着对方法进行匹配时,由于 TransactionAttributeSourcePointcut 是一个抽象类,需要子类去实现 getTransactionAttributeSource 方法接口,返回 TransactionAttributeSource 实现类。TransactionAttributeSource 的逻辑是如果在 attributeCache 中找不到时,会尝试对该方法进行解析,解析含有 @Transactional 等注解,具体解析的交由 TransactionAnnotationParser 接口的实现类进行解析,如果解析没有 @Transactonal 等注解,会以一个默认的空对象去存放,以便下次检索时,直接返回,不再进行解析。

1.2 AspectJExpressionPointcut

有关对类以及方法的主要入口对象是 PointcutExpression 的实现类 PointcutExpressionImpl。该类是对 pointcut 表达式进行解析,解析的入口是 PointcutParser.parsePointcutExpression 方法。解析出来的对象是 Pointcut 抽象类的子类,由于 spring 只使用了 AspectJ 框架的部分逻辑,所以 Pointcut 的实现类主要是 OrPointcut、AndPointcut、NotPointcut、KindedPointcut、WithinAnnotationPointcut、AnnotationPointcut、ThisOrTargetPointcut、ArgsPointcut、ArgsAnnotationPointcut、ThisOrTargetAnnotationPointcut、ArgsAnnotationPointcut、WithinAnnotationPointcut。而 Pointcuct 的组成结构结构主要是 TypePattern 抽象类的子类。

当调用 PointcutExpressionImpl 对象对目标类进行匹配时,会调用 couldMatchJoinPointsInType 方法进行匹配,其会调用 Pointcut 的 fastMatch 方法进行匹配,里面有可能会调用 TypePattern 类中的 matchesStatically 方法。

当调用 PointcutExpressionImpl 对象对目标方法进行匹配时,会调用 matchesMethodExecution 方法进行匹配,其会调用 match 方法进行匹配。里面需要的注意的是,会解析 pointcut 表达式中有关需要传递给 Advice 方法中的入参,其内容最终会保存到 ReflectiveMethodInvocation 的 userAttributes 属性当中。便以运行时期,对方法的入参进行绑定。

上面列了 AspectJ 关键的一些类,只是冰山一角。想要深入了解,需梳理清楚其数据结构,PatternNode 抽象及其子类,UnresolvedType 以及其子类。Shadow 及其子类等。

其底层原理是对方法名进行匹配,以及类型匹配,入参匹配,注解匹配等逻辑。

这里的逻辑比较复杂,粗略说一下以及列一些主要的类以及方法,自行去阅读。这里不再解读。后续有可能会针对 Aspect 框架进行解读输出对应的文章。

2. Advice

以上有关 Advice 的子接口类图关系。

实现类过多,就不再图上进行展示,简单说一下我们比较熟悉常用的几个 Advice

  • TransactionInterceptor 有关事务的拦截器

  • CacheInterceptor 有关 @Cache 注解标注的缓存拦截器

  • AsyncExecutionInterceptor 有关 @Async 注解标注的异步拦截器

  • EventPublicationInterceptor 有关事件触发的拦截器

  • MethodValidationInterceptor 有关对方法入参进行校验的拦截器

  • PersistenceExceptionTranslationInterceptor 有关持久化异常处理的拦截器

  • PerformanceMonitorInterceptor 有关性能监控的拦截器

3. Advisor

4. 总结

上面列出了 Spring AOP 框架的主要的接口,至于它们是如何协同还没进行解读。以及拦截器的入参都有哪些对象等。下面将细细道来。从 AbstractAutoProxyCreator 的 getEarlyBeanReference 方法入手。

public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {	//生成一个缓存key保存起来,为了避免重复创建代理用的  Object cacheKey = getCacheKey(bean.getClass(), beanName);  if (!this.earlyProxyReferences.contains(cacheKey)) {    this.earlyProxyReferences.add(cacheKey);  }  //对目标对象封装,创建代理对象  return wrapIfNecessary(bean, beanName, cacheKey);}protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {  //targetSourcedBeans记录着是不需要进行代理的对象,如果包含,则不对它进行创建代理  if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {    return bean;  }  //advisedBeans是缓存着不需要创建代理对象的对象  if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {    return bean;  }  if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {    //如果对象类是spring基础类,则不需要创建代理    this.advisedBeans.put(cacheKey, Boolean.FALSE);    return bean;  }    //从spring里获取匹配到Advisor对象  Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);  if (specificInterceptors != DO_NOT_PROXY) {    //保存该对象是需要代理的标志    this.advisedBeans.put(cacheKey, Boolean.TRUE);    //创建代理对象    Object proxy = createProxy(      bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));    this.proxyTypes.put(cacheKey, proxy.getClass());    return proxy;  }  //如果执行到这里,则说明该对象不需要进行创建代理对象  this.advisedBeans.put(cacheKey, Boolean.FALSE);  return bean;}
复制代码

createProxy 方法创建代理的逻辑跟上面讲述过,不再讲述。而 getAdvicesAndAdvisorsForBean 方法就是有关三大接口如何协同的具体逻辑。

第一步,从 spring 容器获取 Advisor 类型的对象列表,最终的代码是在 BeanFactoryAdvisorRetrievalHelper 类的 findAdvisorBeans 方法。

第二步,对 Advisor 列表进行匹配校验,最终的是由 AopUtils.findAdvisorsThatCanApply 来做校验。就会获取该 Pointcut 接口的 ClassFilter 的实现进行对目标对象校验。

其中有一个比较特殊的就是有关 spring 集成 AspectJ 的,它不是以 Advisor 对象保存到 spring 容器里面的。具体的代码实在 AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors 方法

protected List<Advisor> findCandidateAdvisors() {  // 从spring容器里找到Advisor类型的对象列表  List<Advisor> advisors = super.findCandidateAdvisors();    if (this.aspectJAdvisorsBuilder != null) {    //遍历spring中所有含有@Aspect注解对象进行解析,    //解析该对象的@Pointcut、@Before、@Around、@After、@AfterReturning、@AfterThrowing等标注的方法    //创建Advisor接口的实现InstantiationModelAwarePointcutAdvisorImpl    //Pointcut接口的实现为AspectJExpressionPointcut    advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());  }  return advisors;}
复制代码

我们具体看一下 spring 框架是如何对含有 @AspectJ 注解的对象是如何创建 Advisor 对象的。主要的入口是 BeanFactoryAspectJAdvisorsBuilder 对象中的 buildAspectJAdvisors 方法。其大概的逻辑是遍历 spring 容器的所有对象,对含有 @Aspect 注解的类的对象,进行封装 Advisor 实现类 InstantiationModelAwarePointcutAdvisorImpl 对象,以及该对象中的 Pointcut 实现类 AspectJExpressionPointcut。Advice 实现类,针对 @Before 的为 AspectJMethodBeforeAdvice 类,@After 的 AspectJAfterAdvice 类,@AfterReturning 的 AspectJAfterReturningAdvice 类,@AfterThrowing 的 AspectJAfterThrowingAdvice 类,@Around 的 AspectJAroundAdvice 类。

四. AspectJ 用法

1. Pointcut 表达式

AspectJ 有很多种切面构造函数,方法,属性等形式的切面,然而 Spring 只支持方法层面切面。所以 spring 集成 AspectJ 时,只支持了方法,方法注解,入参,入参注解,类、类的注解等匹配机制。其语法如下:

//基本单位是Pointcut,Pointcut又可以进行逻辑操作,如not等操作。Pointcut之间PointcutExpression := [(!|not)] Pointcut ((and|or|&&|'||') [(!|not)]Pointcut)Pointcut := execution(MethodPattern) |						this(Type or Id) | /**目标对象类**/            target(Type or Id) | /**目标对象类**/            within(TypePattern) | /**目标对象类**/            args(Type or Id, ...) | /**方法中参数类型**/            @target(Type or Id) | /**方法所归属的类包含有的注解**/            @args(Type or Id, ...) | /**方法中入参对象类所包含的注解**/            @within(Type or Id) /**目标对象类所包含的注解**/方法签名 := [修饰符] 返回类型 包名.类名.方法名(入参类型)[throws 异常类型]MethodPattern := [ModifiersPattern] TypePattern [TypePattern . ] IdPattern (TypePattern | ".." , ... )         [ throws ThrowsPattern ]TypePattern := SimpleTypePattern |							 '!' TypePattern |							 '(' AnnotationPattern? TypePattern ')' 							 TypePattern '&&' TypePattern |							 TypePattern '||' TypePattern SimpleTypePattern := DottedNamePattern '+'? '[]'* 	  	DottedNamePattern := FullyQualifiedName RestOfNamePattern? |					 					 '*' NotStarNamePattern?RestOfNamePattern := '..' DottedNamePattern | 		  	             '*' NotStarNamePattern?NotStarNamePattern := FullyQualifiedName RestOfNamePattern? |		                 '..' DottedNamePattern               FullyQualifiedName := JavaIdentifierCharacter+ ('.' JavaIdentifierCharacter+)*  AnnotationPattern := '@' qualified-name |										 '@' TypePatternType := 全限定类名Id := 方法中的参数名称, 当使用该ID时,aspect框架会将对应的对象传入方法中
复制代码

有关 TypePattern 进行讲解,例如某类是 com.michael.aop.Passenger。要想匹配该类型,可以有如下几种".."后面必须有 java 标识符或者"*"表达式:

  • com.michael.aop.Passenger 精确匹配

  • com..* com 包下的所有类

  • com..*.Passenger com 包下的 Passenger 类型

  • com..*.Pas* com 包下的前缀为 Pas 的类型

  • com.*.*.Pas* com 包下的第三层级下前缀为 Pas 类型

  • execution 对方法签名进行匹配,与 java 对类中方法定义的语法大同小异。

方法名 IdPattern 进行讲解,例如某个方法是 getPassenger。要想匹配成功,可以有如下几种

  • getPassenger 精确匹配

  • *

  • get*

相关的语法规则如下,相关的官网地址:Chapter 2. AnnotationsLanguage Semantics,感觉有点不是很清晰,自己稍微总结,并不保证是全面的。相关的例子,可以在spring-practice仓库看里面的例子。

经过上面有关 AspectJ 的梳理以及代码的练习,可以得出,AspectJ 是可以对方法的签名进行匹配(execution),以及方法中的入参以及入参对象注解(args 和 @args),方法标注的注解进行匹配(@annotation),以及方法所归属的对象以及包含的注解进行匹配(target 和 @target,@within),对方法所归属的对象类进行模糊比对(within)。而 this 我们尽量不使用至于 target 与 within 的区别是前者是精确匹配,后者是模糊匹配。而 @target 与 @within,个人理解是无区别的。(如果有大佬能指出其错误的依据,将不胜感激)

有关对 spring 集成 AspectJ 的例子,在我的仓库里。后续会梳理我对 AspectJ 整体框架的认识

2. Advice

  • @AfterReturning 接收返回内容

@AfterReturning(value = "execution(String com.michael..getName())",returning = "returnValue")public void afterReturning(String returnValue){    System.out.println("@AfterReturning(value=\"execution(String com.michael..getName())\",returning = \"returnValue\"");    System.out.println("@AfterReturning returnValue = " + returnValue);}
复制代码
  • @Around 方法中需要申请一个 ProceedingJoinPoint 入参,这样子才能调用目标方法

@Around(value = "execution(String com.michael..getName())")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {    System.out.println("@Around(value = \"execution(String com.michael..getName())\")");    System.out.println("@Around before");    Object proceed = proceedingJoinPoint.proceed();    System.out.println("@Around after");    return proceed;}
复制代码
  • @AfterThrowing 与 @AfterReturning 使用方式差不多。不再阐述,而且日常开发过程中,很少用到。

五. 总结

spring 的 aop 框架是基于 Bean 创建过程的暴露出来的 Hook 进行创建代理的,而创建代理对象的抽象类是 AbstractAutoProxyCreator,大部分逻辑在该抽象类实现,只有部分的特性在其子类。其子类如下:

  • BeanNameAutoProxyCreator 针对对象名进行匹配,从而创建代理对象。但其在 spring 中,没有看到该类的具体场景是什么样的。可以忽略该类

  • DefaultAdvisorAutoProxyCreator 主要是根据过滤规则(前缀匹配规则)进行帅选含有前缀的过滤器。默认情况,是没有过滤规则的。

  • AspectJAwareAdvisorAutoProxyCreator 拓展一个 ExposeInvocationInterceptor 拦截器来将 MethodInvocation 对象放入线程局部变量当中。

  • AnnotationAwareAspectJAutoProxyCreator 会检索 spring 容器里的含有 @Apsect 注解的类进行创建 Advisor;

  • InfrastructureAdvisorAutoProxyCreator 过滤掉不是”基础角色“的过滤器。@Role(2)标注该类。

我们一般情况下,使用 DefaultAdvisorAutoProxyCreator,或者有 @AspectJ 包下会注入 AspectJAwareAdvisorAutoProxyCreator 类。

创建代理之前,需要将目标对象与拦截器绑定,其入口是 Advisor 接口。对目标对象判断是否满足指定规则,其入口是 Pointcut。然而 Pointcut 是基于门面模式下设计的,主要的执行者是 ClassFilter 与 MethodMatcher 接口的实现类。

在创建代理期间会调用 ClassFilter 进行对目标对象进行匹配。成功后,将 Advisor 列表保存到 ProxyFactory 对象中的 advisors 属性当中。调用 ProxyFactory 创建代理对象。

在运行期间,调用目标方法时,会先执行代理对象。代理对象会根据目标方法从 Advisor 列表中找出与目标方法匹配好的 Advice,然后将 advice 对象封装成 MethodInterceptor 接口的实现。如果是拦截器是运行时期校验的,则会对入参对象进行校验。在这里 AspectJ 校验过程当中会解析相关的 Advice 方法的入参绑定。

然后再次封装 ReflectiveMethodInvocation 职责链,逐一执行拦截器的动作。

上面都是基于 Spring 创建 Bean 过程中的创建代理对象的。spring 也提供了另外一种形式,直接创建代理对象,其主要的类是 ProxyFactoryBean。通过它我们可以直接创建代理对象,注入到 spring 容器里面。


发布于: 2021 年 04 月 24 日阅读数: 39
用户头像

邱学喆

关注

计算机原理的深度解读,源码分析。 2018.08.26 加入

在IT领域keep Learning。要知其然,也要知其所以然。原理的爱好,源码的阅读。输出我对原理以及源码解读的理解。个人的仓库:https://gitee.com/Michael_Chan

评论

发布
暂无评论
深入理解Spring框架之AOP子框架