从动态代理到CGLIB的学习记录

       之前看jvm的时候看到CGLIB这个库,就想看看究竟是个什么玩意,结果一研究就是好几天,看的头大,记录一下学习路线,以后忘了好复习。

       个人的理解,动态代理相比于静态代理(资料很多这里就不赘述了),优势在于动态代理不需要重新实现接口的所有函数,代码更为简练,以下是一个例子(主要 参考了文章:Java 动态代理作用是什么? - bravo1988的回答 - 知乎),注释里面有注意的点:

public interface Calculator {
    public Integer add(Integer a , Integer b);
}


public class CalculatorImpl implements Calculator {
    @Override
    public Integer add(Integer a, Integer b) {
        return a+b;
    }
}
    

public static void main(String[] args) throws Exception {
        Class proxyClazz = Proxy.getProxyClass(Demo.class.getClassLoader(),Calculator.class);
        Calculator proxyCalculator =(Calculator) proxyClazz.getConstructor(InvocationHandler.class).newInstance(new InvocationHandler(){
//InvocationHandler 这个接口个人理解在于,代理类一切方法被调用时。
//默认就会调用这个接口里面的唯一方法invoke()
//从而实现封装
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//此处的proxy 就是调用方法的代理实例
                System.out.println("鸡汤来咯");  //前部分封装的代码
//此处注意,要new一个或者使用一个非代理的原对象,而不是直接使用proxy。
//原因在于使用了proxy这个实例,就会导致再次进入Invocationhandler这个方法里面,无限递归
//最后栈溢出
                Object result= method.invoke(new CalculatorImpl(),args);

                System.out.println(result);
                System.out.println("为什么不喝!!!");//后面封装的代码
                return result;
            }
        });
        System.out.println(proxyCalculator.add(3,5));


    }

这一部分的代码可以用newInstance方法进一步精简,具体的上述文章里有,就不赘述了。

JDK动态代理的实质是最终生成一个继承于proxy实现了被代理类所有接口的一个类,由于java单继承的限制,因此动态代理对于没有接口的类是无法代理的,更具体的可参考文章:关于代理:为什么 JDK 动态代理只能为接口生成代理? - SegmentFault 思否

 而CGLIB则没有了这个问题,CGLIB的实现原理,主要是参考了这篇文章:Cglib动态代理实现原理 - 两条闲鱼 - 博客园 (cnblogs.com),CGLIB在实现上直接继承了被代理类同时implement了Factory接口,因此可以对类进行代理。(特殊情况,当CGLIB传入接口的Class时候,CGLIB将不继承任何class而是直接空实现该类,参考:JAVA面试宝典—为什么CGlib方式可以对接口实现代理_哔哩哔哩_bilibili

此外,CGLIB和JDK动态代理各有其优势,一般情况下,CGLIB构造出来的代理对象性能更高,但是JDK动态代理的构造代理对象的时间要短的多,因此,如果是单例的情况下,CGLIB更好,但要频繁new的情况下,JDK的动态代理更好。

CGLIB的一些基本操作代码:

MethodInterceptor版本:

   @Test
    public void testCGLibOne(){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(CalculatorImpl.class);//设置要代理的类(父类)
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                //obj 与InvokeHandler相同 这里传的是代理后的类
                //method 被拦截的方法
                //args 参数
                //method的MethodProxy版本,该类有invokeSuper方法用于调用被代理前对象的方法
                System.out.println();
                System.out.println("front deal");
                //这的意思是调用父类 也就是原被代理对象的方法
                //直接调用invoke会和InvokeHandler一样陷入死循环。
                Object rs = proxy.invokeSuper(obj,args);
                System.out.println("back deal");
                return rs;
            }
        });
        Calculator calculator = (Calculator)enhancer.create();
        System.out.println(calculator.add(3,5));
    }

 CallBackFilter

    @Test
    public void testFilter() {
        Enhancer enhancer = new Enhancer();
        CallbackFilter filter = new CallbackFilter() {
            //这里是设置返回一个int数,会变成下面CallBack数组中的索引
            @Override
            public int accept(Method method) {
                if(method.getReturnType()==Integer.class)
                    return 0;
                if(method.getReturnType()==String.class)
                    return  1;
                return 2;
            }
        };
        enhancer.setCallbackFilter(filter);
        Callback callbackInt = new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                System.out.println("will return a int");
                Object rs = proxy.invokeSuper(obj,args);
                System.out.println("return int end");
                return rs;
            }
        };
        Callback callbackString = new FixedValue() {
            //不执行原方法,返回常量
            @Override
            public Object loadObject() throws Exception {
                return "输出你个头String啊!";
            }
        };
        //对应上方CallbackFilter的返回值 0-> Integer,1->String,2->什么都不干
        enhancer.setCallbacks(new Callback[]{callbackInt,callbackString,new NoOp(){}});

         enhancer.setSuperclass(CalculatorImpl.class);
        Calculator calculator = (Calculator) enhancer.create();
        System.out.println(calculator.add(1,3));
        System.out.println(calculator.toString());
        System.out.println(calculator.getClass());
    }

最后输出

will return a int
return int end
4
输出你个头String啊!
class tech.target.CalculatorImpl$$EnhancerByCGLIB$$c8aceca5

其它一些操作暂时就先不看了,不少于Bean的操作有关,需要的时候再继续研究

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值