[Java高级篇]动态代理 从JDK到CGLib

本文介绍了Java动态代理的概念,从静态代理到动态代理的需求演变,详细讲解了JDK的动态代理机制,包括对象创建和动态代理的实现,并探讨了CGLib动态代理的使用和原理,展示了如何通过CGLib对未实现接口的类进行代理。最后,对CGLib的FastClass机制进行了分析,揭示了动态代理的调用链。
摘要由CSDN通过智能技术生成

前言

什么是代理?

为其他对象提供一个代理以控制对某个对象的访问。代理类主要负责为委托的目标对象(真实对象)的操作预处理、操作过滤、传递消息给委托类,代理类不现实具体服务,而是利用目标对象来完成服务,并将执行结果封装处理。

听起来比较绕,其实就是方法调用的时候不直接使用原有的类,而是使用一个代理类来执行,这个代理类可以在原有的类方法上执行更多的操作。

也可以说 代理是提供一种“一个类对另外一个类的控制权。”

静态代理

知道了代理 就是“一个类控制另外一个类”,增强被代理的类的功能

那么怎么进行代理?很简单就能想到创建一个代理类,然后用这个代理类持有被代理的类,调用时调用代理类的方法,代理类的被调用的方法中对请求进行过处理,然后再转发到被代理的类的真正的方法。

创建一个接口,然后创建被代理的类实现该接口并且实现该接口中的抽象方法。之后再创建一个代理类,同时使其也实现这个接口。在代理类中持有一个被代理对象的引用,而后在代理类方法中调用该对象的方法。

也就是说 代理对象 = 增强代码 + 目标对象(原对象)

接口:

public interface IDo {
    void doAction();
}

被代理类:

public class Do implements IDo {
    @Override
    public void doAction() {
        System.out.println("Hello World!");
    }
}

代理类:

public class TheProxy implements IDo {
    private IDo do= new Do();
    @Override
    public void doAction() {
        System.out.println("Before invoke doAction" );
        do.doAction();
        System.out.println("After invoke doAction");
    }
}

是不是很简单呢
复合优先于继承法则,复合类就是代理了代理类的方法。复合类通过持有一个代理类的引用,代理了代理类的方法,对被代理的类的功能进行了增强。

但是静态代理有个缺陷 那就是需要手动为每一个目标类编写对应的代理类

为什么需要动态代理?

如果现在有几个需求:在项目现有所有类的方法前后打印日志或者记录方法的调用时间

显然一个个的修改已有的代码是个费力不讨好的办法,那么肯定想到了使用代理的方法
不修改原有的代码,而是通过代理去执行增强的功能。

能使用静态代理吗?静态代理需要为手动为每一个目标类编写对应的代理类 当你的代码中的类数量很大时,这几乎是个不可能的任务。

所以,我们的需求其实是:如何少写或者不写代理类,却能完成代理功能?

此时就 需要动态代理了。

常见的动态代理场景

Spring的AOP 面向切面编程 通过动态代理实现对切面(也就是被代理类的方法)的增强

有些框架,比如Feign,Retrofit 可以在用户完全不写实现类,只需定义接口的情况下,还能调用接口中的方法,这就是通过动态代理在调用过程中动态的生成了被代理类

使用

JDK的原生动态代理机制

通过静态代理我们可知,代理的关键是创建代理类以代理目标对象的方法。那么动态代理的关键就是怎么自动化的,可控的创建需要的代理对象。

对象的创建

对象的创建,第一个就会想到使用new关键字+构造函数创建 A a=new A()
非常的容易,但是在给若干个未知类动态的创建代理类对象的时候,显然就不能使用new关键字去创造了,那么怎么办呢?
自然就想到创建对象的另一个方法,使用反射去创建对象。不写代理类,直接得到代理Class对象,然后根据它创建代理实例。

Class对象包含了一个类的所有信息,比如构造器、方法、字段等。如果我们不写代理类,这些信息从哪获取呢?JDK的原生动态代理机制要求代理类和目标对象实现相同的接口。之所以实现相同接口,是为了尽可能保证代理对象的内部结构和目标对象一致,这样我们对代理对象的操作最终都可以转移到目标对象身上,代理对象只需专注于增强代码的编写。

接口拥有代理对象和目标对象共同的方法信息。所以,我们可以从接口那得到理应由代理类提供的信息。

动态代理

JDK提供了java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy类,这两个类相互配合,入口是Proxy。

