java动态代理模式之Cglib动态代理机制

JDK动态代理是基于接口的,代理类必须实现相同的接口,然而这么做有时候感觉有点不爽啊,要改来改去,总归有点不方便。那么,有没有不需要实现相同接口就能实现动态代理的东西呢。Java作为一种字节码的解释性语言,怎么可能没有这玩意。没错,文章标题的Cglib动态代理就实现了这种功能,当然了,这玩意一样是基于jvm的,在运行期弄的。没办法,java在编译期就想弄出来,着实有点搞不过来。
首先,不着急,写个简单的用例先。

/**
 * 受代理的目标class
 */
class Hello {
    public void caca() {
        System.out.println("caca");
    }

    public void say() {
        System.out.println("say");
    }
}
/**
 * 实现MethodInterceptor 接口的代理class
 */
class CglibProxy implements MethodInterceptor {
    public void before() {
        System.out.println("before----------");
    }

    public void after() {
        System.out.println("after----------");
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy methodProxy) throws Throwable {
        before();
        methodProxy.invokeSuper(obj, args);
        after();
        return null;
    }
}
/**
 * main方法入口
 */
public static void main(String[] args) {
    //调用Enhancer的create方法,生成代理
    Hello hello = (Hello)Enhancer.create(Hello.class, new CglibProxy());
    hello.say();
}

输出结果:

before----------
say
after----------

就那么短短几行,就生成了代理,是不是觉得很简单。而且,我不管怎么改动Hello类,其代理接口都不需要变动,着实比JDK动态代理强多了,其耦合性大大减少。说笑了,JDK 动态代理和Cglib动态代理各有优劣,都有合适的场景。
下面看一看该Cglib代理的生成流程吧。
为了更好地说明create方法的内部原理,我把create内部的方法给反射出来调用了。没错,你没听错,我反射出来调用了。原本想要放源码包Debug的,但是一时神经,就反射出来调用了,没想到调用起来这么麻烦。
下面就先贴上我傻乎乎反射调用写出来的代码。

/**
 * 反射调用工具类,看名字不像,别慌,随便起的
 */
class ClassObj {
    private Object object;
    private Class<?> objClass;

    public ClassObj(Object object) {
        this.object = object;
        this.objClass = object.getClass();
    }

    /**
     * 通过获取方法名和参数类型获取方法
     */
    public Method getMethod(String methodName, Class<?>... args)
            throws NoSuchMethodException, SecurityException {
        //为图方便直接复制粘贴了三遍try...catch,测试可以,实际可别这么做
        try {
            return objClass.getDeclaredMethod(methodName, args);
        } catch (Exception e1) {
            try {
                return objClass.getSuperclass().getDeclaredMethod(methodName,
                        args);
            } catch (Exception e2) {
                try {
                    return objClass.getSuperclass().getSuperclass()
                            .getDeclaredMethod(methodName, args);
                } catch (Exception e3) {
                    return objClass.getSuperclass().getSuperclass()
                            .getSuperclass().getSuperclass()
                            .getDeclaredMethod(methodName, args);

                }

            }
        }
    }

    /**
     * 获取并调用方法
     */
    @SuppressWarnings("rawtypes")
    public Object invokeMethod(String methodName, Class[] classes,
            Object... args) throws NoSuchMethodException, SecurityException,
            IllegalAccessException, IllegalArgumentException,
            InvocationTargetException {
        //setAccessible放入getMethod比较合适,我只是随便写,勿仿这风格
        Method method = getMethod(methodName, classes);
        method.setAccessible(true);
        return method.invoke(object, args);
    }

    /**
     * 获取并调用方法
     */
    public Object invokeMethod(String methodName) throws NoSuchMethodException,
            SecurityException, IllegalAccessException,
            IllegalArgumentException, InvocationTargetException {
        return invokeMethod(methodName, new Class[] {}, new Object[] {});
    }

    /**
     * 获取域属性
     */
    public Field getFiled(String fieldName) throws NoSuchFieldException,
            SecurityException {
        //为图方便直接复制粘贴了三遍try...catch,测试可以,实际可别这么做
        try {
            return objClass.getDeclaredField(fieldName);
        } catch (Exception e1) {
            try {
                return objClass.getSuperclass().getDeclaredField(fieldName);
            } catch (Exception e2) {
                return objClass.getSuperclass().getSuperclass()
                        .getDeclaredField(fieldName);
            }
        }
    }

