基于JDK动态代理实现的Spring AOP源码学习

    xiaoxiao2023-10-15  36

    最近花了点时间学习了一下Spring Aop的内容,到此简单做一下笔记。如果存在错误还望多多指教。

     

    概念

    所谓的Aop就是Aspect Oriented Programming,即面向切面编程。对于Aop存在一个组织AOP Alliance,该组织制定了一下跟Aop有关的标准,制定的只是标准,没有实现。通常在Aop中主要有如下术语:

    1:Aspect是切面,是面向切面编程的一个抽象,例如面向对象编程中抽象的Class。Aspect由Pointcut与Advice组成。 2:JoinPoint,是切入点,例如对系统的login方法切入记录日志功能,此时login方法就是切入点。 3:Pointcut,切面,是用于描述切入点的一个表达式。例如:execution(* com.javartisan.spring.jdbc.service..*.*(..) 就是一个切面,该表达式是joinpoint集合的抽象表示,表述的是该表下面的所有方法都是joinpoint。 4:Advice,是切入逻辑,也就是增强的逻辑。例如对于login方法添加记录日志功能,记录日志的功能就是advice。 5:织入:将Advice添加到Joinpoint所在位置就是织入。 6:织入器,就是负责将Advice织入到Joinpoint位置的工具。在Spring中为ProxyFactory。

    如果上面概念比较难懂,可以简单看一下下面的简单类比,例如你所居住的小区,如果物业需要对小区内每个家庭进行出入等登记,此时登记逻辑就是Advice。由于一些原因物业可能只需要对小区内楼号是奇数的居民进行登记,此时,奇数楼号就是Pointcut,而奇数楼号里面的每一个家庭的出入就是Joinpoint。此时对应的Aspect定义伪代码如下:

    aspect 小区出入登记Aspect{ point :所居住的楼号是奇数 advice:对居民进行出入登记 }

    Spring中的Aop

    在Spring中Aop有两种实现方式,分别是基于JDK动态代理的实现方式(基于接口的子类实现方式),还有就是基于Cglib字节码生成子类的动态代理。在Spring实现类分别是:JdkDynamicAopProxy与CglibAopProxy,这两个实现类都继承至AopProxy,继承图:

    其中的ObjenesisCglibAopProxy是解决Cglib代理实例化代理类时无合适构造器失败的问题,如果不同可以先了解一下Objenesis类库的作用,之后就会很容易理解。可以参考:Objenesis入门参考。本文主要简单记录一下Spring Aop是如何基于JDK代理实现的。

     

    JDK动态代理实现思路

    JDK动态代理实现主要是基于JDK的两个类分别是:InvocationHandler与Proxy两个类;通过这两个名字就很容易理解他们的职责。InvocationHandler翻译为中文名字是调用处理器,其实就是方法调用处理器,职责就是负责方法调用的,该类只有一个方法:

    方法都三个参数,第一个参数为代理,第二个参数为要调用的方法,第三个参数为方法的参数,因此在我们实现此类时候需要将targetObject实例set到此类中作为一个成员变量用于方法调用,而我们的增强逻辑也就是在invoke方法中实现。另一个类Proxy就是代理,它负责生成代理类暴露出去供方法调用。具体可以参考:JDK动态代理。

     

    Spring  JdkDynamicAopProxy实现

    测试代码:

    import com.javartisan.spring.advised.listener.CustomAdvisedSupportListener; import com.javartisan.util.Utils; import org.junit.Before; import org.junit.Test; import org.springframework.aop.framework.ProxyFactory; import org.springframework.aop.support.NameMatchMethodPointcutAdvisor; import sun.misc.Unsafe; import java.io.IOException; import java.lang.reflect.Field; /** * Unit test for simple ProxyFactory. */ public class ProxyFactoryTest { private ProxyFactory proxyFactory; @Before public void init() { StudentService target = new StudentServiceImpl(); proxyFactory = new ProxyFactory(target); // 添加一个Listener proxyFactory.addListener(new CustomAdvisedSupportListener()); } @Test public void testGetProxyJdk() throws IOException { // 将当前的代理暴露到ThreadLocal中 proxyFactory.setExposeProxy(true); proxyFactory.setInterfaces(StudentService.class); //aspect (in spring called advisor) NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor(); //advice advisor.setAdvice(new StudentAdvice()); //pointcut advisor.setMappedName("add"); //添加切面到织入器 proxyFactory.addAdvisor(advisor); StudentService proxy = (StudentService) proxyFactory.getProxy(); Utils.generateProxyClassToFile(proxy.getClass(), "StudentService"); // 调用joinpoint方法 proxy.add(); } }

    代理类是由ProxyFactory生成的,前面概念中也提到ProxyFactory是Spring Aop的织入器。继承结构:

    接下来重点跟踪代码:StudentService proxy = (StudentService) proxyFactory.getProxy()看看内部,getProxy()方法内部主要做如下几件事:

    1:创建一个AopProxy,调用ProxyCreatorSupport#createAopProxy方法完成AopProxy的创建:

    /** * Subclasses should call this to get a new AOP proxy. They should <b>not</b> * create an AOP proxy with {@code this} as an argument. */ protected final synchronized AopProxy createAopProxy() { if (!this.active) { //初始化工作,主要完成Listener的注册,可以通过添加Listener监听变化 activate(); } //创建代理 return getAopProxyFactory().createAopProxy(this); }

    获取到AopProxyFactory之后调用createAopProxy方法创建代理,也就是调用DefaultAopProxyFactory#createAopProxy方法:

    @Override public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (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)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }

    这里面有比较了解一下方法参数AdvisedSupport:

    AdvisedSupport继承至ProxyConfig与Advised,这两个父类都是AopProxy的配置信息类,因此AdvisedSupport中就包含这AopProxy的配置信息,例如Advisor(Aspect),targetSource(targetObject)等。

    既然DefaultAopProxyFactory#createAopProxy方法有AopProxyConfig信息当然就可以根据targetObject属性创建Jdk或者cglib的代理了。这里面我们跟踪的是JDK的Aop实现,因此最终会创建一个JdkDynamicAopProxy。到此AopProxy的具体实现类JdkDynamicAopProxy就完成了创建。

    2:然后调用AopProxy#getProxy()获取代理,在JDK动态代理中AopProxy对应的是JdkDynamicAopProxy#getProxy方法:

    @Override public Object getProxy(ClassLoader classLoader) { if (logger.isDebugEnabled()) { logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource()); } // 查找代理接口,基于接口生成代理子类 Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised); findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); //使用JDK的Proxy完成代理类创建 return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }

    java.lang.reflect.Proxy#newProxyInstance方法三个参数:类加载器,代理接口以及InvocationHandler方法调用器。在Spring的JdkDynamicAopProxy中该类已经实现InvocationHandler,继承图如下:

    因此第三个参数直接传入this即可。到此也就完成了代理类的创建,生成的代理类代码可以借助Arthas工具导出来,生成代理类代码:

    package com.sun.proxy; import com.javartisan.spring.StudentService; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import org.aopalliance.aop.Advice; import org.springframework.aop.Advisor; import org.springframework.aop.SpringProxy; import org.springframework.aop.TargetSource; import org.springframework.aop.framework.Advised; import org.springframework.aop.framework.AopConfigException; /** * 实现了我们的StudentService接口 */ public final class $Proxy0 extends Proxy implements StudentService,SpringProxy,Advised { // joinpoint方法成员变量 private static Method m3; private static Method m1; private static Method m8; private static Method m10; private static Method m22; private static Method m17; private static Method m14; private static Method m4; private static Method m11; private static Method m23; private static Method m24; private static Method m0; private static Method m15; private static Method m20; private static Method m7; private static Method m25; private static Method m2; private static Method m26; private static Method m9; private static Method m18; private static Method m5; private static Method m6; private static Method m19; private static Method m12; private static Method m21; private static Method m16; private static Method m13; // 反射获取方法初始化成员变量 static { try { // 此方法就是我们的joinpoint方法,也就是需要增强的方法 m3 = Class.forName("com.javartisan.spring.StudentService").getMethod("add", new Class[0]); m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m8 = Class.forName("org.springframework.aop.framework.Advised").getMethod("addAdvisor", Class.forName("org.springframework.aop.Advisor")); m10 = Class.forName("org.springframework.aop.framework.Advised").getMethod("isExposeProxy", new Class[0]); m22 = Class.forName("org.springframework.aop.framework.Advised").getMethod("isProxyTargetClass", new Class[0]); m17 = Class.forName("org.springframework.aop.framework.Advised").getMethod("removeAdvisor", Integer.TYPE); m14 = Class.forName("org.springframework.aop.framework.Advised").getMethod("getProxiedInterfaces", new Class[0]); m4 = Class.forName("org.springframework.aop.framework.Advised").getMethod("indexOf", Class.forName("org.springframework.aop.Advisor")); m11 = Class.forName("org.springframework.aop.framework.Advised").getMethod("getTargetSource", new Class[0]); m23 = Class.forName("org.springframework.aop.framework.Advised").getMethod("addAdvice", Integer.TYPE, Class.forName("org.aopalliance.aop.Advice")); m24 = Class.forName("org.springframework.aop.framework.Advised").getMethod("addAdvice", Class.forName("org.aopalliance.aop.Advice")); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m15 = Class.forName("org.springframework.aop.framework.Advised").getMethod("isInterfaceProxied", Class.forName("java.lang.Class")); m20 = Class.forName("org.springframework.aop.framework.Advised").getMethod("removeAdvice", Class.forName("org.aopalliance.aop.Advice")); m7 = Class.forName("org.springframework.aop.framework.Advised").getMethod("setExposeProxy", Boolean.TYPE); m25 = Class.forName("org.springframework.aop.framework.Advised").getMethod("setTargetSource", Class.forName("org.springframework.aop.TargetSource")); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m26 = Class.forName("org.springframework.aop.framework.Advised").getMethod("getTargetClass", new Class[0]); m9 = Class.forName("org.springframework.aop.framework.Advised").getMethod("addAdvisor", Integer.TYPE, Class.forName("org.springframework.aop.Advisor")); m18 = Class.forName("org.springframework.aop.framework.Advised").getMethod("removeAdvisor", Class.forName("org.springframework.aop.Advisor")); m5 = Class.forName("org.springframework.aop.framework.Advised").getMethod("indexOf", Class.forName("org.aopalliance.aop.Advice")); m6 = Class.forName("org.springframework.aop.framework.Advised").getMethod("isFrozen", new Class[0]); m19 = Class.forName("org.springframework.aop.framework.Advised").getMethod("replaceAdvisor", Class.forName("org.springframework.aop.Advisor"), Class.forName("org.springframework.aop.Advisor")); m12 = Class.forName("org.springframework.aop.framework.Advised").getMethod("setPreFiltered", Boolean.TYPE); m21 = Class.forName("org.springframework.aop.framework.Advised").getMethod("toProxyConfigString", new Class[0]); m16 = Class.forName("org.springframework.aop.framework.Advised").getMethod("getAdvisors", new Class[0]); m13 = Class.forName("org.springframework.aop.framework.Advised").getMethod("isPreFiltered", new Class[0]); return; } catch (NoSuchMethodException noSuchMethodException) { throw new NoSuchMethodError(noSuchMethodException.getMessage()); } catch (ClassNotFoundException classNotFoundException) { throw new NoClassDefFoundError(classNotFoundException.getMessage()); } } public final void add() { try { //h就是我们的InvocationHandler,也就是本文所说的方法调用器 this.h.invoke(this, m3, null); return; } catch (Error | RuntimeException throwable) { throw throwable; } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } } //忽略其他与本文无关的方法 }

    到此已经彻底完成了代理类的生成,接下来就了解一下如何对joinpoint方法进行增强的。

    JoinPoint的增强实现

    到此为止代理类已经生成,最终业务代码可以借助代理类进行方法调用已完成增强,增强的标准接口继承结构如下:

    Advice就是aopalliance制定的标准接口。对于示例代码的增强逻辑代码如下:

    import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class StudentAdvice implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { System.out.println("StudentAdvice start ."); Object obj = invocation.proceed(); System.out.println("StudentAdvice end ."); return obj; } }

     

    接下来看一下如何完成的增强。在本文中joinpoint为StudentService#add()方法,因此当我们调用add方法时候会触发代理类的add方法,即如上的add方法,最终进入到JdkDynamicAopProxy#invoke方法中,精简代码如下:

    /** * Implementation of {@code InvocationHandler.invoke}. * <p>Callers will see exactly the exception thrown by the target, * unless a hook method throws an exception. */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodInvocation invocation; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Class<?> targetClass = null; Object target = null; // 忽略equal与hashcode方法的处理 Object retVal; //是否暴露代理到线程局部变量,如果暴露的话需要设置expose参数 //使用场景,如果targetObject内部调用本类的其他方法还需增强的话。 //需要使用AopContext#currentProxy获取this实例对应的代理对象调用 if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // May be null. Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. //TargetSource是spring对TargetObject的一种封装表示 target = targetSource.getTarget(); if (target != null) { targetClass = target.getClass(); } // Get the interception chain for this method. //获取待增强的所有 advice List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. if (chain.isEmpty()) { //没有增强,直接调用target方法 // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // We need to create a method invocation... //有增强,需要完成增强的逻辑调用 //ReflectiveMethodInvocation负责完成增强逻辑与最终targetObject方法的封装 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. //通过增强逻辑与最终targetObject方法的封装对象最终完成调用 retVal = invocation.proceed(); } //处理返回值 // Massage return value if necessary. Class<?> returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // Special case: it returned "this" and the return type of the method // is type-compatible. Note that we can't help if the target sets // a reference to itself in another returned object. 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()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. // 如果开启了暴露当前代理到线程局部变量的话,最后需要还原之前的代理 AopContext.setCurrentProxy(oldProxy); } } }

    JdkDynamicAopProxy中含有所有的Aop信息,因此就可以完成最终方法的增强调用。不过在JdkDynamicAopProxy#invoke中进行了增强advice与targetObject方法的封装,封装为:ReflectiveMethodInvocation,ReflectiveMethodInvocation#proceed方法才是最后的方法调用结束。ReflectiveMethodInvocation继承结构如下:

    上层的接口都是Aopalliance的标准接口,下面是Spring进行的实现。 ReflectiveMethodInvocation#proceed源码如下:

    @Override public Object proceed() throws Throwable { // We start with an index of -1 and increment early. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { // 最终完成targetObject的方法调用,也就是joinpoint方法调用 return invokeJoinpoint(); } //递归调用增强的chain //this.interceptorsAndDynamicMethodMatchers就是advice组成的调用链 Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { // Evaluate dynamic method matcher here: static part will already have // been evaluated and found to match. InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) { return dm.interceptor.invoke(this); } else { // Dynamic matching failed. // Skip this interceptor and invoke the next in the chain. return proceed(); } } else { // It's an interceptor, so we just invoke it: The pointcut will have // been evaluated statically before this object was constructed. //调用advice,也就是调用增强逻辑 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }

    到此也就结束了,完成了从代理对象的生成以及借助代理对象调用增强逻辑以及最终的targetObject的方法。

    最新回复(0)