Proxy有一个静态方法

public static Class<?> getProxyClass(ClassLoader loader,
                                         Class<?>... interfaces)

传入一个类加载器,一组接口,返回一个实现了所有传入接口的类,这个类就可以作为目标对象的代理类,因为它实现了所有和目标对象相同的接口。

先定义一个接口

public interface Developer {

    void code();

    void debug();
    
	void comment() ;
}

再定义一个实现类

public class PhpDeveloper implements Developer {
    @Override
    public void code() {
        System.out.println("dev php code");
    }

    @Override
    public void debug() {
        System.out.println("debug php code");
    }
    
    @Override
    public void comment() {
        System.out.println("PHP是世界上最好的语言!");
    }
}

现在我们通过Proxy.getProxyClass来代理这个PHPDeveloper对象

public class JavaReflect {
	//主方法
    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        JavaReflect reflect = new JavaReflect();
        reflect.enCode();
    }

    public void enCode() throws NoSuchMethodException, IllegalAccessException, 
    InvocationTargetException, InstantiationException {
        Developer phpDeveloper = new PhpDeveloper();
        //反射从字节码生成Class
        Class delegateDeveloperClass=Proxy.getProxyClass
        (phpDeveloper.getClass().getClassLoader(),Developer.class);
		//调用构造方法从Class创建对象 注意这里是使用了InvocationHandler接口创建自己的调用处理器 所以下面的构造方法需要传入一个InvocationHandler对象 
        Constructor<Developer> constructor=delegateDeveloperClass
        .getConstructor(InvocationHandler.class);
		//生成代理对象 传入一个InvocationHandler对象 InvocationHandler对象实际持有目标对象的引用
        Developer phpDeveloperProxy=
        constructor.newInstance(new InvocationHandler() {
        	//方法调用 每次调用代理对象的方法 实际上就是通过此方法桥接到目标对象的方法
            @Override
            public Object invoke
            (Object proxy, Method method, Object[] args) 
            throws Throwable {
                System.out.println(method.getName());
                //执行目标对象的方法
                return method.invoke(phpDeveloper ,args);
            }
        });

        phpDeveloperProxy.code();
        phpDeveloperProxy.debug();
        phpDeveloperProxy.comment();
    }
}

结果

code
dev php code
debug
debug php code
comment
PHP是世界上最好的语言!

到这里已经实现了动态代理,根据传入的对象自动生成一个代理类,执行代理方法。
无论现在系统有多少类,只要你把实例传进来,getProxy()都能给你返回对应的代理对象。

当然这个方法还是有点麻烦,需要创建类,获取构造方法,再从类构造方法创建对象,最后调用代理对象。所以JDK已经提供了一个更方便的方法Proxy.newProxyInstance()
Proxy.getProxyClass已经在JDK11被标记为@废弃

   public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
  • loder,选用的类加载器。因为代理的是目标对象,所以一般都会用加载目标对象的类加载器。
  • interfaces,被代理的类所实现的接口,这个接口可以是多个。
  • h,调用处理器 用来绑定代理类的方法。

InvocationHandler调用处理器,作用就是,当代理对象的原本方法被调用的时候,会绑定执行一个方法,这个方法就是InvocationHandler里面定义的内容,同时会替代原本方法的结果返回。

new InvocationHandler() {
        	//方法调用 每次调用代理对象的方法 实际上就是通过此方法桥接到目标对象的方法
            @Override
            public Object invoke
            (Object proxy, Method method, Object[] args) 
            throws Throwable {
                System.out.println(method.getName());
                //执行目标对象的方法
                return method.invoke(phpDeveloper ,args);
            }
        }

InvocationHandler接收三个参数

  • proxy,代理后的实例对象。
  • method,对象被调用方法。
  • args,调用时的参数。

使用Proxy.newProxyInstance()进行动态代理

public void enCode() {
        Developer developerProxy=(Developer) JavaReflect
        .getProxy(new PhpDeveloper());
        developerProxy.code();
        developerProxy.debug();
        developerProxy.comment();

    }

    private static Object getProxy(final Object target) {
        return  Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), 
        (proxy, method, args) ->
                {
                    System.out.println("方法前置处理");
                    Object object=method.invoke(target,args);
                    System.out.println("方法后置处理");
                    return  object;
                });
    }