    /*
     * 获取域值
     */
    public Object getValue(String fieldName) throws IllegalArgumentException,
            IllegalAccessException, NoSuchFieldException, SecurityException {
        //setAccessible放入getFiled比较合适,我只是随便写,勿仿这风格
        Field field = getFiled(fieldName);
        field.setAccessible(true);
        return field.get(object);
    }

    /*
     * 设置域值
     */
    public void setValue(String fieldName, Object obj)
            throws IllegalArgumentException, IllegalAccessException,
            NoSuchFieldException, SecurityException {
        //setAccessible放入getFiled比较合适,我只是随便写,勿仿这风格
        Field field = getFiled(fieldName);
        field.setAccessible(true);
        field.set(this.object, obj);
    }
}

下面贴上我将Enhancer.create里面挖出来的main方法,我将Enhancer.create方法以反射的形式写在main方法当中,当然了,限于篇幅,这里只贴上反射后的主逻辑的代码,其它什么缓存啊,线程隔离啊等等等等的代码我全删掉了。

    public static void main(String[] args) throws Exception {
        //hello最后将会被用来指向生成的代理对象
        //初始化Enhancer,并生成我们自己的ClassObj对象,利用该对象完成对Enhancer对象的操纵
        //字节数组b后面将会被用来存放生成的未加载的Class的信息
        //gen后面将用来存放加载后的代理对象的class
        //cache2是一个HashMap,后面过程中进行缓存的
        Hello hello = null;
        Enhancer e = new Enhancer();
        ClassObj eo = new ClassObj(e);
        byte[] b;
        Class gen = null;
        Map cache2 = new HashMap();

        /*
         * 这里设置一些基本信息
         * 并且将我们的Hello.class这个class信息对象传入
         * 这时候,Enhancer就知道了所要代理的class信息
         */
        e.setSuperclass(Hello.class);
        e.setCallback(new CglibProxy());
        eo.setValue("classOnly", false);
        eo.setValue("argumentTypes", null);
        eo.invokeMethod("validate");
        eo.invokeMethod("setNamePrefix", new Class[] { String.class },
                ((Class) eo.getValue("superclass")).getName());

        /**
         * 获取EnhancerKey的实现对象,这个生成看似很长,但是别慌
         * 也就是利用一些属性生成一个Key而已
         */
        Object key = ((EnhancerKey) eo.getValue("KEY_FACTORY")).newInstance(
                ((Class) eo.getValue("superclass")).getName(),
                ReflectUtils.getNames(((Class[]) eo.getValue("interfaces"))),
                (CallbackFilter) eo.getValue("filter"),
                (Type[]) eo.getValue("callbackTypes"),
                (boolean) eo.getValue("useFactory"),
                (boolean) eo.getValue("interceptDuringConstruction"),
                (Long) eo.getValue("serialVersionUID"));

        synchronized (eo.getValue("source")) {
            //获取类加载器
            //该ClassLoader被用做key,在生成class的字节数组通过该key获取相关信息来生成字节码
            ClassLoader loader = (ClassLoader) eo
                    .invokeMethod("getClassLoader");
            ClassObj source = new ClassObj(eo.getValue("source"));
            Map cache = (Map) source.getValue("cache");
            cache2.put(eo.getValue("NAME_KEY"), new HashSet());
            cache.put(loader, cache2);


            if (gen == null) {
                //设置键
                //生成字节数组b的时候需要用到
                eo.setValue("key", key);
                if (gen == null) {
                    //将我们的传入的代理对象的信息转成字节码存入byte[]数组里面
                    //可以将字节数组b的值写入到文件当中,然后反编译可以看到生成的代理的代码信息
                    GeneratorStrategy strategy = (GeneratorStrategy) eo
                            .getValue("strategy");
                    b = strategy.generate(e);

                    //读取字节码里的className信息
                    String className = ClassNameReader
                            .getClassName(new ClassReader(b));
                    //加载字节数组b里存的class信息
                    //这里生成目标代理的class
                    gen = ReflectUtils.defineClass(className, b, loader);
                }
                //实例化代理对象
                hello = (Hello) eo.invokeMethod("firstInstance",
                        new Class[] { Class.class }, gen);

                //当然了,利用下面也可以实例化,删除了一些细节,因此仅限于本例
                //hello = (Hello) gen.newInstance();
                //Method method = gen.getDeclaredMethod("setCallbacks",Callback[].class);
                //method.invoke(hello,new Object[] { new Callback[] { new CglibProxy() } });

                //最后输出验证结果
                hello.say();
            }
        }
    }
}

