理解Spring动态代理的前世今生

我对代理的理解就两个字:增强, 脱离增强谈代理都是耍流氓

在代理中有两个概念:目标类代理类

由于目标类对某些功能存在缺失,代理类通过增强弥补目标类的功能缺失。例如在某个方法前后打印日志,在执行使用事务时要对其自动提交功能关闭等。

说到增强,要么是在编译期生成的class文件对目标进行增强,要么就是运行期间使用代理字节码文件对其增强;

1.Aspectj增强 — 编译期

1.引入maven的插件

该插件会导致Lombok插件失效,比如通过@Data注解生成get set方法调用的地方会报红。

  <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.14.0</version>
                <configuration>
                    <complianceLevel>1.8</complianceLevel>
                    <source>1.8</source>
                    <target>1.8</target>
                    <showWeaveInfo>true</showWeaveInfo>
                    <Xlint>ignore</Xlint>
                    <encoding>UTF-8</encoding>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <configuration>
                            <skip>false</skip>
                        </configuration>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

2.编写测试代码

//1.声明切面
@Aspect
public class MyAspect {
    @Before("execution (* com.dev.wizard.proxy.aspectj.TestService.testService())")  //要求是方法的全路径
    public void before(){
        System.out.println("before......");
    }
}
//2.声明被切入的方法,静态方法也是可以增强的
public class TestService {
    public void testService(){
        System.out.println("testService");
    }
}
//3.测试主方法
public class TestMain {
    public static void main(String[] args) {
        TestService testService = new TestService();
        testService.testService();
    }
}
//4.输出结果
before......
testService

上述主要是对MyAspect/TestService类在编译期间修改成了生成的class文件,class文件如下:

@Aspect
public class MyAspect {
    public MyAspect() {
    }

    @Before("execution (* com.dev.wizard.proxy.aspectj.TestService.testService())")
    public void before() {
        System.out.println("before......");
    }

    public static MyAspect aspectOf() {
        if (ajc$perSingletonInstance == null) {
            throw new NoAspectBoundException("com.dev.wizard.proxy.aspectj.MyAspect", ajc$initFailureCause);
        } else {
            return ajc$perSingletonInstance;
        }
    }

    public static boolean hasAspect() {
        return ajc$perSingletonInstance != null;
    }

    static {
        try {
            ajc$postClinit();
        } catch (Throwable var1) {
            ajc$initFailureCause = var1;
        }

    }
}


public class TestService {
    public TestService() {
    }

    public void testService() {
        MyAspect.aspectOf().before();
        System.out.println("testService");
    }
}
注意:

由于IDEA使用Javac编译,会导致插件没有生效,你可以通过设置去让插件生效,也可以使用maven的complie方式。 image-20220814214841612

2.Aspectj增强 — 类加载期间(LTW)

基于上述的代码不变,去除pom文件的maven插件,然后再classpath路径下的META-INF文件夹下建立aop.xml文件,该文件用于在程序启动时读取对那些路径下的文件做切面。

<?xml version="1.0" encoding="UTF-8"?>
<aspectj>
    <aspects>
        <!-- 1.切面类 -->
        <aspect name="com.dev.wizard.springboot.proxy.agent.MyAspect" />
    </aspects>
    <weaver options="-Xset:weaveJavaxPackages=true">
        <!-- 2.指定需要进行织入操作的目标类范围 -->
        <include within="com.dev.wizard.springboot.proxy.agent.*" />
    </weaver>
</aspectj>

这里是我的文件结构: image-20220814215536390

在项目启动时需要配置参数:-javaagent:F:/Repository/maven_respository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar,这里是我配置的绝对路径,大家也可以把这个jar包拷贝到项目中配置一个相对路径。例如在项目路径下创建一个common的文件夹(该文件夹和src齐平),然后拷贝jar包到该文件夹下面,这时你可以配置成-javaagent:./common/aspectjweaver-1.9.7.jar