到这里,也明白了为什么使用JDK动态代理,被代理的目标对象必须实现接口,因为只有实现了接口,代理类才知道目标对象调用的方法,接口中没有的方法,代理类就无法执行。那有没有代理没有实现接口的类的办法呢?也是有的,就是使用cglib库代理。

使用CGLib进行动态代理

前面说了,有没有代理没有实现接口的类的办法呢?也是有的,就是使用CGLib库实现代理。因为JDK动态代理是基于接口的方式;而CGLib动态代理是代理类去继承目标类,然后重写其中目标类的方法啊,这样去保证代理类拥有目标类的同名方法的,需要生成代理类,所以叫Code Generation Library(代码生成库)。
正是由于CGLib可以代理未实现接口的类,所以功能显然比JDK动态代理要强大,所以很多地方都会用到这个库去实现代理。

CGLib的使用

和JDK动态代理不同,作为一个外部库,CGLib需要引入依赖。

使用Gradle引入CGLib 3.3依赖

dependencies {
    implementation group: 'cglib', name: 'cglib', version: '3.3.0'
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

编写一个目标类,作为被代理的对象
作为CGLib的代理目标对象,不需要实现接口

public class JavaDeveloper {
    public void code() {
        System.out.println("dev javacode");
    }

    public void debug() {
        System.out.println("debug java code");
    }

    public void comment() {
        System.out.println("Java是一种主流的,有些啰嗦和臃肿的语言.");
    }
}

编写一个本地拦截器LocalMethodInterceptor 实现对目标对象的方法调用时拦截 并进行增强
这个的作用就等于JDK动态代理的InvocationHandler

public class LocalMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("进入本地拦截器 前置处理");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("退出本地拦截器 后置处理");
        return result;
    }
}

进行动态代理

        /*
        DebuggingClassWriter 这个类时用来生成代理类的
        所以设置其属性以指定生成动态代理类的存放目录
         */
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, ".\\build\\classes");

        //创建Enhancer对象,类似于JDK动态代理的Proxy类
        Enhancer enhancer = new Enhancer();
        //设置目标类的字节码文件
        enhancer.setSuperclass(JavaDeveloper.class);
        //设置回调函数
        enhancer.setCallback(new LocalMethodInterceptor());

        //这里的creat方法就是正式创建代理类
        JavaDeveloper proxyDev = (JavaDeveloper)enhancer.create();
        //调用代理类的方法
        proxyDev.code();
        proxyDev.debug();
        proxyDev.comment();

结果

进入本地拦截器 前置处理
dev java code
退出本地拦截器 后置处理
进入本地拦截器 前置处理
debug java code
退出本地拦截器 后置处理
进入本地拦截器 前置处理
Java是一种主流的,有些啰嗦和臃肿的语言.
退出本地拦截器 后置处理

到这里就完成对一个目标对象的静态代理了,可以看出,不是很复杂。
只要传入一个目标对象的类属性,设置好要增强的拦截器,就可以对一个类进行动态增强了。

原理解析

生成代理类时,CGLib底层使用了ASM字节码生成库来操作字节码,ASM编织完字节码后,CGLib上层使用字节码来生成新的类。ASM使用类似SAX的解析器来实现高性能的生成字节码,一般情况下我们不需要去直接使用ASM,因为使用它需要对Java字节码的格式足够的了解。

在Java 8之前的JDK动态代理,使用反射的方式去生成代理类,性能相比CGLib有些差距,但是Java 8以来,JDK也改用编织字节码的方式来生成代理类,两者的性能已经所差无几。所以使用CGLib更看重的是其提供了对未实现接口的类代理功能。

查看一下设置的代码生成目录,可以找到生成的代理类
生成的代理类
可以看到生成了三个相关代理类

一个是代理类 JavaDeveloper$$EnhancerByCGLIB$$e3631778 带有增强(Enhance)标志的是代理类
剩下两个一个是代理类的FastClass 另一个是目标对象的FastClass
FastClass是快速访问方法类 可以理解为代理方法的索引 详情后面再解释

打开代理类 JavaDeveloper$$EnhancerByCGLIB$$e3631778.class
Idea有反编译机制 可以直接看到源码

