• CGLIB学习笔记


    0 概述

    CGLIB基于ASM实现。提供比反射更为强大的动态特性。使用CGLIB可以非常方便的实现的动态代理。

    0.1 CGLIB包结构

    • net.sf.cglib.core    底层字节码处理类。
    • net.sf.cglib.transform    该包中的类用于class文件运行时转换或编译时转换。
    • net.sf.cglib.proxy    该包中的类用于创建代理和方法拦截。
    • net.sf.cglib.reflect    该包中的类用于快速反射,并提供了C#风格的委托。
    • net.sf.cglib.util    集合排序工具类。
    • net.sf.cglib.beans    JavaBean工具类。

    1 使用CGLIB实现动态代理

    1.1 CGLIB代理相关的类

    • net.sf.cglib.proxy.Enhancer    主要的增强类。
    • net.sf.cglib.proxy.MethodInterceptor    主要的方法拦截类,它是Callback接口的子接口,需要用户实现。
    • net.sf.cglib.proxy.MethodProxy    JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用。

    cglib是通过动态的生成一个子类去覆盖所要代理类的非final方法,并设置好callback,则原有类的每个方法调用就会转变成调用用户定义的拦截方法(interceptors)。

    CGLIB代理相关的常用API如下图所示:

    net.sf.cglib.proxy.Callback接口在CGLIB包中是一个重要的接口,所有被net.sf.cglib.proxy.Enhancer类调用的回调(callback)接口都要继承这个接口。

    net.sf.cglib.proxy.MethodInterceptor能够满足任何的拦截(interception )需要。对有些情况下可能过度。为了简化和提高性能,CGLIB包提供了一些专门的回调(callback)类型:

    • net.sf.cglib.proxy.FixedValue 为提高性能,FixedValue回调对强制某一特别方法返回固定值是有用的。
    • net.sf.cglib.proxy.NoOp NoOp回调把对方法调用直接委派到这个方法在父类中的实现。
    • net.sf.cglib.proxy.LazyLoader 当实际的对象需要延迟装载时,可以使用LazyLoader回调。一旦实际对象被装载,它将被每一个调用代理对象的方法使用。
    • net.sf.cglib.proxy.Dispatcher Dispathcer回调和LazyLoader回调有相同的特点,不同的是,当代理方法被调用时,装载对象的方法也总要被调用。
    • net.sf.cglib.proxy.ProxyRefDispatcher ProxyRefDispatcher回调和Dispatcher一样,不同的是,它可以把代理对象作为装载对象方法的一个参数传递。

    1.2 CGLIB动态代理的基本原理

    CGLIB动态代理的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数,如图

    在intercept()函数里,除执行代理类的原因方法,在原有方法前后加入其他需要实现的过程,改变原有方法的参数值,即可以实现对原有类的代理了。这似于AOP中的around advice。

    1.3 使用MethodInterceptor接口实现方法回调

    当对代理中所有方法的调用时,都会转向MethodInterceptor类型的拦截(intercept)方法,在拦截方法中再调用底层对象相应的方法。下面我们举个例子,假设你想对目标对象的所有方法调用进行权限的检查,如果没有经过授权,就抛出一个运行时的异常。

    net.sf.cglib.proxy.MethodInterceptor接口是最通用的回调(callback)类型,它经常被基于代理的AOP用 来实现拦截(intercept)方法的调用。

    MethodInterceptor接口只定义了一个方法:

    public Object intercept(Object object, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable;

    参数Object object是被代理对象,不会出现死循环的问题。

    参数java.lang.reflect.Method method是java.lang.reflect.Method类型的被拦截方法。

    参数Object[] args是被被拦截方法的参数。

    参数MethodProxy proxy是CGLIB提供的MethodProxy 类型的被拦截方法。

    注意:

    1、若原方法的参数存在基本类型,则对于第三个参数Object[] args会被转化成类的类型。如原方法的存在一个参数为int,则在intercept方法中,对应的会存在一个Integer类型的参数。

    2、若原方法为final方法,则MethodInterceptor接口无法拦截该方法。

    1.3.1 实现MethodInterceptor接口

    class MethodInterceptorImpl implements MethodInterceptor {

        @Override    

        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

            System.out.println("Before invoke " + method);

            Object result = proxy.invokeSuper(obj, args);

            System.out.println("After invoke" + method);

            return result;

        }

    }

    Object result=proxy.invokeSuper(o,args); 表示调用原始类的被拦截到的方法。这个方法的前后添加需要的过程。在这个方法中,我们可以在调用原方法之前或之后注入自己的代码。 

    由于性能的原因,对原始方法的调用使用CGLIB的net.sf.cglib.proxy.MethodProxy对象,而不是反射中一般使用java.lang.reflect.Method对象。

    1.4 使用CGLIB代理最核心类Enhancer生成代理对象

    net.sf.cglib.proxy.Enhancer中有几个常用的方法:

    • void setSuperclass(java.lang.Class superclass) 设置产生的代理对象的父类。
    • void setCallback(Callback callback) 设置CallBack接口的实例。
    • void setCallbacks(Callback[] callbacks) 设置多个CallBack接口的实例。
    • void setCallbackFilter(CallbackFilter filter) 设置方法回调过滤器。
    • Object create() 使用默认无参数的构造函数创建目标对象。
    • Object create(Class[], Object[]) 使用有参数的构造函数创建目标对象。参数Class[] 定义了参数的类型,第二个Object[]是参数的值。

    注意:在参数中,基本类型应被转化成类的类型。

    基本代码:

    public Object createProxy(Class targetClass) {

        Enhancer enhancer = new Enhancer();

        enhancer.setSuperclass(targetClass);

        enhancer.setCallback(new MethodInterceptorImpl ());

        return enhancer.create();

    }

    createProxy方法返回值是targetClass的一个实例的代理。

    1.5 使用CGLIB继进行动态代理示例

    例1:使用CGLIB生成代理的基本使用。

    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 TestMain {

        public static void main(String[] args) {

            Enhancer enhancer = new Enhancer();

            enhancer.setSuperclass(Cglib.class);

            enhancer.setCallback(new HelloProxy());

            Cglib cglibProxy = (Cglib)enhancer.create();

            cglibProxy.cglib();

        }

    }

    class Cglib{

        public void cglib(){

            System.out.println("CGLIB");

        }

    }

    class HelloProxy implements MethodInterceptor{

        @Override

        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

            System.out.println("Hello");

            Object object = proxy.invokeSuper(obj, args);

            System.out.println("Powerful!");

            return object;

        }

    }

    输出内容:

    Hello

    CGLIB

    Powerful!

    例2:使用CGLIB创建一个Dao工厂,并展示一些基本特性。

    public interface Dao {

        void add(Object o);

        void add(int i);

        void add(String s);

    }

    public class DaoImpl implements Dao {

        @Override

        public void add(Object o) {

            System.out.println("add(Object o)");

        }

        @Override

        public void add(int i) {

            System.out.println("add(int i)");

        }

        public final void add(String s) {

            System.out.println("add(String s)");

        }

    }

    public class Proxy implements MethodInterceptor {

        @Override

        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

            System.out.println("拦截前...");

            // 输出参数类型

            for (Object arg : args) {

                System.out.print(arg.getClass() + ";");

            }

            Object result = proxy.invokeSuper(obj, args);

            System.out.println("拦截后...");

            return result;

        }

    }

    public class DaoFactory {

        public static Dao create() {

            Enhancer enhancer = new Enhancer();

            enhancer.setSuperclass(DaoImpl.class);

            enhancer.setCallback(new Proxy());

            Dao dao = (Dao) enhancer.create();

            return dao;

        }

    }

    public class TestMain {

        public static void main(String[] args) {

            Dao dao = DaoFactory.create();

            dao.add(new Object());

            dao.add(1);

            dao.add("1");

        }

    }

    输出内容:

    拦截前...

    class java.lang.Object;add(Object o)

    拦截后...

    拦截前...

    class java.lang.Integer;add(int i)

    拦截后...

    add(String s)

    2 回调过滤器CallbackFilter

    net.sf.cglib.proxy.CallbackFilter有选择的对一些方法使用回调。

    CallbackFilter可以实现不同的方法使用不同的回调方法。所以CallbackFilter称为"回调选择器"更合适一些。

    CallbackFilter中的accept方法,根据不同的method返回不同的值i,这个值是在callbacks中callback对象的序号,就是调用了callbacks[i]。

    import java.lang.reflect.Method;

    import net.sf.cglib.proxy.Callback;

    import net.sf.cglib.proxy.CallbackFilter;

    import net.sf.cglib.proxy.Enhancer;

    import net.sf.cglib.proxy.MethodInterceptor;

    import net.sf.cglib.proxy.MethodProxy;

    import net.sf.cglib.proxy.NoOp;

    public class CallbackFilterDemo {

        public static void main(String[] args) {

            // 回调实例数组

            Callback[] callbacks = new Callback[] { new MethodInterceptorImpl(), NoOp.INSTANCE };

            // 使用enhancer,设置相关参数。

            Enhancer enhancer = new Enhancer();

            enhancer.setSuperclass(User.class);

            enhancer.setCallbacks(callbacks);

            enhancer.setCallbackFilter(new CallbackFilterImpl());

            // 产生代理对象

            User proxyUser = (User) enhancer.create();

            proxyUser.pay(); // 买

            proxyUser.eat(); // 吃

        }

        /**

         * 回调过滤器类。

         */

        private static class CallbackFilterImpl implements CallbackFilter {

            @Override

            public int accept(Method method) {

                String methodName = method.getName();

                if ("eat".equals(methodName)) {

                    return 1; // eat()方法使用callbacks[1]对象拦截。

                } else if ("pay".equals(methodName)) {

                    return 0; // pay()方法使用callbacks[0]对象拦截。

                }

                return 0;

            }

        }

        /**

         * 自定义回调类。

         */

        private static class MethodInterceptorImpl implements MethodInterceptor {

            @Override

            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

                System.out.println("Before invoke " + method);

                Object result = proxy.invokeSuper(obj, args); // 原方法调用。

                System.out.println("After invoke" + method);

                return result;

            }

        }

    }

    class User {

        public void pay() {

            System.out.println("买东西");

        }

        public void eat() {

            System.out.println("吃东西");

        }

    }

    输出结果:

    Before invoke public void sjq.cglib.filter.User.pay()

    pay()

    After invokepublic void sjq.cglib.filter.User.pay()

    eat()

    3 CGLIB对Mixin的支持

    CGLIB的代理包net.sf.cglib.proxy.Mixin类提供对Minix编程的支持。Minix允许多个对象绑定到一个单个的大对象上。在代理中对方法的调用委托到下面相应的对象中。 这是一种将多个接口混合在一起的方式, 实现了多个接口。

    Minix是一种多继承的替代方案, 很大程度上解决了多继承的很多问题, 实现和理解起来都比较容易。

    import net.sf.cglib.proxy.Mixin;

    public class MixinDemo {

        public static void main(String[] args) {

            //接口数组

            Class<?>[] interfaces = new Class[] { MyInterfaceA.class, MyInterfaceB.class };

              

            //实例对象数组

            Object[] delegates = new Object[] { new MyInterfaceAImpl(), new MyInterfaceBImpl() };

              

            //Minix组合为o对象。

            Object o = Mixin.create(interfaces, delegates);

              

            MyInterfaceA a = (MyInterfaceA) o;

            a.methodA();

              

            MyInterfaceB b = (MyInterfaceB) o;

            b.methodB();

              

            System.out.println(" 输出Mixin对象的结构...");

            Class clazz = o.getClass();

            Method[] methods = clazz.getDeclaredMethods();

            for (int i = 0; i < methods.length; i++) {

                System.out.println(methods[i].getName());

            }

            System.out.println(clazz);

        }

    }

    interface MyInterfaceA {

        public void methodA();

    }

    interface MyInterfaceB {

        public void methodB();

    }

    class MyInterfaceAImpl implements MyInterfaceA {

        @Override

        public void methodA() {

            System.out.println("MyInterfaceAImpl.methodA()");

        }

    }

    class MyInterfaceBImpl implements MyInterfaceB {

        @Override

        public void methodB() {

            System.out.println("MyInterfaceBImpl.methodB()");

        }

    }

     

    输出结果:

    MyInterfaceAImpl.methodA()

    MyInterfaceBImpl.methodB()

    输出Mixin对象的结构...

    methodA

    methodB

    newInstance

    class sjq.cglib.mixin.MyInterfaceA$$MixinByCGLIB$$d1f6261a

    4 CGLIB用来对象之间拷贝属性

    package sjq.cglib.bean.copy;

    import net.sf.cglib.beans.BeanCopier;

    public class PropertyCopyDemo {

        public static void main(String[] args) {

            //两个对象

            Other other = new Other("test", "1234");

            Myth myth = new Myth();

            System.out.println(other);

            System.out.println(myth);

            //构建BeanCopier,并copy对象的属性值。

            BeanCopier copier = BeanCopier.create(Other.class, Myth.class, false);

            copier.copy(other, myth, null);

            System.out.println(other);

            System.out.println(myth);

        }

    }

    class Other {

        private String username;

        private String password;

        private int age;

        public String getUsername() {

            return username;

        }

        public void setUsername(String username) {

            this.username = username;

        }

        public String getPassword() {

            return password;

        }

        public void setPassword(String password) {

            this.password = password;

        }

        public Other(String username, String password) {

            super();

            this.username = username;

            this.password = password;

        }

        @Override

        public String toString() {

            return "Other: " + username + ", " + password + ", " + age;

        }

        public int getAge() {

            return age;

        }

        public void setAge(int age) {

            this.age = age;

        }

    }

    class Myth {

        private String username;

        private String password;

        private String remark;

        public String getUsername() {

            return username;

        }

        public void setUsername(String username) {

            this.username = username;

        }

        public String getPassword() {

            return password;

        }

        public void setPassword(String password) {

            this.password = password;

        }

        @Override

        public String toString() {

            return "Myth: " + username + ", " + password + ", " + remark;

        }

        public void setRemark(String remark) {

            this.remark = remark;

        }

        public String getRemark() {

            return remark;

        }

    }

    运行结果如下:

    Other: test, 1234, 0

    Myth: null, null, null

    Other: test, 1234, 0

    Myth: test, 1234, null

    5 使用CGLIB动态生成Bean

    import java.util.Iterator;

    import java.util.Map;

    import java.util.Set;

    import net.sf.cglib.beans.BeanGenerator;

    import net.sf.cglib.beans.BeanMap;

    /**

    *动态实体bean

    */

    public class CglibBean {

        /**

         * 实体Object

         */

        public Object object = null;

        

        /**

         * 属性map

         */

        public BeanMap beanMap = null;

        public CglibBean() {

            super();

        }

        @SuppressWarnings("unchecked")

        public CglibBean(Map<String, Class> propertyMap) {

            this.object = generateBean(propertyMap);

            this.beanMap = BeanMap.create(this.object);

        }

        /**

         * 给bean属性赋值

         * @param property属性名

         * @param value值

         */

        public void setValue(String property, Object value) {

            beanMap.put(property, value);

        }

        /**

         * 通过属性名得到属性值

         * @param property属性名

         */

        public Object getValue(String property) {

            return beanMap.get(property);

        }

        /**

         * 得到该实体bean对象。

         */

        public Object getObject() {

            return this.object;

        }

        /**

         * 生成Bean

         * @param propertyMap

         * @return

         */

        @SuppressWarnings("unchecked")

        private Object generateBean(Map<String, Class> propertyMap) {

            BeanGenerator generator = new BeanGenerator();

            Set keySet = propertyMap.keySet();

            for (Iterator i = keySet.iterator(); i.hasNext();) {

                String key = (String) i.next();

                generator.addProperty(key, (Class) propertyMap.get(key));

            }

            return generator.create();

        }

    }

    测试并使用动态Bean

    import java.lang.reflect.Method;

    import java.util.HashMap;

    /**

    * Cglib测试类

    */

    public class CglibTest {

        @SuppressWarnings("unchecked")

        public static void main(String[] args) throws ClassNotFoundException {

            // 设置类成员属性

            HashMap<String, Class> propertyMap = new HashMap<String, Class>();

            propertyMap.put("id", Class.forName("java.lang.Integer"));

            propertyMap.put("name", Class.forName("java.lang.String"));

            propertyMap.put("address", Class.forName("java.lang.String"));

            // 生成动态Bean

            CglibBean bean = new CglibBean(propertyMap);

            // 给Bean设置值

            bean.setValue("id", new Integer(123));

            bean.setValue("name", "454");

            bean.setValue("address", "789");

            // 从Bean中获取值,当然了获得值的类型是Object

            System.out.println(">>id=" + bean.getValue("id"));

            System.out.println(">>name=" + bean.getValue("name"));

            System.out.println(">>address=" + bean.getValue("address"));// 获得bean的实体

            Object object = bean.getObject();

            // 通过反射查看所有方法名

            Class clazz = object.getClass();

            Method[] methods = clazz.getDeclaredMethods();

            for (int i = 0; i < methods.length; i++) {

                System.out.println(methods[i].getName());

            }

        }

    }

    输出:

    >>id=123

    >>name=454

    >>address=789

    setId

    getAddress

    getName

    getId

    setName

    setAddress

    class net.sf.cglib.empty.Object$$BeanGeneratorByCGLIB$$1d39cfaa

    本篇学习文档参考于:

    http://jnb.ociweb.com/jnb/jnbNov2005.html

    http://wenku.baidu.com/view/3f92297c27284b73f24250b9.html

    http://www.cnblogs.com/icejoywoo/

    http://www.blogjava.net/calvin/archive/2005/11/28/21741.html

    http://m635674608.iteye.com/blog/1435221
    等其他高手博客提供的资料。

     

     

     

     

  • 相关阅读:
    R 画structure图
    perl ExtUtils::Manifest
    解决libstdc++.so.5问题
    R ggplot2 线性回归
    perl 计算方差中值平均数 Statistics::Descriptive;
    专题(十一)scp
    tcpdump的使用
    专题 (四) sed
    python 学习笔记(二)之 字符串操作
    redis 学习笔记之(二)主备高可用(热备)
  • 原文地址:https://www.cnblogs.com/shijiaqi1066/p/3429691.html
Copyright © 2020-2023  润新知