该方法不同于代理模式生成的代理类调用
@Aspect
public class MyAspect {

    @Before("execution (* com.dev.wizard.proxy.agent.TestService.*()  )")
    public void before(){
        System.out.println("before......");
    }
}

@Service
public class TestService {

    public void first(){
        System.out.println("first");
        this.second();

    }

    public void second(){
        System.out.println("second");
    }
}
//使用LTW 可以对this这种调用也会执行切面的逻辑,此处输出如下:
before......
first
before......
second

如果上述使用的是代理类的方式就不会触发second的切面逻辑,因为代理类必须要用代理类调用需要切面的方法,这样才可以对目标进行增强。

通过arthas工具看看类加载对这个目标类做了什么?

windows使用cmd命令打开终端(powershell不支持下载命令):curl -O https://arthas.aliyun.com/arthas-boot.jar


       @Service
       public class TestService {
           public void first() {
/*10*/         MyAspect.aspectOf().before();
               System.out.println("first");
/*11*/         this.second();
           }

           public void second() {
/*16*/         MyAspect.aspectOf().before();
               System.out.println("second");
           }
       }
从反编译后的代码可以两个方法内部都会执行before的方法

上述的两种切面,是从执行的代码层面来进行改变进而实现了静态的AOP。

2.动态代理

2.1 JDK代理

public class JDKProxy {
    interface ProxyInterface{
        void proxyMethod();
    }
    static class Target implements ProxyInterface{
        @Override
        public void proxyMethod() {
            System.out.println("Target + proxyMethod");
        }
    }
    public static void main(String[] args) {
        //1.获取当前加载该代理的类加载器,用于加载运行期间生成的字节码文件
        ClassLoader loader = JDKProxy.class.getClassLoader();
        //2.获取目标类的对象
        Target target = new Target();
        //invocationHandler 提供反射所需要的参数,目标对象需要执行方法时才会执行
        //1.代理对象
        //2.需要增强的方法
        //3.增强的方法中的参数
        ProxyInterface proxyInterface = (ProxyInterface) Proxy.newProxyInstance(loader, new Class[]{ProxyInterface.class}, (proxyObject, method, parameters) -> {

            System.out.println("before......");
            //通过反射执行目标类的方法
            Object result = method.invoke(target, parameters);
            System.out.println("after......");
            return null;
        });

        proxyInterface.proxyMethod();
    }
}

Note:

  1. JDK中的目标类和代理类是实现了同一个接口,两者是平级关系,不能进行相互转换;
  2. 目标类为final在JDK中都是可以使用代理类执行(Cglib是不可以的)

JDK代理的原理

  • 手写简易的JDK代理
//我们很多人都应该想到代理类所干的的事情 代码如下 第一版本
public class MyProxy {
    interface ProxyInterface{

        void voidMethod();

        void voidMethodWithPram(String value);

        String stringMethodWithPram(String value);
    }

    static class Target implements ProxyInterface{
        @Override
        public void voidMethod() {
            System.out.println("voidMethod");
        }
        @Override
        public void voidMethodWithPram(String value) {
            System.out.println("voidMethodWithPram" + value);
        }
        @Override
        public String stringMethodWithPram(String value) {
            System.out.println("stringMethodWithPram" + value);
            return "stringMethodWithPram" + value;
        }
    }

    static class $Proxy0 implements ProxyInterface{
        @Override
        public void voidMethod() {
            System.out.println("before...");
            new Target().voidMethod();

        }
        @Override
        public void voidMethodWithPram(String value) {
            System.out.println("before");
            new Target().voidMethodWithPram("hello");
        }

        @Override
        public String stringMethodWithPram(String value) {
            System.out.println("before");
            return new Target().stringMethodWithPram(value);
        }
    }
    public static void main(String[] args) {
        $Proxy0 proxy0 = new $Proxy0();
        proxy0.voidMethod();
        proxy0.voidMethodWithPram("hello");
        String returnValue = proxy0.stringMethodWithPram("world");
    }
}