public class JavaDeveloper$$EnhancerByCGLIB$$e3631778 extends JavaDeveloper 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 MethodInterceptor CGLIB$CALLBACK_0;
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$debug$0$Method;
    private static final MethodProxy CGLIB$debug$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$comment$1$Method;
    private static final MethodProxy CGLIB$comment$1$Proxy;
    private static final Method CGLIB$code$2$Method;
    private static final MethodProxy CGLIB$code$2$Proxy;
    private static final Method CGLIB$equals$3$Method;
    private static final MethodProxy CGLIB$equals$3$Proxy;
    private static final Method CGLIB$toString$4$Method;
    private static final MethodProxy CGLIB$toString$4$Proxy;
    private static final Method CGLIB$hashCode$5$Method;
    private static final MethodProxy CGLIB$hashCode$5$Proxy;
    private static final Method CGLIB$clone$6$Method;
    private static final MethodProxy CGLIB$clone$6$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("or.g.dyj.kotlin.learn.JavaDeveloper$$EnhancerByCGLIB$$e3631778");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"debug", "()V", "comment", "()V", "code", "()V"}, (var1 = Class.forName("or.g.dyj.kotlin.learn.JavaDeveloper")).getDeclaredMethods());
        CGLIB$debug$0$Method = var10000[0];
        CGLIB$debug$0$Proxy = MethodProxy.create(var1, var0, "()V", "debug", "CGLIB$debug$0");
        CGLIB$comment$1$Method = var10000[1];
        CGLIB$comment$1$Proxy = MethodProxy.create(var1, var0, "()V", "comment", "CGLIB$comment$1");
        CGLIB$code$2$Method = var10000[2];
        CGLIB$code$2$Proxy = MethodProxy.create(var1, var0, "()V", "code", "CGLIB$code$2");
        var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$3$Method = var10000[0];
        CGLIB$equals$3$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$3");
        CGLIB$toString$4$Method = var10000[1];
        CGLIB$toString$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$4");
        CGLIB$hashCode$5$Method = var10000[2];
        CGLIB$hashCode$5$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$5");
        CGLIB$clone$6$Method = var10000[3];
        CGLIB$clone$6$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$6");
    }

    final void CGLIB$debug$0() {
        super.debug();
    }

    public final void debug() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$debug$0$Method, CGLIB$emptyArgs, CGLIB$debug$0$Proxy);
        } else {
            super.debug();
        }
    }

    final void CGLIB$comment$1() {
        super.comment();
    }

    public final void comment() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$comment$1$Method, CGLIB$emptyArgs, CGLIB$comment$1$Proxy);
        } else {
            super.comment();
        }
    }

    final void CGLIB$code$2() {
        super.code();
    }

    public final void code() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$code$2$Method, CGLIB$emptyArgs, CGLIB$code$2$Proxy);
        } else {
            super.code();
        }
    }

    final boolean CGLIB$equals$3(Object var1) {
        return super.equals(var1);
    }

    public final boolean equals(Object var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            Object var2 = var10000.intercept(this, CGLIB$equals$3$Method, new Object[]{var1}, CGLIB$equals$3$Proxy);
            return var2 == null ? false : (Boolean)var2;
        } else {
            return super.equals(var1);
        }
    }

    final String CGLIB$toString$4() {
        return super.toString();
    }

    public final String toString() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$4$Method, CGLIB$emptyArgs, CGLIB$toString$4$Proxy) : super.toString();
    }

    final int CGLIB$hashCode$5() {
        return super.hashCode();
    }

    public final int hashCode() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            Object var1 = var10000.intercept(this, CGLIB$hashCode$5$Method, CGLIB$emptyArgs, CGLIB$hashCode$5$Proxy);
            return var1 == null ? 0 : ((Number)var1).intValue();
        } else {
            return super.hashCode();
        }
    }

    final Object CGLIB$clone$6() throws CloneNotSupportedException {
        return super.clone();
    }

    protected final Object clone() throws CloneNotSupportedException {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? var10000.intercept(this, CGLIB$clone$6$Method, CGLIB$emptyArgs, CGLIB$clone$6$Proxy) : super.clone();
    }

    public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
        String var10000 = var0.toString();
        switch(var10000.hashCode()) {
        case -508378822:
            if (var10000.equals("clone()Ljava/lang/Object;")) {
                return CGLIB$clone$6$Proxy;
            }
            break;
        case 547749954:
            if (var10000.equals("debug()V")) {
                return CGLIB$debug$0$Proxy;
            }
            break;
        case 899095734:
            if (var10000.equals("comment()V")) {
                return CGLIB$comment$1$Proxy;
            }
            break;
        case 941787752:
            if (var10000.equals("code()V")) {
                return CGLIB$code$2$Proxy;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return CGLIB$equals$3$Proxy;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return CGLIB$toString$4$Proxy;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return CGLIB$hashCode$5$Proxy;
            }
        }

        return null;
    }

    public JavaDeveloper$$EnhancerByCGLIB$$e3631778() {
        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;
    }

    private static final void CGLIB$BIND_CALLBACKS(Object var0) {
        JavaDeveloper$$EnhancerByCGLIB$$e3631778 var1 = (JavaDeveloper$$EnhancerByCGLIB$$e3631778)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;
                }
            }

            var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
        }

    }

    public Object newInstance(Callback[] var1) {
        CGLIB$SET_THREAD_CALLBACKS(var1);
        JavaDeveloper$$EnhancerByCGLIB$$e3631778 var10000 = new JavaDeveloper$$EnhancerByCGLIB$$e3631778();
        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Callback var1) {
        CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
        JavaDeveloper$$EnhancerByCGLIB$$e3631778 var10000 = new JavaDeveloper$$EnhancerByCGLIB$$e3631778();
        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
        CGLIB$SET_THREAD_CALLBACKS(var3);
        JavaDeveloper$$EnhancerByCGLIB$$e3631778 var10000 = new JavaDeveloper$$EnhancerByCGLIB$$e3631778;
        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);
        MethodInterceptor var10000;
        switch(var1) {
        case 0:
            var10000 = this.CGLIB$CALLBACK_0;
            break;
        default:
            var10000 = null;
        }

        return var10000;
    }

    public void setCallback(int var1, Callback var2) {
        switch(var1) {
        case 0:
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
        default:
        }
    }

    public Callback[] getCallbacks() {
        CGLIB$BIND_CALLBACKS(this);
        return new Callback[]{this.CGLIB$CALLBACK_0};
    }

    public void setCallbacks(Callback[] var1) {
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
    }

    static {
        CGLIB$STATICHOOK1();
    }
}