输出结果如下,一切正常

before----------
say
after----------

下面是反编译代理类后的基本信息

public class com.my.other.Hello$$EnhancerByCGLIB$$65187392 extends com.my.other.Hello implements net.sf.cglib.proxy.Factory {
  private boolean CGLIB$BOUND;
  private static final java.lang.ThreadLocal CGLIB$THREAD_CALLBACKS;
  private static final net.sf.cglib.proxy.Callback[] CGLIB$STATIC_CALLBACKS;
  private net.sf.cglib.proxy.MethodInterceptor CGLIB$CALLBACK_0;
  private static final java.lang.reflect.Method CGLIB$say$0$Method;
  private static final net.sf.cglib.proxy.MethodProxy CGLIB$say$0$Proxy;
  private static final java.lang.Object[] CGLIB$emptyArgs;
  private static final java.lang.reflect.Method CGLIB$caca$1$Method;
  private static final net.sf.cglib.proxy.MethodProxy CGLIB$caca$1$Proxy;
  private static final java.lang.reflect.Method CGLIB$finalize$2$Method;
  private static final net.sf.cglib.proxy.MethodProxy CGLIB$finalize$2$Proxy;
  private static final java.lang.reflect.Method CGLIB$equals$3$Method;
  private static final net.sf.cglib.proxy.MethodProxy CGLIB$equals$3$Proxy;
  private static final java.lang.reflect.Method CGLIB$toString$4$Method;
  private static final net.sf.cglib.proxy.MethodProxy CGLIB$toString$4$Proxy;
  private static final java.lang.reflect.Method CGLIB$hashCode$5$Method;
  private static final net.sf.cglib.proxy.MethodProxy CGLIB$hashCode$5$Proxy;
  private static final java.lang.reflect.Method CGLIB$clone$6$Method;
  private static final net.sf.cglib.proxy.MethodProxy CGLIB$clone$6$Proxy;
  static void CGLIB$STATICHOOK1();
  final void CGLIB$say$0();
  public final void say();
  final void CGLIB$caca$1();
  public final void caca();
  final void CGLIB$finalize$2() throws java.lang.Throwable;
  protected final void finalize() throws java.lang.Throwable;
  final boolean CGLIB$equals$3(java.lang.Object);
  public final boolean equals(java.lang.Object);
  final java.lang.String CGLIB$toString$4();
  public final java.lang.String toString();
  final int CGLIB$hashCode$5();
  public final int hashCode();
  final java.lang.Object CGLIB$clone$6() throws java.lang.CloneNotSupportedException;
  protected final java.lang.Object clone() throws java.lang.CloneNotSupportedException;
  public static net.sf.cglib.proxy.MethodProxy CGLIB$findMethodProxy(net.sf.cglib.core.Signature);
  public com.my.other.Hello$$EnhancerByCGLIB$$65187392();
  public static void CGLIB$SET_THREAD_CALLBACKS(net.sf.cglib.proxy.Callback[]);
  public static void CGLIB$SET_STATIC_CALLBACKS(net.sf.cglib.proxy.Callback[]);
  private static final void CGLIB$BIND_CALLBACKS(java.lang.Object);
  public java.lang.Object newInstance(net.sf.cglib.proxy.Callback[]);
  public java.lang.Object newInstance(net.sf.cglib.proxy.Callback);
  public java.lang.Object newInstance(java.lang.Class[], java.lang.Object[], net.sf.cglib.proxy.Callback[]);
  public net.sf.cglib.proxy.Callback getCallback(int);
  public void setCallback(int, net.sf.cglib.proxy.Callback);
  public net.sf.cglib.proxy.Callback[] getCallbacks();
  public void setCallbacks(net.sf.cglib.proxy.Callback[]);
  static {};
}

从上面可以看出,该class的方法和成员贼多,至少比JDK的动态代理多很多,因此,用Cglib初始化代理相对JDK动态代理,在初始化对象的时候,慢很多。不过对象出来后就没什么了。所以Cglib比较适合初始化的阶段使用。附上生成代理对象的class样例文件http://download.csdn.net/detail/qwe125698420/9629960 额外话:另外大家有什么好的反编译器可以推荐下,用的几款反编译工具最近反编译老失败。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值