//上述的写法是我们自己本身知道存在这样的逻辑,输出before这样的增强逻辑被写死,
//为了能够然后使用者灵活使用,我们需要将这种增强逻辑交出去,交给使用方
//第二版本

public class MyProxy_SecondEdition {

    interface ProxyInterface{

        void voidMethod();

        void voidMethodWithPram(String value);

        String stringMethodWithPram(String value);
    }

    //通过定义一个接口, 该接口的内部实现就是交给使用方的增强逻辑
    interface InvocationHandler {
        Object invoke(Method method, Object[] objects) throws Throwable;
    }
    static class Target implements MyProxy_FirstEdition.ProxyInterface {

        @Override
        public void voidMethod() {
            System.out.println("voidMethod");
        }

        @Override
        public void voidMethodWithPram(String value) {
            System.out.println("voidMethodWithPram" + value);
        }

        @Override
        public String stringMethodWithPram(String value) {
            System.out.println("stringMethodWithPram" + value);
            return "stringMethodWithPram" + value;
        }
    }

    static class $Proxy0 implements MyProxy_FirstEdition.ProxyInterface {
        private InvocationHandler invocationHandler;
        //传递增强逻辑的方法引用
        public $Proxy0(InvocationHandler invocationHandler){
            this.invocationHandler = invocationHandler;
        }