以code()方法为例,发现代理类中有code()方法,还有CGLIB$code$2$Proxy这个代理方法

CGLIB$code$2$Proxy 这个代理方法代理了什么呢?

是由MethodProxy方法代理创建的CGLIB$code$2()这个方法的索引(为什么这样创建后面再说)
CGLIB$code$2()方法就是调用的super.code()
代理类继承了目标对象,所以super.code()调用的就是JavaDeveloper.code()方法

	 CGLIB$code$2$Proxy = MethodProxy.create(var1, var0, "()V", "code", "CGLIB$code$2");

    final void CGLIB$code$2() {
        super.code();
    }

三个方法代理索引 可以看出是按照字母顺序排列 命令原理就是 CGLIB$方法名$排序号$Proxy 这么个模式
这样一看是不是就很清楚了,这种奇怪的名字也不难理解了

    private static final MethodProxy CGLIB$debug$0$Proxy;
    private static final MethodProxy CGLIB$comment$1$Proxy;
    private static final MethodProxy CGLIB$code$2$Proxy;

方法代理索引CGLIB$xxx$2$Proxy,方法引用CGLIB$xxx$数字都是这样命名的。

看一下code方法的详情

    public final void code() {
    	//方法拦截器的引用
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        /*
        如果方法拦截器为空 就是说如果没有设置增强切面
        则直接执行原来的方法
        */
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }
		/*
		 如果方法拦截器不为空 
        则调用代理类去执行
		*/
        if (var10000 != null) {
            var10000.intercept(this, CGLIB$code$2$Method, CGLIB$emptyArgs, CGLIB$code$2$Proxy);
        } else {
            super.code();
        }
    }

存在方法拦截器的情况下 代理类会调用方法拦截器去执行目标对象的方法,向拦截器传入三个参数
分别是代理类方法,执行时的入参,代理的目标对象的方法。

看了以上这些后,可以发现代理类中主要有这几部分:

  • 1.重写的父类方法 就是目标对象的真实方法
  • 2.CGLIB$debug$0CGLIB$comment$1CGLIB$code$2这种目标对象方法的引用
  • 3. CGLIB$debug$0$ProxyCGLIB$comment$1ProxyCGLIB$code$2Proxy 这种目标对象方法的索引
  • 4.Interceptor() 拦截器方法,
  • 5.newInstance和get/setCallback方法

这样子代理类已经解析完毕了
那FastClass快速方法类是干什么的?竟然还有两个。
这其实是CGLib的一种索引机制,称为FastClass机制。

