JDK、CGLIB、Spring三种实现代理的区别(二)CGLIB中proxy动态代理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sunnycoco05/article/details/78853148

CGLIB中的动态代理是JDK proxy的一个很好的补充,在JDK中实现代理时,要求代理类必须是继承接口的类,因为JDK最后生成的proxy class其实就是实现了被代理类所继承的接口并且继承了java中的Proxy类,通过反射找到接口的方法,调用InvocationHandler的invoke 方法实现拦截。CGLIb中最后生成的proxy class是一个继承被代理类的class,通过重写被代理类中的非final的方法实现代理。总结为:
JDK proxy:代理类必须实现接口
CGLIB: 代理类不能是final,代理的方法也不能是final(继承限制)
关于JDK proxy原理,可参看之前的整理
http://blog.csdn.net/sunnycoco05/article/details/78845878
下面看看CGLIb中的处理,我们要实现对一个类进行代理,在调用方法前后进行简单处理
创建一个要代理的类:
有一个printName类方法

public class MyTarget {

    public void printName() {
        System.err.println("name:Target-");
    }

自己的拦截器,CGLIb中实现MethodInterceptor接口,它可支持对不同方法的拦截,这里我们统一处理:

public class MyInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] params, MethodProxy proxy) throws Throwable {
        System.err.println("=======before======");
        Object res = proxy.invokeSuper(obj, params);
        System.err.println("=======and======");
        return res;
    }
}

只是在方法调用前后进行简单打印。CGLIb中通过Enhancer类进行代理操作,开始测试:

@Test
    public void proxyTest() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(MyTarget.class);
        enhancer.setCallback(new MyInterceptor());
        MyTarget target = (MyTarget) enhancer.create();
        target.printName();
        System.out.println("proxy class name:" + target.getClass().getName());
    }

运行结果:
这里写图片描述

成功实现拦截。
动态代理,其实就是动态的生成一个有关被代理类的class文件,并加装运行这个class文件的过程。如果我们可以把JVM中生成的class 文件保存下来,再通过反编译后的Java文件去分析,应该就很清楚的去看懂它的原理。我们可以用-javaagent去实现这个功能。自定义一个agent 类, 这个Agent在class文件装载的时候,我们判断是否是生成的代理类(根据class name判断),如果符合,将class的字节流写入文件,存在指定位置

public class MyAgent implements ClassFileTransformer {
    //会在main方法之前执行,添加自定义的的ClassFileTransformer
    public static void premain(String agentArgs, Instrumentation inst) {
        inst.addTransformer(new MyAgent());
    }

