cglib 订阅
CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成。 展开全文
CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate支持它来实现PO(Persistent Object 持久化对象)字节码的动态生成。
信息
概    述
运行期扩展Java类与实现Java接口
简    称
cglib
解    释
一个开源项目
外文名
Code Generation Library
cglibCGLIB包的介绍
代理为控制要访问的目标对象提供了一种途径。当访问对象时,它引入了一个间接的层。JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。JDK的动态代理用起来非常简单,但它有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的继承的类,该怎么办?现在我们可以使用CGLIB包CGLIB是一个强大的高性能的代码生成包。它广泛的被许多AOP的框架使用,例如Spring AOP和dynaop,为他们提供方法的interception(拦截)。最流行的OR Mapping工具hibernate也使用CGLIB来代理单端single-ended(多对一和一对一)关联(对集合的延迟抓取,是采用其他机制实现的)。EasyMock和jMock是通过使用模仿(mock)对象来测试java代码的包。它们都通过使用CGLIB来为那些没有接口的类创建模仿(mock)对象。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。除了CGLIB包,脚本语言例如Groovy和BeanShell,也是使用ASM来生成java的字节码。当然不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
收起全文
精华内容
下载资源
问答
  • cglib

    2018-06-15 08:00:55
    CGLIB CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟...

    CGLIB

    CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架,同样使用CGLIB来代理单端(多对一和一对一)关联(延迟提取集合使用的另一种机制)。CGLIB作为一个开源项目,地址为:https://github.com/cglib/cglib

    主要使用方式:
    Enhancer:允许代理扩展一个具体的基类,还可以实现实现接口,动态生成的子类覆盖超类的非final方法(final不能被继承,重写),并具有回调用户定义的拦截器的钩子(callback方法)
    最常用的callback是MethodInterceptor,实现AOP方法执行前后和抛出异常时执行(Around advice)–在调用超类方法之前之后都可以调用调用自定义代码。另外在调用超类的方法之前可以修改参数,或根本不调用它。

    Enhancer

     public class SampleClass2 {
        public String test(String input) {
            return "Hello world!";
        }
    
        @Test
        public void testFixedValue() throws Exception {
            Enhancer enhancer = new Enhancer();
            // 设置要代理的目标类
            enhancer.setSuperclass(SampleClass2.class);
            // 设置要代理的拦截器
            enhancer.setCallback(new FixedValue() {
                @Override
                public Object loadObject() throws Exception {
                    return "Hello cglib!";
                }
            });
            // 生成代理类的实例 
            SampleClass2 proxy = (SampleClass2) enhancer.create();
            assertEquals("Hello cglib!", proxy.test(null));
        }
    }

    KeyFactory:主要处理Maps 和 Sets集合,适用于多键值对

    public class KeySample {
    //实现一个newInstance的接口,返回值是Object(必须)
        private interface MyFactory {
            public Object newInstance(int a, char[] b, String d);
        }
        public static void main(String[] args) {
            MyFactory f = (MyFactory)KeyFactory.create(MyFactory.class);
            Object key1 = f.newInstance(20, new char[]{ 'a', 'b' }, "hello");
            Object key2 = f.newInstance(20, new char[]{ 'a', 'b' }, "hello");
            Object key3 = f.newInstance(20, new char[]{ 'a', '_' }, "hello");
            System.out.println(key1.equals(key2));
            System.out.println(key2.equals(key3));
        }
    }
    展开全文
  • CGlib

    2018-05-02 11:54:47
    CGLIB和Java动态代理的区别 1.Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机 制不允许多重继承) CGLIB能够代理普通类 2.Java动态代理使用J...

    转载:https://blog.csdn.net/danchu/article/details/70238002

    CGLIB和Java动态代理的区别

    1.Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机 制不允许多重继承)
    CGLIB能够代理普通类

    2.Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;
    CGLIB使用ASM框架直接对字节码进行操作,在 类的执行过程中比较高效

    展开全文
  • CGLIB

    2017-11-29 17:01:10
    CGLIB原理:动态生成一个要代理类的子类,子类重写代理类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入模切逻辑。它比使用java反射的JDK动态代理要快。 CGLIB底层:使用字节...

    原理

    CGLIB原理:动态生成一个要代理类的子类,子类重写代理类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入模切逻辑。它比使用java反射的JDK动态代理要快。

    CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因数它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

     

    CGLIB的API

    1.jar包

    cglib-nodep.jar:不需要关联asm的jar包,jar包内部包含了asm的类。

    cglib.jar:需要关联asm的jar包。

     

    2.CGLIB类库

    由于基本代码很少,学起来有一定的困难,主要是缺少文档和示例,这也是CGLIB的一个不足之处。

    net.sf.cglib.core:底层字节码处理类,他们大部分与ASM有关系 。

    net.sf.cglib.transform:编译期或运行期类和类文件的转换。

    net.sf.cglib.proxy:实现创建代理和方法拦截器类。

    net.sf.cglib.reflect:实现快速反射和C#风格代理的类

    net.sf.cglib.util:集合排序等工具类

    net.sf.cglibbeans:JavaBean相关的工具类。

     

    下面介绍使用MethodInterceptor和Enhance实现一个动态代理。

    一 JDK中的动态代理

    JDK中的动态代理是通过反射类Proxy以及InvocationHandler回调接口实现的,但是,JDK中所要进行动态代理的类必须要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。

     

    二 使用CGLIB实现

    (1)被代理类

    package com.zghw.cglib;  
      
    /** 
     * 没有实现接口,需要CGlib动态代理的目标类 
     *  
     * @author zghw 
     * 
     */  
    public class TargetObject {  
        public String method1(String paramName) {  
            return paramName;  
        }  
      
        public int method2(int count) {  
            return count;  
        }  
      
        public int method3(int count) {  
            return count;  
        }  
      
        @Override  
        public String toString() {  
            return "TargetObject []"+ getClass();  
        }  
    }


    (2)拦截器

    在调用目标方法时,CGLIB会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口。

    package com.zghw.cglib;  
      
    import java.lang.reflect.Method;  
      
    import net.sf.cglib.proxy.MethodInterceptor;  
    import net.sf.cglib.proxy.MethodProxy;  
    /** 
     * 目标对象拦截器,实现MethodInterceptor 
     * @author zghw 
     * 
     */  
    public class TargetInterceptor implements MethodInterceptor{  
      
        /** 
         * 重写方法拦截在方法前和方法后加入业务 
         * Object obj为目标对象 
         * Method method为目标方法 
         * Object[] params 为参数, 
         * MethodProxy proxy CGlib方法代理对象 
         */  
        @Override  
        public Object intercept(Object obj, Method method, Object[] params,  
                MethodProxy proxy) throws Throwable {  
            System.out.println("调用前");  
            Object result = proxy.invokeSuper(obj, params);  
            System.out.println(" 调用后"+result);  
            return result;  
        }  
      
      
    }  

     

    (3)生成动态代理类

     

    package com.zghw.cglib;  
      
    import net.sf.cglib.proxy.Callback;  
    import net.sf.cglib.proxy.CallbackFilter;  
    import net.sf.cglib.proxy.Enhancer;  
    import net.sf.cglib.proxy.NoOp;  
      
    public class TestCglib {  
        public static void main(String args[]) {  
            Enhancer enhancer =new Enhancer();  
            enhancer.setSuperclass(TargetObject.class);  
            enhancer.setCallback(new TargetInterceptor());  
            TargetObject targetObject2=(TargetObject)enhancer.create();  
            System.out.println(targetObject2);  
            System.out.println(targetObject2.method1("mmm1"));  
            System.out.println(targetObject2.method2(100));  
            System.out.println(targetObject2.method3(200));  
        }  
    }


    这里Enhance类是CGLib中的一个这节码增强器,它可以方便的对你想要处理的类进行扩展,以后会经常看到它。

    首先将被代理类TargetObject设置成父类,然后设置拦截器TargetInterceptor,最后执行enhance.create()动态生成一个代理类,并从Object强制转型成父类型 TargetObject。最后,在代理类上调用方法。

     

    (4)回调过滤器

    在CGLIB回调时可以设置对不同方法执行不同的回调逻辑,或者根本不执行回调。

    package com.zghw.cglib;  
      
    import java.lang.reflect.Method;  
      
    import net.sf.cglib.proxy.CallbackFilter;  
    /** 
     * 回调方法过滤 
     * @author zghw 
     * 
     */  
    public class TargetMethodCallbackFilter implements CallbackFilter {  
      
        /** 
         * 过滤方法 
         * 返回的值为数字,代表了Callback数组中的索引位置,要到用的Callback 
         */  
        @Override  
        public int accept(Method method) {  
            if(method.getName().equals("method1")){  
                System.out.println("filter method1 ==0");  
                return 0;  
            }  
            if(method.getName().equals("method2")){  
                System.out.println("filter method2 ==1");  
                return 1;  
            }  
            if(method.getName().equals("method3")){  
                System.out.println("filter method3 ==2");  
                return 2;  
            }  
            return 0;  
        }  
      
    }  

     

    package com.zghw.cglib;  
      
    import net.sf.cglib.proxy.Callback;  
    import net.sf.cglib.proxy.CallbackFilter;  
    import net.sf.cglib.proxy.Enhancer;  
    import net.sf.cglib.proxy.NoOp;  
      
    public class TestCglib {  
        public static void main(String args[]) {  
            Enhancer enhancer =new Enhancer();  
            enhancer.setSuperclass(TargetObject.class);  
            CallbackFilter callbackFilter = new TargetMethodCallbackFilter();  
              
            /** 
             * (1)callback1:方法拦截器 
               (2)NoOp.INSTANCE:这个NoOp表示no operator,即什么操作也不做,代理类直接调用被代理的方法不进行拦截。 
               (3)FixedValue:表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值。 
             */  
            Callback noopCb=NoOp.INSTANCE;  
            Callback callback1=new TargetInterceptor();  
            Callback fixedValue=new TargetResultFixed();  
            Callback[] cbarray=new Callback[]{callback1,noopCb,fixedValue};  
            //enhancer.setCallback(new TargetInterceptor());  
            enhancer.setCallbacks(cbarray);  
            enhancer.setCallbackFilter(callbackFilter);  
            TargetObject targetObject2=(TargetObject)enhancer.create();  
            System.out.println(targetObject2);  
            System.out.println(targetObject2.method1("mmm1"));  
            System.out.println(targetObject2.method2(100));  
            System.out.println(targetObject2.method3(100));  
            System.out.println(targetObject2.method3(200));  
        }  
    }

     

    package com.zghw.cglib;  
      
    import net.sf.cglib.proxy.FixedValue;  
    /**  
     * 表示锁定方法返回值,无论被代理类的方法返回什么值,回调方法都返回固定值。  
     * @author zghw  
     *  
     */  
    public class TargetResultFixed implements FixedValue{  
      
        /**  
         * 该类实现FixedValue接口,同时锁定回调值为999  
         * (整型,CallbackFilter中定义的使用FixedValue型回调的方法为getConcreteMethodFixedValue,该方法返回值为整型)。  
         */  
        @Override  
        public Object loadObject() throws Exception {  
            System.out.println("锁定结果");  
            Object obj = 999;  
            return obj;  
        }  
      
    }  


    (5)延迟加载对象

    LazyLoader接口继承了Callback,因此也算是CGLib中的一种Callback类型。另一种延迟加载接Dispatcher,Dispatcher同样继承于Callback,也是一种回调类型。不同的是:LazyLoader只在第一次访问延迟加载属性里触发代理类回调方法,而Dispatcher在每次访问加载属性时都会触发代理类回调方法。

     

    package com.zghw.cglib;  
      
    import net.sf.cglib.proxy.Enhancer;  
      
    public class LazyBean {  
        private String name;  
        private int age;  
        private PropertyBean propertyBean;  
        private PropertyBean propertyBeanDispatcher;  
      
        public LazyBean(String name, int age) {  
            System.out.println("lazy bean init");  
            this.name = name;  
            this.age = age;  
            this.propertyBean = createPropertyBean();  
            this.propertyBeanDispatcher = createPropertyBeanDispatcher();  
        }  
      
          
      
        /** 
         * 只第一次懒加载 
         * @return 
         */  
        private PropertyBean createPropertyBean() {  
            /** 
             * 使用cglib进行懒加载 对需要延迟加载的对象添加代理,在获取该对象属性时先通过代理类回调方法进行对象初始化。 
             * 在不需要加载该对象时,只要不去获取该对象内属性,该对象就不会被初始化了(在CGLib的实现中只要去访问该对象内属性的getter方法, 
             * 就会自动触发代理类回调)。 
             */  
            Enhancer enhancer = new Enhancer();  
            enhancer.setSuperclass(PropertyBean.class);  
            PropertyBean pb = (PropertyBean) enhancer.create(PropertyBean.class,  
                    new ConcreteClassLazyLoader());  
            return pb;  
        }  
        /** 
         * 每次都懒加载 
         * @return 
         */  
        private PropertyBean createPropertyBeanDispatcher() {  
            Enhancer enhancer = new Enhancer();  
            enhancer.setSuperclass(PropertyBean.class);  
            PropertyBean pb = (PropertyBean) enhancer.create(PropertyBean.class,  
                    new ConcreteClassDispatcher());  
            return pb;  
        }  
        public String getName() {  
            return name;  
        }  
      
        public void setName(String name) {  
            this.name = name;  
        }  
      
        public int getAge() {  
            return age;  
        }  
      
        public void setAge(int age) {  
            this.age = age;  
        }  
      
        public PropertyBean getPropertyBean() {  
            return propertyBean;  
        }  
      
        public void setPropertyBean(PropertyBean propertyBean) {  
            this.propertyBean = propertyBean;  
        }  
      
        public PropertyBean getPropertyBeanDispatcher() {  
            return propertyBeanDispatcher;  
        }  
      
        public void setPropertyBeanDispatcher(PropertyBean propertyBeanDispatcher) {  
            this.propertyBeanDispatcher = propertyBeanDispatcher;  
        }  
      
        @Override  
        public String toString() {  
            return "LazyBean [name=" + name + ", age=" + age + ", propertyBean="  
                    + propertyBean + "]";  
        }  
    }  
    package com.zghw.cglib;  
      
    public class PropertyBean {  
        private String key;  
        private Object value;  
        public String getKey() {  
            return key;  
        }  
        public void setKey(String key) {  
            this.key = key;  
        }  
        public Object getValue() {  
            return value;  
        }  
        public void setValue(Object value) {  
            this.value = value;  
        }  
        @Override  
        public String toString() {  
            return "PropertyBean [key=" + key + ", value=" + value + "]" +getClass();  
        }  
          
    }  

     

    package com.zghw.cglib;  
      
    import net.sf.cglib.proxy.LazyLoader;  
      
    public class ConcreteClassLazyLoader implements LazyLoader {  
        /** 
         * 对需要延迟加载的对象添加代理,在获取该对象属性时先通过代理类回调方法进行对象初始化。 
         * 在不需要加载该对象时,只要不去获取该对象内属性,该对象就不会被初始化了(在CGLib的实现中只要去访问该对象内属性的getter方法, 
         * 就会自动触发代理类回调)。 
         */  
        @Override  
        public Object loadObject() throws Exception {  
            System.out.println("before lazyLoader...");  
            PropertyBean propertyBean = new PropertyBean();  
            propertyBean.setKey("zghw");  
            propertyBean.setValue(new TargetObject());  
            System.out.println("after lazyLoader...");  
            return propertyBean;  
        }  
      
    }  

     

    package com.zghw.cglib;  
      
    import net.sf.cglib.proxy.Dispatcher;  
      
    public class ConcreteClassDispatcher implements Dispatcher{  
      
        @Override  
        public Object loadObject() throws Exception {  
            System.out.println("before Dispatcher...");  
            PropertyBean propertyBean = new PropertyBean();  
            propertyBean.setKey("xxx");  
            propertyBean.setValue(new TargetObject());  
            System.out.println("after Dispatcher...");  
            return propertyBean;  
        }  
      
    }  

     

     

    (6)接口生成器InterfaceMaker

    会动态生成一个接口,该接口包含指定类定义的所有方法。

     

    package com.zghw.cglib;  
      
    import java.lang.reflect.InvocationTargetException;  
    import java.lang.reflect.Method;  
      
    import net.sf.cglib.proxy.Enhancer;  
    import net.sf.cglib.proxy.InterfaceMaker;  
    import net.sf.cglib.proxy.MethodInterceptor;  
    import net.sf.cglib.proxy.MethodProxy;  
      
    public class TestInterfaceMaker {  
      
        public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {  
            InterfaceMaker interfaceMaker =new InterfaceMaker();  
            //抽取某个类的方法生成接口方法  
            interfaceMaker.add(TargetObject.class);  
            Class<?> targetInterface=interfaceMaker.create();  
            for(Method method : targetInterface.getMethods()){  
                System.out.println(method.getName());  
            }  
            //接口代理并设置代理接口方法拦截  
            Object object = Enhancer.create(Object.class, new Class[]{targetInterface}, new MethodInterceptor(){  
                @Override  
                public Object intercept(Object obj, Method method, Object[] args,  
                        MethodProxy methodProxy) throws Throwable {  
                    if(method.getName().equals("method1")){  
                        System.out.println("filter method1 ");  
                        return "mmmmmmmmm";  
                    }  
                    if(method.getName().equals("method2")){  
                        System.out.println("filter method2 ");  
                        return 1111111;  
                    }  
                    if(method.getName().equals("method3")){  
                        System.out.println("filter method3 ");  
                        return 3333;  
                    }  
                    return "default";  
                }});  
            Method targetMethod1=object.getClass().getMethod("method3",new Class[]{int.class});  
            int i=(int)targetMethod1.invoke(object, new Object[]{33});  
            Method targetMethod=object.getClass().getMethod("method1",new Class[]{String.class});  
            System.out.println(targetMethod.invoke(object, new Object[]{"sdfs"}));  
        }  
      
    }

    最后欢迎大家访问我的个人网站:1024s

    ​​​​​​​

    展开全文
  • Java两种动态代理JDK动态代理和CGLIB动态代理

    万次阅读 多人点赞 2018-08-07 15:33:35
    cglib动态代理 测试 代理模式 代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是“真正”实现...

    目录

    代理模式

    JDK动态代理

    cglib动态代理

    测试


    代理模式

    代理模式是23种设计模式的一种,他是指一个对象A通过持有另一个对象B,可以具有B同样的行为的模式。为了对外开放协议,B往往实现了一个接口,A也会去实现接口。但是B是“真正”实现类,A则比较“虚”,他借用了B的方法去实现接口的方法。A虽然是“伪军”,但它可以增强B,在调用B的方法前后都做些其他的事情。Spring AOP就是使用了动态代理完成了代码的动态“织入”。

    使用代理好处还不止这些,一个工程如果依赖另一个工程给的接口,但是另一个工程的接口不稳定,经常变更协议,就可以使用一个代理,接口变更时,只需要修改代理,不需要一一修改业务代码。从这个意义上说,所有调外界的接口,我们都可以这么做,不让外界的代码对我们的代码有侵入,这叫防御式编程。代理其他的应用可能还有很多。

    上述例子中,类A写死持有B,就是B的静态代理。如果A代理的对象是不确定的,就是动态代理。动态代理目前有两种常见的实现,jdk动态代理和cglib动态代理。

    JDK动态代理

    jdk动态代理是jre提供给我们的类库,可以直接使用,不依赖第三方。先看下jdk动态代理的使用代码,再理解原理。

    首先有个“明星”接口类,有唱、跳两个功能:

    package proxy;
    
    public interface Star
    {
        String sing(String name);
        
        String dance(String name);
    }
    

    再有个明星实现类“刘德华”:

    package proxy;
    
    public class LiuDeHua implements Star
    {   
        @Override
        public String sing(String name)
        {
             System.out.println("给我一杯忘情水");
    
            return "唱完" ;
        }
        
        @Override
        public String dance(String name)
        {
            System.out.println("开心的马骝");
    
            return "跳完" ;
        }
    }
    

    明星演出前需要有人收钱,由于要准备演出,自己不做这个工作,一般交给一个经纪人。便于理解,它的名字以Proxy结尾,但他不是代理类,原因是它没有实现我们的明星接口,无法对外服务,它仅仅是一个wrapper。

    package proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class StarProxy implements InvocationHandler
    {
        // 目标类,也就是被代理对象
        private Object target;
        
        public void setTarget(Object target)
        {
            this.target = target;
        }
        
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            // 这里可以做增强
            System.out.println("收钱");
            
            Object result = method.invoke(target, args);
            
            return result;
        }
        
        // 生成代理类
        public Object CreatProxyedObj()
        {
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        }  
       
    }
    

    上述例子中,方法CreatProxyedObj返回的对象才是我们的代理类,它需要三个参数,前两个参数的意思是在同一个classloader下通过接口创建出一个对象,该对象需要一个属性,也就是第三个参数,它是一个InvocationHandler。需要注意的是这个CreatProxyedObj方法不一定非得在我们的StarProxy类中,往往放在一个工厂类中。上述代理的代码使用过程一般如下:

    1、new一个目标对象

    2、new一个InvocationHandler,将目标对象set进去

    3、通过CreatProxyedObj创建代理对象,强转为目标对象的接口类型即可使用,实际上生成的代理对象实现了目标接口。

            Star ldh = new LiuDeHua();
    
            StarProxy proxy = new StarProxy();
    
            proxy.setTarget(ldh); 
      
            Object obj = proxy.CreatProxyedObj();
            
            Star star = (Star)obj;

    Proxy(jdk类库提供)根据B的接口生成一个实现类,我们成为C,它就是动态代理类(该类型是 $Proxy+数字 的“新的类型”)。生成过程是:由于拿到了接口,便可以获知接口的所有信息(主要是方法的定义),也就能声明一个新的类型去实现该接口的所有方法,这些方法显然都是“虚”的,它调用另一个对象的方法。当然这个被调用的对象不能是对象B,如果是对象B,我们就没法增强了,等于饶了一圈又回来了。

    所以它调用的是B的包装类,这个包装类需要我们来实现,但是jdk给出了约束,它必须实现InvocationHandler,上述例子中就是StarProxy, 这个接口里面有个方法,它是所有Target的所有方法的调用入口(invoke),调用之前我们可以加自己的代码增强。

    看下我们的实现,我们在InvocationHandler里调用了对象B(target)的方法,调用之前增强了B的方法。

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
        {
            // 这里增强
            System.out.println("收钱");
            
            Object result = method.invoke(target, args);
            
            return result;
        }

    所以可以这么认为C代理了InvocationHandler,InvocationHandler代理了我们的类B,两级代理。

    整个JDK动态代理的秘密也就这些,简单一句话,动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是一个InvocationHandler,该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强。从原理可以看出,JDK动态代理是“对象”的代理。

    下面看下动态代理类到底如何调用的InvocationHandler的,为什么InvocationHandler的一个invoke方法能为分发target的所有方法。C中的部分代码示例如下,通过反编译生成后的代码查看,摘自链接地址。Proxy创造的C是自己(Proxy)的子类,且实现了B的接口,一般都是这么修饰的:

    public final class XXX extends Proxy implements XXX
    

    一个方法代码如下:

    
      public final String SayHello(String paramString)
      {
        try
        {
          return (String)this.h.invoke(this, m4, new Object[] { paramString });
        }
        catch (Error|RuntimeException localError)
        {
          throw localError;
        }
        catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
    

    可以看到,C中的方法全部通过调用h实现,其中h就是InvocationHandler,是我们在生成C时传递的第三个参数。这里还有个关键就是SayHello方法(业务方法)跟调用invoke方法时传递的参数m4一定要是一一对应的,但是这些对我们来说都是透明的,由Proxy在newProxyInstance时保证的。留心看到C在invoke时把自己this传递了过去,InvocationHandler的invoke的第一个方法也就是我们的动态代理实例类,业务上有需要就可以使用它。(所以千万不要在invoke方法里把请求分发给第一个参数,否则很明显就死循环了)

    C类中有B中所有方法的成员变量

      private static Method m1;
      private static Method m3;
      private static Method m4;
      private static Method m2;
      private static Method m0;
    

    这些变量在static静态代码块初始化,这些变量是在调用invocationhander时必要的入参,也让我们依稀看到Proxy在生成C时留下的痕迹。

    static
      {
        try
        {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]);
          m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") });
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          return;
        }
        catch (NoSuchMethodException localNoSuchMethodException)
        {
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException localClassNotFoundException)
        {
          throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
      }
    

    从以上分析来看,要想彻底理解一个东西,再多的理论不如看源码,底层的原理非常重要。

    jdk动态代理类图如下

    cglib动态代理

    我们了解到,“代理”的目的是构造一个和被代理的对象有同样行为的对象,一个对象的行为是在类中定义的,对象只是类的实例。所以构造代理,不一定非得通过持有、包装对象这一种方式。

    通过“继承”可以继承父类所有的公开方法,然后可以重写这些方法,在重写时对这些方法增强,这就是cglib的思想。根据里氏代换原则(LSP),父类需要出现的地方,子类可以出现,所以cglib实现的代理也是可以被正常使用的。

    先看下代码

    package proxy;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class CglibProxy implements MethodInterceptor
    {
        // 根据一个类型产生代理类,此方法不要求一定放在MethodInterceptor中
        public Object CreatProxyedObj(Class<?> clazz)
        {
            Enhancer enhancer = new Enhancer();
            
            enhancer.setSuperclass(clazz);
            
            enhancer.setCallback(this);
            
            return enhancer.create();
        }
        
        @Override
        public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
        {
            // 这里增强
            System.out.println("收钱");
            
            return arg3.invokeSuper(arg0, arg2);
        } 
    }
    

    从代码可以看出,它和jdk动态代理有所不同,对外表现上看CreatProxyedObj,它只需要一个类型clazz就可以产生一个代理对象, 所以说是“类的代理”,且创造的对象通过打印类型发现也是一个新的类型。不同于jdk动态代理,jdk动态代理要求对象必须实现接口(三个参数的第二个参数),cglib对此没有要求。

    cglib的原理是这样,它生成一个继承B的类型C(代理类),这个代理类持有一个MethodInterceptor,我们setCallback时传入的。 C重写所有B中的方法(方法名一致),然后在C中,构建名叫“CGLIB”+“$父类方法名$”的方法(下面叫cglib方法,所有非private的方法都会被构建),方法体里只有一句话super.方法名(),可以简单的认为保持了对父类方法的一个引用,方便调用。

    这样的话,C中就有了重写方法、cglib方法、父类方法(不可见),还有一个统一的拦截方法(增强方法intercept)。其中重写方法和cglib方法肯定是有映射关系的。

    C的重写方法是外界调用的入口(LSP原则),它调用MethodInterceptor的intercept方法,调用时会传递四个参数,第一个参数传递的是this,代表代理类本身,第二个参数标示拦截的方法,第三个参数是入参,第四个参数是cglib方法,intercept方法完成增强后,我们调用cglib方法间接调用父类方法完成整个方法链的调用。

    这里有个疑问就是intercept的四个参数,为什么我们使用的是arg3而不是arg1?

        @Override
        public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable
        {
            System.out.println("收钱");
            
            return arg3.invokeSuper(arg0, arg2);
        }

     因为如果我们通过反射 arg1.invoke(arg0, ...)这种方式是无法调用到父类的方法的,子类有方法重写,隐藏了父类的方法,父类的方法已经不可见,如果硬调arg1.invoke(arg0, ...)很明显会死循环。

    所以调用的是cglib开头的方法,但是,我们使用arg3也不是简单的invoke,而是用的invokeSuper方法,这是因为cglib采用了fastclass机制,不仅巧妙的避开了调不到父类方法的问题,还加速了方法的调用。

    fastclass基本原理是,给每个方法编号,通过编号找到方法执行避免了通过反射调用。

    对比JDK动态代理,cglib依然需要一个第三者分发请求,只不过jdk动态代理分发给了目标对象,cglib最终分发给了自己,通过给method编号完成调用。cglib是继承的极致发挥,本身还是很简单的,只是fastclass需要另行理解。

    参考

    https://blog.csdn.net/jiankunking/article/details/52143504

    http://www.php.cn/java-article-407212.html

    https://www.cnblogs.com/chinajava/p/5880887.html

    https://rejoy.iteye.com/blog/1627405

    测试

       public static void main(String[] args)
        {
            int times = 1000000;
            
            Star ldh = new LiuDeHua();
            StarProxy proxy = new StarProxy();
            proxy.setTarget(ldh);
            
            long time1 = System.currentTimeMillis();
            Star star = (Star)proxy.CreatProxyedObj();
            long time2 = System.currentTimeMillis();
            System.out.println("jdk创建时间:" + (time2 - time1));
            
            CglibProxy proxy2 = new CglibProxy();
            long time5 = System.currentTimeMillis();
            Star star2 = (Star)proxy2.CreatProxyedObj(LiuDeHua.class);
            long time6 = System.currentTimeMillis();
            System.out.println("cglib创建时间:" + (time6 - time5));
            
            long time3 = System.currentTimeMillis();
            for (int i = 1; i <= times; i++)
            {
                star.sing("ss");
                
                star.dance("ss");
            }
            long time4 = System.currentTimeMillis();
            System.out.println("jdk执行时间" + (time4 - time3));
            
            long time7 = System.currentTimeMillis();
            for (int i = 1; i <= times; i++)
            {
                star2.sing("ss");
                
                star2.dance("ss");
            }
            
            long time8 = System.currentTimeMillis();
            
            System.out.println("cglib执行时间" + (time8 - time7));   
        }

    经测试,jdk创建对象的速度远大于cglib,这是由于cglib创建对象时需要操作字节码。cglib执行速度略大于jdk,所以比较适合单例模式。另外由于CGLIB的大部分类是直接对Java字节码进行操作,这样生成的类会在Java的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory异常。spring默认使用jdk动态代理,如果类没有接口,则使用cglib。

    展开全文
  • CGLIB简介CGLIB简介CGLIB简介CGLIB简介CGLIB简介
  • cglib的依赖包

    2017-02-23 14:11:14
    cglib
  • java CGlib

    2020-12-26 17:28:05
    asm-2.2.3.jar asm-commons-2.2.3.jar asm-util-2.2.3.jar cglib-nodep-2.1_3.jar ...
  • Cglib的jar文件 Cglib.zip

    2020-07-16 16:13:37
    Cglib的jar文件,包含:asm.jar、asm-commons.jar、asm-tree.jar、cglib-2.2.jar 四个jar包
  • Spring的两种代理JDK和CGLIB的区别浅谈

    万次阅读 多人点赞 2016-08-04 17:39:49
    我的店铺 一、原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,...1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP...
  • 使用 cglib_CGlib

    2021-01-13 16:34:17
    CGLib(Code Generation Library)是一个强大的、高性能、高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。CGLib比Java的java.lang.reflect.Proxy 类更强大的地方在于它不仅可以接管接口类的方法,...
  • java通过cglib动态生成实体bean

    万次阅读 2021-01-08 15:50:49
    import net.sf.cglib.beans.BeanGenerator; import org.apache.commons.collections.map.MultiValueMap; import java.lang.reflect.*; import java.util.ArrayList; import java.util.HashMap; import java.util.It
  • cglib3.2.5

    2018-06-19 10:38:19
    spring源码需要cglib3.2.5,欢迎下载使用。确认好版本再下载
  • cglib3.2.10

    2018-12-20 22:19:44
    cglib的最新版本3.2.10,稳定的字节码生成工具,通过继承方式动态生成代理类的工具,很多应用在拦截、切片中。
  • 【Spring基础】CGLIB动态代理实现原理

    万次阅读 多人点赞 2018-06-09 18:11:19
    CGLIB介绍 CGLIB(Code Generation Library)是一个开源项目!是一个强大的,高性能,高质量的Code生成类库, 它可以在运行期扩展Java类与实现Java接口。Hibernate用它来实现PO(Persistent Object 持久化对象)...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 18,105
精华内容 7,242
关键字:

cglib