为什么要用这种机制呢?因为相比直接使用反射来调用方法,性能较差,
所以CGLib库选择了FastClass机制来实现高性能的代理调用。

我们从一开始梳理下调用链

首先找到调用部分

        JavaDeveloper proxyDev = (JavaDeveloper)enhancer.create();
        //调用代理类的方法
        proxyDev.code();

从前面知识可知 proxyDev 就是代理类 继承了JavaDeveloper类
代理类
可以看到proxyDevd的实际类型就是生成的代理类 JavaDeveloper$$EnhancerByCGLIB$$e3631778
之前解读过代理类的源码 知道执行代理的方法时 如果存在拦截器 则先执行拦截器
就是之前写的拦截器切面增强方法

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable 	  {
        System.out.println("进入本地拦截器 前置处理");
        //proxy.invokeSuper中调用了FastClass的索引方法
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("退出本地拦截器 后置处理");
        return result;
    }

刚才的代码生成中,生成了两个FastClass类
目标对象的FastClass JavaDeveloper$$FastClassByCGLIB$$987a353c
代理类的FastClass JavaDeveloper$$EnhancerByCGLIB$$e3631778$$FastClassByCGLIB$$b4a5b8bc
但是 在代理类中却找不到调用的地方 那么是在哪里调用的呢?
回到本地方法拦截器中的代理执行语句
Object result = proxy.invokeSuper(obj, args);
调用的是MethodProxy的invokeSuper方法
此时就能找到 实际调用FastClass的地方了

    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

在此处打断点,执行一下,执行时的FastClass是 JavaDeveloper$$EnhancerByCGLIB$$e3631778$$FastClassByCGLIB$$b4a5b8bc
fc
那这个FastClass是在init()方法中被赋值的
我们看一下init()方法

    private void init()
    {
    	/**
    	这里使用个带锁的双重检测单例法
    	*/
        if (fastClassInfo == null)
        {
            synchronized (initLock)
            {
                if (fastClassInfo == null)
                {
                    CreateInfo ci = createInfo;
					
                    FastClassInfo fci = new FastClassInfo();
                    fc1就是目标对象FastClass fc2就是代理对象的FastClass
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    这里就是通过索引获取真实调用的方法 sig1="code()V" sig2="CGLIB$code$2()V"
                    所以fci.i1就是目标对象方法的索引 fci.i2 就是代理对象方法的索引
                    fci.i1 = fci.f1.getIndex(sig1);
                    fci.i2 = fci.f2.getIndex(sig2);
                    fastClassInfo = fci;
                    createInfo = null;
                }
            }
        }
    }