        @Override
        public void voidMethod() {
            try {
                Method method = Target.class.getDeclaredMethod("voidMethod");
                invocationHandler.invoke(method, new Object[0]);
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }

        @Override
        public void voidMethodWithPram(String value) {
            try {
              Method method = Target.class.getDeclaredMethod("voidMethodWithPram", String.class);
              invocationHandler.invoke(method, new Object[]{value});
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }

        @Override
        public String stringMethodWithPram(String value) {
            try {
              Method method = Target.class.getDeclaredMethod("stringMethodWithPram", String.class);
              return (String)invocationHandler.invoke(method, new Object[]{value});
            } catch (Throwable e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    public static void main(String[] args) {
        Target target = new Target();
        MyProxy_SecondEdition.$Proxy0 proxy0 = new MyProxy_SecondEdition.$Proxy0(new InvocationHandler() {
            @Override
            public Object invoke(Method method, Object[] objects) throws Throwable {
                System.out.println("before...");
                Object object = method.invoke(target, objects);
                return object;
            }
        });
        proxy0.voidMethod();
        proxy0.voidMethodWithPram("hello");
        String returnValue = proxy0.stringMethodWithPram("world");
        System.out.println(returnValue);
    }
}
//纵观整个流程,其实上述的整个流程就是赤裸裸的静态代理,代理类的构造方法传递了增强的逻辑的引用,然后再每一个代理方法中执行
//对于上述的代理类中所有获取Method的对象可以放在static块中加载
   private static Method method0;
   private static Method method1;
   private static Method method2;
        static {
            try {
                method0 = Target.class.getDeclaredMethod("voidMethod");
                method1 = Target.class.getDeclaredMethod("voidMethodWithPram", String.class);
                method2 = Target.class.getDeclaredMethod("stringMethodWithPram", String.class);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        }
//这样的话只会加载一次 不用每次在方法中调用都要执行
  • JDK的代理的源码,使用Arthas反编译
package com.dev.wizard.proxy.jdk;

import com.dev.wizard.proxy.jdk.JDKProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $Proxy0
extends Proxy
implements JDKProxy.ProxyInterface {
    private static Method m1;
    private static Method m5;
    private static Method m2;
    private static Method m4;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler invocationHandler) {
        super(invocationHandler);
    }

    public final boolean equals(Object object) {
        try {
            return (Boolean)this.h.invoke(this, m1, new Object[]{object});
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void voidMethod() {
        try {
            this.h.invoke(this, m5, null);
            return;
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString() {
        try {
            return (String)this.h.invoke(this, m2, null);
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String stringMethodWithPram(String string) {
        try {
            return (String)this.h.invoke(this, m4, new Object[]{string});
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void voidMethodWithPram(String string) {
        try {
            this.h.invoke(this, m3, new Object[]{string});
            return;
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode() {
        try {
            return (Integer)this.h.invoke(this, m0, null);
        }
        catch (Error | RuntimeException throwable) {
            throw throwable;
        }
        catch (Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m5 = Class.forName("com.dev.wizard.proxy.jdk.JDKProxy$ProxyInterface").getMethod("voidMethod", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m4 = Class.forName("com.dev.wizard.proxy.jdk.JDKProxy$ProxyInterface").getMethod("stringMethodWithPram", Class.forName("java.lang.String"));
            m3 = Class.forName("com.dev.wizard.proxy.jdk.JDKProxy$ProxyInterface").getMethod("voidMethodWithPram", Class.forName("java.lang.String"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            return;
        }
        catch (NoSuchMethodException noSuchMethodException) {
            throw new NoSuchMethodError(noSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException classNotFoundException) {
            throw new NoClassDefFoundError(classNotFoundException.getMessage());
        }
    }
}

//和我们上述自己手写的代码相似度很高的
  • JDK动态代理使用了ASM技术直接生成了字节码文件,我们看到的文件是通过字节码反编译后的class文件。ASM就是一门使用程序生成程序技术
  • JDK代理在前15次使用的是反射方式调用目标类方法,该反射类为NativeMethodAccessorImpl。 在第16次的时候JDK对调用做了优化,生成一个GeneratedMethodAccessor的代理类直接调用目标类的方法,大大降低性能损耗,但是该优化是对一个方法生成一个代理类来优化。
//JDK的优化反射细节
public class TestMethodInvoke {
    static class TestClass{

        public String testMethod(String value){
            System.out.println("testMethod");
            return "testMethod" + value;
        }

        public void testVoidMethod(String value){
            System.out.println("testVoidMethod");
        }
    }

    public static void main(String[] args) throws Exception{
        Class testClass = TestClass.class;
        TestClass testObj = new TestClass();
        Method testMethod = testClass.getMethod("testMethod", String.class);
        Method testVoidMethod = testClass.getMethod("testVoidMethod", String.class);
        printProxyMethod(testObj, testMethod);
        printProxyMethod(testObj, testVoidMethod);
        System.in.read();

    }

    public static void printProxyMethod(TestClass testClass, Method testMethod){
        IntStream.range(0, 18).forEach( index -> {
            try {
                testMethod.invoke(testClass, "Hello");
                Field field = testMethod.getClass().getDeclaredField("methodAccessor");
                field.setAccessible(true);
                MethodAccessor f = (MethodAccessor) field.get(testMethod);
                Field field1 = f.getClass().getDeclaredField("delegate");
                field1.setAccessible(true);
                System.out.println(field1.get(f).toString() + "---" + index);
            } catch (Exception e ) {
                e.printStackTrace();
            }
        });
    }
}
//部分输出结果的 index是从0开始的
...
testMethod
sun.reflect.NativeMethodAccessorImpl@445b84c0---14
testMethod
sun.reflect.GeneratedMethodAccessor1@2b80d80f---15
...
testVoidMethod
sun.reflect.NativeMethodAccessorImpl@3ab39c39---14
testVoidMethod
sun.reflect.GeneratedMethodAccessor2@546a03af---15
...

//对于字节码生成的GeneratedMethodAccessor1
    public class GeneratedMethodAccessor1 extends MethodAccessorImpl {
    /*
     * Loose catch block
     */
    public Object invoke(Object object, Object[] objectArray) throws InvocationTargetException {
        if (object == null) {
            throw new NullPointerException();
        }
        TestMethodInvoke.TestClass testClass = (TestMethodInvoke.TestClass)object;
        if (objectArray.length != 1) {
            throw new IllegalArgumentException();
        }
        String string = (String)objectArray[0];
        try {
            return testClass.testMethod(string); //此处为使用对象的直接调用
        }
        catch (Throwable throwable) {
            throw new InvocationTargetException(throwable);
        }
        catch (ClassCastException | NullPointerException runtimeException) {
            throw new IllegalArgumentException(super.toString());
        }
    }
}
Note: 

使用生成的字节码对象是方法级别的,JVM会对使用反射方式对于某个类中的方法调用超过15次时就认为这个方法是一个热方法,于是会对该方法专门生成一个GeneratedMethodAccessor类来调用invoke方法。另外这个类生成是属于当前JVM的,只要JVM启动者一旦生成就可以一直被调用,重启后需要触发阈值重新生成,该阈值可使用JVM启动参数设置-Dsun.reflect.inflationThreshold=,大于该阈值就会生成代理类。

2.2CGLIB代理

public class CglibProxyDemo {

    public static void main(String[] args) {

        Target target = new Target();
        /**
         * 1.代理对象本身
         * 2.需要代理的方法
         * 3.代理方法的参数
         * 4.被代理的方法(已经增强的方法)
         * 这里只是生成了一个代理对象,包括该代理对象在执行的情况下的一些逻辑;
         * 另外如果此处的代理对象如果没有执行方法是,是不会执行生成代理对象的。
         */
        Target proxyObject = (Target) Enhancer.create(Target.class, (MethodInterceptor) (proxyObj, method, methodArgs, methodProxy) -> {
            System.out.println("before...");
            //1.使用目标方法通过反射方式执行
            //Object result = method.invoke(target, methodArgs);

            //2.使用代理方法方式调用  底层无反射 spring的选择
            // Object result = methodProxy.invoke(target, methodArgs);
            //3.使用代理方法父类方法  底层无反射
            Object result = methodProxy.invokeSuper(proxyObj, methodArgs);
            System.out.println("after...");
            String returnString = (String) result;

            return returnString + "after Proxy" ;
        });
        String value = proxyObject.proxyMethod();

        System.out.println(value);
    }
    //该类被final修饰时时不能生成代理对象的, 会报错
    static class Target {
        /**
         * 该方法如果被final或者是private修饰的,那么代理对象就无法对该方法实现代理
         */
       public String proxyMethod() {
            System.out.println("Target + proxyMethod");
            return "return: Target + proxyMethod";
        }
    }
}

Cglib代理存在三种方式:
  • 结合目标类使用反射;
  • 结合目标类使用直接调用;

  • 结合代理类使用直接调用;

上述三种方式暂时可不理解,下述为该三种方式手写代码:

//第一种:结合目标类使用反射,该种方式和JDK相似度很高

//定义目标类
public class CglibTarget {
    public void saveMethod(){
        System.out.println("CglibTarget -- 无参 saveMethod");
    }

    public String saveMethod(int value){
        System.out.println("CglibTarget -- int参 saveMethod" + value);
        return "int参";
    }
}

//定义代理类,并让代理继承目标类, 代理中的MethodInterceptor和JDK代理中的InvocationHandler相似作用
public class CglibProxy extends CglibTarget {
    private MethodInterceptor methodInterceptor;

    CglibProxy (MethodInterceptor methodInterceptor){
        this.methodInterceptor = methodInterceptor;
    }

    static Method save0;
    static Method save1;
    static {
        try {
            //我个人理解:定位一个类中的某个方法只需方法名和参数即可,实际上返回类型没有关系
            save0 = CglibTarget.class.getDeclaredMethod("saveMethod");
            save1 = CglibTarget.class.getDeclaredMethod("saveMethod", int.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
    public void saveMethod(){
        try {
            methodInterceptor.intercept(this, save0, new Object[0], save0MethodProxy);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    public String saveMethod(int value){
        try {
            methodInterceptor.intercept(this, save1, new Object[]{value}, save1MethodProxy);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return "int参";
    }
    public static void main(String[] args) {

        CglibTarget target = new CglibTarget();
        CglibProxy cglibProxy = new CglibProxy(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("before...");
                method.invoke(target, objects); //底层使用了反射调用
                return null;
            }
        });
        cglibProxy.saveMethod();
        cglibProxy.saveMethod(33);
    }
}
//运行该方法后输出:
//before...
//CglibTarget -- 无参 saveMethod
//before...
//CglibTarget -- int参 saveMethod33


public class CglibProxy extends CglibTarget {

    private MethodInterceptor methodInterceptor;

    CglibProxy (MethodInterceptor methodInterceptor){
        this.methodInterceptor = methodInterceptor;
    }
    static Method save0;
    static Method save1;

    static MethodProxy save0MethodProxy;
    static MethodProxy save1MethodProxy;

    static {
        try {
            save0 = CglibTarget.class.getDeclaredMethod("saveMethod");
            save1 = CglibTarget.class.getDeclaredMethod("saveMethod", int.class);

            //根据方法信息生成MethodProxy对象,根据该对象实现增强
            //1.目标类的信息;2.代理类的信息;3.方法描述信息;4.需要增强的方法名;5调用原始的方法名;
            // 其中方法描述信息和增强的方法名用于后期根据此信息找到对应的方法进行执行
            save0MethodProxy = MethodProxy.create(CglibTarget.class, CglibProxy.class, "()V", "saveMethod", "saveSuperMethod");
            save1MethodProxy = MethodProxy.create(CglibTarget.class, CglibProxy.class, "(I)Ljava/lang/String;", "saveMethod", "saveSuperMethod");

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    public void saveSuperMethod(){
        super.saveMethod();
    }
    public String saveSuperMethod(int value){
        return super.saveMethod(value);
    }

    public void saveMethod(){
        try {
            methodInterceptor.intercept(this, save0, new Object[0], save0MethodProxy);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    public String saveMethod(int value){
        try {
            methodInterceptor.intercept(this, save1, new Object[]{value}, save1MethodProxy);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return "int参";
    }


    public static void main(String[] args) {

        CglibTarget target = new CglibTarget();
        CglibProxy cglibProxy = new CglibProxy(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("before...");
                //method.invoke(target, objects); //底层使用了反射
                //methodProxy.invoke(target, objects); //使用目标对象直接调用
                methodProxy.invokeSuper(o, objects); //使用代理对象直接调用
                return null;
            }
        });
        cglibProxy.saveMethod();
        cglibProxy.saveMethod(33);
    }
}
为啥invoke(target, objects)和 invokeSuper(proxy, objects)没有使用反射?

由于对于生成的Cglib代理类后,基于目标类和代理类生成两个fastclass子类,且均使用MethodProxy配合目标类和代理类实现无反射调用。

TargetFastClass:
//模拟生成TargetFastClass   该类是为了结合methodProxy.invoke(target, args)使用的
public class TargetFastClass {
    static Signature s0 = new Signature("saveMethod", "()V");
    static Signature s1 = new Signature("saveMethod", "(I)Ljava/lang/String;");

    /**
     * 根据方法的签名信息获取对应的编号
     * void saveMethod()   ---> 0
     * String saveMethod(int value) ---> 1
     */
    public int getIndex(Signature signature){
        //signature = 方法名 + 参数 + 返回值类型, 实际上方法名+参数就可以确定一个方法
        //因为一个方法的重载和返回类型没有关系

        if(s0.equals(signature)){
            return 0;
        }else if(s1.equals(signature)){
            return 1;
        }
        return -1;
    }

    /**
     * 根据方法的编号找到对应的方法,使用target对象直接调用该方法
     */
    public Object invoke(int index, Object target, Object[] args){

        if( 0 == index){
            ((CglibTarget)target).saveMethod();
        }else if( 1 == index){
           return ((CglibTarget)target).saveMethod((int)args[0]);
        }
        return null;
    }

    public static void main(String[] args) {

        TargetFastClass targetFastClass = new TargetFastClass();
        // 由于在生成MethodInterceptor对象的时候塞了方法名和参数返回类型,根据此信息可得到index值
        int index = targetFastClass.getIndex(new Signature("saveMethod", "()V"));
        System.out.println( "index: "+ index);
        //  再根据index找到对应的方法进行执行
        targetFastClass.invoke(index, new CglibTarget(), new Object[0]);

        int indexTwo = targetFastClass.getIndex(new Signature("saveMethod", "(I)Ljava/lang/String;"));
        System.out.println( "index: "+ indexTwo);
        String result = (String)targetFastClass.invoke(indexTwo, new CglibTarget(), new Object[]{100});
        System.out.println(result);
    }
}


ProxyFastClass:

/**
 * 模拟字节码生成的FastClass
 * 该类的目标是为了避免使用methodProxy.invokeSuper(proxy, args)时通过反射调用生成的
 * 该类会生成所有的方法
 */
public class ProxyFastClass  {
    static Signature s0 = new Signature("saveSuperMethod", "()V");
    static Signature s1 = new Signature("saveSuperMethod", "(I)Ljava/lang/String;");

    /**
     * 根据方法的签名信息获取对应的编号
     * void saveSuperMethod()   ---> 3
     * String saveSuperMethod(int value) ---> 4
     */
    public int getIndex(Signature signature){
        //signature = 方法名 + 参数 + 返回值类型, 实际上方法名+参数就可以确定一个方法
        //因为一个方法的重载和返回类型没有关系

        if(s0.equals(signature)){
            return 3;
        }else if(s1.equals(signature)){
            return 4;
        }
        return -1;
    }

    /**
     * 根据方法的编号找到对应的方法,使用proxy对象直接调用该方法
     */
    public Object invoke(int index, Object proxy, Object[] args){
        if( 3 == index){
            ((CglibProxy)proxy).saveSuperMethod();
        }else if(4 == index){
          return ((CglibProxy)proxy).saveSuperMethod((int)args[0]);
        }
        return null;
    }

    public static void main(String[] args) {
        ProxyFastClass proxyFastClass = new ProxyFastClass();
        int index = proxyFastClass.getIndex(new Signature("saveSuperMethod", "(I)Ljava/lang/String;"));
        System.out.println("index: " + index);
        String result = (String)proxyFastClass.invoke(index, new CglibProxy(), new Object[]{55});
        System.out.println(result);
    }
}

实际上上述的两种方式的实现底层都是一致的,TargetFastClass使用了目标对象调用了目标类里面的方法(该方法是没有被增强的),ProxyFastClass使用代理对象调用代理类里面的方法,(注意)代理类中存在增强的方法,这个增强方法不是ProxyFastClass所要调用的方法,因为ProxyFastClass调用的方法是在增强方法的内部,其只是为了执行原始的方法,如果调用的是增强的方法的话,会陷入一个循环,最后会报错的。所以使用代理类执行原始的方法就是通过内部使用super的方式执行了父类的方法,而此时的父类正好是目标类,所以两者的本质都是执行了目标类的需要增强的方法,甚至是反射调用也是这样的。

该两个类可以解决代理类和目标类中所有方法的直接调用,和JDK的无反射区别

  • 前者在MethodProxy对象第一次调用invoke或者是invokeSuper方法的时候就会生成两个FastClass的代理类,而后者是当调用次数大于某个阈值(默认是15)是才会生成代理类;
  • 前者是两个FastClass类均可以生成所有的方法,而后者是一个方法生成一个类,其实也很好理解,因为需要某个方法调用次数达到阈值啊,没有到阈值是不会生成的;