    //每次装载class文件都会执行
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        //com.cglib.MyTarget$$EnhancerByCGLIB$$65288428
        //com.sun.proxy.$Proxy4
        //CGLIB和JDK proxy生成的代理类进行拦截
        if (className.contains("$$EnhancerByCGLIB$$") || className.contains("$Proxy")) {
            //指定存放路径
            String path = "yourdirpath";
            int lastIndexOf = className.lastIndexOf("/") + 1;
            String classFileName = className.substring(lastIndexOf) + ".class";
            writeClassToDisk(path + classFileName, classfileBuffer);
            System.out.println(className + "---writeClassToDisk Succeess!");
        }
        return classfileBuffer;
    }

    //将class文件存在磁盘
    private void writeClassToDisk(String fileName, byte[] data) {
        try {
            File file = new File(fileName);
            if (!file.exists()) {
                file.createNewFile();
            }
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(data);
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

将MyAgent打成jar包myagent.jar 。在eclipse中的export功能中,选择工程中存在的MANIFEST.MF文件进行打包,MANIFEST.MF文件内容为:

Manifest-Version: 1.0
Premain-Class: com.cglib.MyAgent

表示版本号和Premain-Class所在的class文件
将 jar包放入项目路径下。
测试类:

public class AgentTest {
    //main方法中运行
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(MyTarget.class);
        enhancer.setCallback(new MyInterceptor());
        MyTarget myTarget = (MyTarget) enhancer.create();
         myTarget.printName();
        System.err.println("proxy name: " + myTarget.getClass().getName());
    }
}

字eclipse中,右键->Run As >Run configurations,在Arguments中添加:-javaagent:myagent.jar
这里写图片描述
运行:
这里写图片描述
从命名可以看到,MyTarget$$EnhancerByCGLIB$$b0fb28f5就是我们要找的MyTarget类的代理类,而运行中又加载了一个com/cglib/MyTarget$$EnhancerByCGLIB$$b0fb28f5$$FastClassByCGLIB$$1a454ece的class文件。
现在我们已经把cglib生成的class文件保留了,反编译查看代码:

package com.cglib;

import com.cglib.MyTarget;
import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//继承了MyTarget实现了Factory接口
public class MyTarget$$EnhancerByCGLIB$$b0fb28f5 extends MyTarget implements Factory {
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$printName$0$Method;
    private static final MethodProxy CGLIB$printName$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$finalize$1$Method;
    private static final MethodProxy CGLIB$finalize$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;
    //初始化一堆方法参数组等
    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class arg = Class.forName("com.cglib.MyTarget$$EnhancerByCGLIB$$b0fb28f5");
        Class arg0;
        Method[] arg9999 = ReflectUtils.findMethods(
                new String[]{"finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;",
                        "hashCode", "()I", "clone", "()Ljava/lang/Object;"},
                (arg0 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = arg9999[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create(arg0, arg, "()V", "finalize", "CGLIB$finalize$1");
        CGLIB$equals$2$Method = arg9999[1];
        CGLIB$equals$2$Proxy = MethodProxy.create(arg0, arg, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        CGLIB$toString$3$Method = arg9999[2];
        CGLIB$toString$3$Proxy = MethodProxy.create(arg0, arg, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        CGLIB$hashCode$4$Method = arg9999[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create(arg0, arg, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = arg9999[4];
        CGLIB$clone$5$Proxy = MethodProxy.create(arg0, arg, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        CGLIB$printName$0$Method = ReflectUtils.findMethods(new String[]{"printName", "()V"},
                (arg0 = Class.forName("com.cglib.MyTarget")).getDeclaredMethods())[0];
        CGLIB$printName$0$Proxy = MethodProxy.create(arg0, arg, "()V", "printName", "CGLIB$printName$0");
    }

    final void CGLIB$printName$0() {
        super.printName();
    }
    //重写MyTarget的printName方法
    public final void printName() {
        MethodInterceptor arg9999 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            arg9999 = this.CGLIB$CALLBACK_0;
        }

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

    final void CGLIB$finalize$1() throws Throwable {
        super.finalize();
    }

    protected final void finalize() throws Throwable {
        MethodInterceptor arg9999 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            arg9999 = this.CGLIB$CALLBACK_0;
        }

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

    final boolean CGLIB$equals$2(Object arg0) {
        return super.equals(arg0);
    }

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

        if (arg9999 != null) {
            Object arg1 = arg9999.intercept(this, CGLIB$equals$2$Method, new Object[]{arg0}, CGLIB$equals$2$Proxy);
            return arg1 == null ? false : ((Boolean) arg1).booleanValue();
        } else {
            return super.equals(arg0);
        }
    }

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

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

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

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

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

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

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

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

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

    public static MethodProxy CGLIB$findMethodProxy(Signature arg) {
        String arg9999 = arg.toString();
        switch (arg9999.hashCode()) {
            case -1574182249 :
                if (arg9999.equals("finalize()V")) {
                    return CGLIB$finalize$1$Proxy;
                }
                break;
            case -508378822 :
                if (arg9999.equals("clone()Ljava/lang/Object;")) {
                    return CGLIB$clone$5$Proxy;
                }
                break;
            case 1826985398 :
                if (arg9999.equals("equals(Ljava/lang/Object;)Z")) {
                    return CGLIB$equals$2$Proxy;
                }
                break;
            case 1861880221 :
                if (arg9999.equals("printName()V")) {
                    return CGLIB$printName$0$Proxy;
                }
                break;
            case 1913648695 :
                if (arg9999.equals("toString()Ljava/lang/String;")) {
                    return CGLIB$toString$3$Proxy;
                }
                break;
            case 1984935277 :
                if (arg9999.equals("hashCode()I")) {
                    return CGLIB$hashCode$4$Proxy;
                }
        }

        return null;
    }

    public MyTarget$$EnhancerByCGLIB$$b0fb28f5() {
        CGLIB$BIND_CALLBACKS(this);
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] arg) {
        CGLIB$THREAD_CALLBACKS.set(arg);
    }

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] arg) {
        CGLIB$STATIC_CALLBACKS = arg;
    }

    private static final void CGLIB$BIND_CALLBACKS(Object arg) {
        MyTarget$$EnhancerByCGLIB$$b0fb28f5 arg0 = (MyTarget$$EnhancerByCGLIB$$b0fb28f5) arg;
        if (!arg0.CGLIB$BOUND) {
            arg0.CGLIB$BOUND = true;
            Object arg9999 = CGLIB$THREAD_CALLBACKS.get();
            if (arg9999 == null) {
                arg9999 = CGLIB$STATIC_CALLBACKS;
                if (CGLIB$STATIC_CALLBACKS == null) {
                    return;
                }
            }

            arg0.CGLIB$CALLBACK_0 = (MethodInterceptor) ((Callback[]) arg9999)[0];
        }

    }

    public Object newInstance(Callback[] arg0) {
        CGLIB$SET_THREAD_CALLBACKS(arg0);
        MyTarget$$EnhancerByCGLIB$$b0fb28f5 arg9999 = new MyTarget$$EnhancerByCGLIB$$b0fb28f5();
        CGLIB$SET_THREAD_CALLBACKS((Callback[]) null);
        return arg9999;
    }

    public Object newInstance(Callback arg0) {
        CGLIB$SET_THREAD_CALLBACKS(new Callback[]{arg0});
        MyTarget$$EnhancerByCGLIB$$b0fb28f5 arg9999 = new MyTarget$$EnhancerByCGLIB$$b0fb28f5();
        CGLIB$SET_THREAD_CALLBACKS((Callback[]) null);
        return arg9999;
    }

    public Object newInstance(Class[] arg0, Object[] arg1, Callback[] arg2) {
        // $FF: Couldn't be decompiled
    }

    public Callback getCallback(int arg0) {
        CGLIB$BIND_CALLBACKS(this);
        MethodInterceptor arg9999;
        switch (arg0) {
            case 0 :
                arg9999 = this.CGLIB$CALLBACK_0;
                break;
            default :
                arg9999 = null;
        }

        return arg9999;
    }

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

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

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

    static {
        CGLIB$STATICHOOK1();
    }
}

CGLIB生成的class文件中,部分不能被反编译,但对我们理解它的代理原理起到很大作用了。从中我们看到,继承了MyTaget通过重写定义方法啦实现代理,也就说明CGLIB不能用作代理final标识的类和方法。在调用printName方法时,最终调用传入的MethodInterceptor实例的intercept方法,也就是我们的MyInterceptor。具体的传入和逻辑可根据源码分析

展开阅读全文

没有更多推荐了,返回首页