Cglib

    最近在公司自研框架中,用到了Cglib技术,而看源码如果不对这些技术有较为深入的理解话,看起来会非常吃力,所以想对其,做一下总结。

1.什么是Cglib?

CGLIB是一个功能强大,高性能的代码生成包。它相比于jdk的动态代理,它可以对没有实现接口的类进行代理。所以当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。 

2.Cglib代理原理?

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

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

 

注意:Cglib对于final方法无法进行代理,而jdk动态代理是可以的。 

3.第一步先从一个demo开始

首先引入Cglib依赖


       <dependency>
           <groupId>cglib</groupId>
           <artifactId>cglib</artifactId>
           <version>2.2.2</version>
       </dependency>
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class TestCglib {

    public String a(String a, String b) {
        System.out.println("a方法被执行了");
        return a + "-" + b;
    }

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(TestCglib.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            //参数:
            // Object为由CGLib动态生成的代理类实例,
            // Method为上文中实体类所调用的被代理的方法引用,
            // Object[]为参数值列表,
            // MethodProxy为生成的代理类对方法的代理引用。
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("代理前");
                for (int i = 0; i < objects.length; i++) {
                    objects[i] = objects[i] + "&";
                }
                Object result = methodProxy.invokeSuper(o, objects);
                System.out.println("代理后");
                return result;
            }
        });
        TestCglib test = (TestCglib) enhancer.create();
        System.out.println(test.a("a", "b"));
    }
}

在这个示例中,只在调用被代理类方法前后各打印了一句话,并且对传入的参数,拼接一个字符,如果拼接参数类型不正确,会报ClassCastException,当然实际编程中可以是其它复杂逻辑

其中Enhancer 这个类是比较重要的。

4.Enhancer是什么?

Enhancer是cglib中使用频率很高的一个类,它是一个字节码增强器,可以用来为无接口的类创建代理。它的功能与java自带的Proxy类挺相似的。它会根据某个给定的类创建子类,并且所有非final的方法都带有回调钩子

 5.那么Enhancer具体的回调钩子有哪些呢(CallBack)?

  1.  Callback-NoOp 这个回调相当简单,就是啥都不干的意思。

  2. Callback-LazyLoader LazyLoader是cglib用于实现懒加载的callback。当被增强bean的方法初次被调用时,会触发回调,之后每次再进行方法调用都是对LazyLoader第一次返回的bean调用。

      public static void test4(){
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(TestCglib.class);
    
            enhancer.setCallback(new LazyLoader() {
                @Override
                public Object loadObject() throws Exception {
                    System.out.println("打印");
                    return new TestCglib();
                }
            });
            TestCglib test = (TestCglib) enhancer.create();
            System.out.println(test.a("a", "b"));
            //当在调用这个实例方法的时候,会机智的返回上一次bean的调用,意思只会进入Callback一次
            System.out.println(test.a("a", "b"));
        }

  3.  Callback-Dispatcher Dispatcher和LazyLoader作用很相似,区别是用Dispatcher的话每次对增强bean进行方法调用都会触发回调。

      public static void test4(){
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(TestCglib.class);
    
            enhancer.setCallback(new Dispatcher() {
                @Override
                public Object loadObject() throws Exception {
                    System.out.println("打印");
                    return new TestCglib();
                }
            });
            TestCglib test = (TestCglib) enhancer.create();
            System.out.println(test.a("a", "b"));
            System.out.println(test.a("a", "b"));
        }

  4. Callback-InvocationHandler cglib的InvocationHandler和JDK自带的InvocationHandler作用完全相同。使用的时候要注意,如果对参数中的method再次调用,会重复进入InvocationHandler。

  5.  Callback-FixedValue FixedValue一般用于替换方法的返回值为回调方法的返回值,但必须保证返回类型是兼容的,否则会出转换异常。

  6. Callback-MethodInterceptor MethodInterceptor 方法拦截器。与InvocationHandler作用基本相同,但是实现机制完全不一样,因为如果用method调用会再次进入拦截器。为了避免这种情况,应该使用接口方法中第四个参数methodProxy调用invokeSuper方法。

也就是对应这几个接口,上面的demo也就是用的第6个CallBack

关于invoke和invokeSurper的区别,为什么调用invoke会发生StackOverflowError,可以看看这篇文章

6.CallbackFilter

CallbackFilter可以实现不同的方法使用不同的回调方法,也可以不进行回调也就是使用上文的NoOp这个接口。

CallbackFilter中的accept方法, 根据不同的method返回不同的值i, 这个值是在callbacks中的顺序, 就是调用了callbacks[i],下面请看示例

public class TestCglib {

    public String a(String a, String b) {
        System.out.println("a方法被执行了");
        return a + "-" + b;
    }
    public String c(String a, String b) {
        System.out.println("b方法被执行了");
        return a + "-" + b;
    }



    private static class MethodInterceptorImpl implements MethodInterceptor{

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("代理前");
            Object result = methodProxy.invokeSuper(o, objects);
            System.out.println("代理后"+this.getClass());
            return result;
        }
    }
    public static void test1(){
        // callbacks里面的顺序,对应accpet()方法里面的返回值,如NoOp.INSTANCE的顺序对应 1
        Callback[] callbacks = new Callback[] {
                new MethodInterceptorImpl(), NoOp.INSTANCE
        };
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(TestCglib.class);
        enhancer.setCallbacks(callbacks);
        enhancer.setCallbackFilter(new CallbackFilterImpl());
        //enhancer.setCallback(methodInterceptor);
        TestCglib test = (TestCglib) enhancer.create();

        System.out.println(test.a("a", "b"));
        System.out.println(test.c("a", "b"));
    }

    public static void main(String[] args) {
        test1();
    }
}
class CallbackFilterImpl implements CallbackFilter {

    @Override
    public int accept(Method method) {
        System.out.println(method.getName());
        if (method.getName().equals("a"))
            //因为 return 1对应Callback[]中的NoOp.INSTANCE所以不会对其进行任何回调处理,a方 
            //法就被拦截下来了
            // Callback[] callbacks = new Callback[] {
            //                methodInterceptor, NoOp.INSTANCE
            //        };
            return 1;
        else
            return 0;
    }

}

可以看到a方法被拦截下来没有执行代理逻辑

 

以上就是关于Cglib的总结,如果有不足的地方欢迎评论留言

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值