我们看下代理对象的获取索引方法

	Signature var1就是方法签名 code方法传入的方法签名是code()V
        public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        传入签名方法进行散列 然后根据散列值去匹配索引 code方法的索引是15
        switch(var10000.hashCode()) {
        case -2128373160:
            if (var10000.equals("CGLIB$debug$1()V")) {
                return 10;
            }
            break;
        case -2071771415:
            if (var10000.equals("CGLIB$clone$6()Ljava/lang/Object;")) {
                return 8;
            }
            break;
        case -2055565910:
            if (var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
                return 4;
            }
            break;
        case -1663710620:
            if (var10000.equals("CGLIB$equals$3(Ljava/lang/Object;)Z")) {
                return 13;
            }
            break;
        case -1606711239:
            if (var10000.equals("CGLIB$code$0()V")) {
                return 6;
            }
            break;
        case -1457535688:
            if (var10000.equals("CGLIB$STATICHOOK1()V")) {
                return 9;
            }
            break;
        case -1411783143:
            if (var10000.equals("CGLIB$hashCode$5()I")) {
                return 7;
            }
            break;
        case -894172689:
            if (var10000.equals("newInstance(Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
                return 20;
            }
            break;
        case -623122092:
            if (var10000.equals("CGLIB$findMethodProxy(Lnet/sf/cglib/core/Signature;)Lnet/sf/cglib/proxy/MethodProxy;")) {
                return 14;
            }
            break;
        case -508378822:
            if (var10000.equals("clone()Ljava/lang/Object;")) {
                return 19;
            }
            break;
        case -419626537:
            if (var10000.equals("setCallbacks([Lnet/sf/cglib/proxy/Callback;)V")) {
                return 0;
            }
            break;
        case 547749954:
            if (var10000.equals("debug()V")) {
                return 23;
            }
            break;
        case 560567118:
            if (var10000.equals("setCallback(ILnet/sf/cglib/proxy/Callback;)V")) {
                return 3;
            }
            break;
        case 811063227:
            if (var10000.equals("newInstance([Ljava/lang/Class;[Ljava/lang/Object;[Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
                return 21;
            }
            break;
        case 899095734:
            if (var10000.equals("comment()V")) {
                return 24;
            }
            break;
        case 941787752:
            if (var10000.equals("code()V")) {
                return 15;
            }
            break;
        case 973717575:
            if (var10000.equals("getCallbacks()[Lnet/sf/cglib/proxy/Callback;")) {
                return 1;
            }
            break;
        case 1221173700:
            if (var10000.equals("newInstance([Lnet/sf/cglib/proxy/Callback;)Ljava/lang/Object;")) {
                return 22;
            }
            break;
        case 1230699260:
            if (var10000.equals("getCallback(I)Lnet/sf/cglib/proxy/Callback;")) {
                return 2;
            }
            break;
        case 1506978155:
            if (var10000.equals("CGLIB$comment$2()V")) {
                return 11;
            }
            break;
        case 1584330438:
            if (var10000.equals("CGLIB$SET_STATIC_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
                return 5;
            }
            break;
        case 1729170762:
            if (var10000.equals("CGLIB$toString$4()Ljava/lang/String;")) {
                return 12;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return 16;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return 17;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return 18;
            }
        }

        return -1;
    }

此时fci.i2=23 然后执行invoke()方法下面 我们来看下代理类的FastClass 的invoke方法是什么
应该是code()方法

 public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        e3631778 var10000 = (e3631778)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                return new Boolean(var10000.equals(var3[0]));
            case 1:
                return var10000.toString();
            case 2:
                return new Integer(var10000.hashCode());
            case 3:
                return var10000.clone();
            case 4:
                return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
            case 5:
                return var10000.newInstance((Callback[])var3[0]);
            case 6:
                return var10000.newInstance((Callback)var3[0]);
            case 7:
                var10000.debug();
                return null;
            case 8:
                var10000.comment();
                return null;
            case 9:
                var10000.code();
                return null;
            case 10:
                var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
                return null;
            case 11:
                e3631778.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
                return null;
            case 12:
                e3631778.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
                return null;
            case 13:
                var10000.setCallbacks((Callback[])var3[0]);
                return null;
            case 14:
                return var10000.getCallbacks();
            case 15:
                return var10000.getCallback(((Number)var3[0]).intValue());
            case 16:
                return var10000.CGLIB$toString$4();
            case 17:
                var10000.CGLIB$comment$1();
                return null;
            case 18:
                e3631778.CGLIB$STATICHOOK1();
                return null;
            case 19:
                return new Boolean(var10000.CGLIB$equals$3(var3[0]));
            case 20:
                var10000.CGLIB$debug$0();
                return null;
            case 21:
                return new Integer(var10000.CGLIB$hashCode$5());
            case 22:
                return var10000.CGLIB$clone$6();
            case 23:
                var10000.CGLIB$code$2();
                return null;
            case 24:
                return e3631778.CGLIB$findMethodProxy((Signature)var3[0]);
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

上面说 fc.i2=23 ,那么此时执行的就是case 23;也就是var10000.CGLIB$code$2();
var10000就是代理类e36178,所以此时就在执行代理类的CGLIB$code$2()方法
上面说过 CGLIB$code$2()方法就是调用的super.code()
代理类继承了目标对象,所以super.code()调用的就是JavaDeveloper.code()方法
此时就调用到了目标对象的方法

综上,就是这样一个调用链

我们调用代理类门面的代理方法,比如proxyDev.code()
实际执行的是代理类JavaDeveloper$$EnhancerByCGLIB$$e3631778.code()方法
JavaDeveloper$$EnhancerByCGLIB$$e3631778.code()方法又通过调用
本地拦截器LocalMethodInterceptor执行方法增强,本地方法拦截器通过方法代理
MethodProxy.invokeSuper()方法去匹配真正要执行的目标对象方法
而目标对象方法的引用保持在代理类JavaDeveloper$$EnhancerByCGLIB$$e3631778.code()
通过向FastClass对象JavaDeveloper$$EnhancerByCGLIB$$e3631778$$FastClassByCGLIB$$b4a5b8bc
传入hash索引 匹配到真正的目标对象方法

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值