美文网首页Java核心Design Pattern
CGLib - 另一种动态代理的实现方式

CGLib - 另一种动态代理的实现方式

作者: DjangoW | 来源:发表于2018-08-27 15:22 被阅读2次

    代理模式

    代理模式:是设计模式(Design Pattern)的一种,旨在通过代理为某些操作添加额外的处理,比如涉及用户Payment账户变动操作时检查用户授权。代理模式的实现又分为静态代理和动态代理。

    静态代理&Java反射实现的动态代理在之前的文章已经讲过,这里不在赘述。而且之前提到过Java反射机制实现的动态代理不足之处是其被代理对象需要实现统一的接口。如果想为没有实现接口的类做代理呢?这就引入了这篇文章的重点:CGLib实现的动态代理(通过继承被代理类来实现代理)。

    CGLib

    CGLib实现的动态代理除了可以不通过接口实现动态代理的优点之外,还有处理速度快、效率高的优点!因为生成代码比Java反射的速度要快很多。

    CGLib到底是什么?其实CGLib是依靠asm字节码处理框架实现的一个high level & 高性能 & 高质量的Code生成类库,可以在运行时扩展Java类或者实现接口。那可不可以直接通过asm框架来实现CGLib可以实现的功能呢?当然可以,不过要想熟练运用asm框架必须对Java类文件的格式和指令集比较熟悉。CGLib对用户隐藏了asm复杂的内部实现,提供了Developer友好、面向特定功能的实现,比如方法拦截(Interpreter)、懒加载(Lazyloader & Dispatcher)等,因此用户不需要有Java底层类文件格式的知识就可以通过CGLib实现动态代理。

    当今许多成熟的开源框架都应用了CGLib,比如AOP中的方法拦截,Hibernate中的延迟加载。由此CGLib的应用之广泛,名气之大可见一斑。其中Spring AOP优先选用JDK反射实现动态代理,只有在被代理对象没有实现接口时才会采用CGLib。究其原因,通过接口实现代理的代码耦合度更低,而Spring是在松耦合的思想上实现的,所以通过反射实现动态代理成了AOP的默认首选。

    示例

    语言多苍白,咱们来上Code吧!

    public class Sample {
        public String test(String input) {
            System.out.println("hello world");
            return "hello world";
        }
        public String test1(String input) {
            System.out.println("hello world-1");
            return "hello world-1 ";
        }
    }
    

    1. No Operation

    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(Sample.class);
    enhancer.setCallback(NoOp.INSTANCE);
    Sample proxy0 = (Sample)enhancer.create();
    assertEquals("hello world", proxy0.test(null)); // Success!
    

    2. 固定值 - FixedValue

    Enhancer enhancer = new Enhancer();
    //fixedValue
    enhancer.setSuperclass(Sample.class);
    enhancer.setCallback(new FixedValue() {     
        @Override
        public Object loadObject() throws Exception {
            return "hello cglib!";
        }
    });
    Sample proxy = (Sample)enhancer.create();
    assertEquals("hello cglib!", proxy.test(null)); // Success!
    

    3. 方法拦截 - Interpreter

    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(Sample.class);
    enhancer.setCallback(new MethodInterceptor() {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("before");
            Object result = proxy.invokeSuper(obj, args);
            System.out.println("after");
            return result;
        }
    });
    Sample proxy = (Sample)enhancer.create();
    assertEquals("hello world", proxy.test(null)); // Success!
    

    OUTPUT:

    before
    hello world
    after
    

    4. 懒加载

    class Bean{
        private int id;
        private PriorityBean pBean;
        public Bean(int id){
            this.id = id;
            this.pBean = lazyLoadPBean();
        }
        private PriorityBean lazyLoadPBean() {
            return (PriorityBean)Enhancer.create(PriorityBean.class, new LazyLoader(){
                @Override
                public Object loadObject() throws Exception {
                    System.out.println("lazyloader start loading!");
                    PriorityBean result = new PriorityBean();
                    result.setValue(111);
                    return result;
                }
            });
    //      return (PriorityBean)Enhancer.create(PriorityBean.class, new Dispatcher() {
    //          @Override
    //          public Object loadObject() throws Exception {
    //              System.out.println("lazyloader start loading!");
    //              PriorityBean result = new PriorityBean();
    //              result.setValue(111);
    //              return result;
    //          }
    //      });
        }
        public int getId() {
            System.out.println("get id");
            return this.id;
        }
        public PriorityBean getPBean() {
            System.out.println("get pBean");
            return this.pBean;
        }
    }
    
    class PriorityBean{
        private int value;
        public int getValue() {
            System.out.println("get value");
            return this.value;
        }
    }
    
    4.1 懒加载 - Lazyloader
    Bean b = new Bean(123);
    assertEquals(b.getId(), 123); // Success!
    b.setId(222);
    assertEquals(b.getId(), 222); // Success!
    assertEquals(b.getPBean().getValue(), 111); // Success!
    assertEquals(b.getPBean().getValue(), 111); // Success!
    

    OUTPUT:

    get id
    get id
    get pBean
    lazyloader start loading!
    get value
    get pBean
    get value
    
    4.2 懒加载 - Dispatcher
    Bean b = new Bean(123);
    assertEquals(b.getId(), 123); // Success!
    b.setId(222);
    assertEquals(b.getId(), 222); // Success!
    assertEquals(b.getPBean().getValue(), 111); // Success!
    assertEquals(b.getPBean().getValue(), 111); // Success!
    

    OUTPUT:

    get id
    get id
    get pBean
    lazyloader start loading!
    get value
    get pBean
    lazyloader start loading!
    get value
    

    由Lazyloader与Dispatcher的OUTPUT不同可以看出,前者只懒加载一次,后者每次访问该元素(pBean)都会执行一次懒加载!

    5. 回调方法的过滤 - Filter

    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(Sample.class);
    Callback[] callbacks = new Callback[] {NoOp.INSTANCE, new FixedValue() {
        @Override
        public Object loadObject() throws Exception {
            return "hello cglib!";
        }} };
    enhancer.setCallbacks(callbacks);
    enhancer.setCallbackFilter(new CallbackFilter() {
        @Override
        public int accept(Method method) {
            if(method.getName().equals("test")) return 1;
                return 0;
            }
        });
    Sample proxy = (Sample)enhancer.create();
    assertEquals("hello cglib!", proxy.test(null)); // Success!
    assertEquals("hello world", proxy.test1(null)); // Success!
    

    可以看到setCallbacks传入的是一个Callback数组,在setCallbackFilter设置的CallbackFilter中通过判断method的名字来决定使用Callback数组里的哪一个(通过在CallbackFilter的accept函数中返回Callback数组中某Callback对应的index值)。

    Reference

    https://blog.csdn.net/zghwaicsdn/article/details/50957474
    https://github.com/cglib/cglib
    https://www.cnblogs.com/CarpenterLee/p/8241042.html

    相关文章

      网友评论

        本文标题:CGLib - 另一种动态代理的实现方式

        本文链接:https://www.haomeiwen.com/subject/ravliftx.html