Java cglib动态代理的基本使用

之前写过一篇jdk代理的笔记:Java代理的基本用法_小楼夜听雨的博客-CSDN博客

今天补充cglib代理相关的一些笔记。

问:动态代理与静态代理最大的区别是什么?

答:代理类的创建时间不同。

静态代理在编码时期,需要硬编码实现对应的静态代理类,随后进入编译阶段生成对应的字节码,不够灵活。

动态代理是在运行的过程中,生成字节码文件,加载进内存,更加的灵活。

(感谢评论区网友指正:动态代理并不是将生成的字节码文件加载进内存,而是直接在内存中生成字节码文件,并且默认是不会保存到磁盘上的)

那么jdk代理与cglib代理有什么区别呢?我们可以带着这个疑问,来写一个简单的cglib demo。

导入依赖

    <dependency>
      <groupId>cglib</groupId>
      <artifactId>cglib-nodep</artifactId>
      <version>3.3.0</version>
    </dependency>

测试类


public interface Animal {
    String getName();
}

实现类

public class Dog implements Animal{
    @Override
    public String getName() {
        System.out.println("dog。。。。。。");
        return "dog";
    }
}

代理对象创建工具

public class CglibProxy {
    public static Object getProxy(Class<?> clazz) {
        //利用cglib自带的调试工具,将生成的代理类字节码文件输出到指定文件夹
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E:\\personcode\\proxy-learn\\cglibproxy");
        Enhancer enhancer = new Enhancer();
        //设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        //设置被代理类
        enhancer.setSuperclass(clazz);
        //设置方法拦截器
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("方法调用前");
                Object result = methodProxy.invokeSuper(obj, args);
                System.out.println("方法调用后");
                return result;
            }
        });

        return enhancer.create();
    }
}

测试

public class MainTest {
    public static void main(String[] args) {
        Animal animal = (Animal) CglibProxy.getProxy(Dog.class);
        animal.getName();
    }
}

输出

方法调用前
dog。。。。。。
方法调用后

分析

从代码中可以看出,Enhancer类是总线类,代理对象的创建过程都是围绕Enhancer进行的。

Enhancer是对外的窗口,提供调用其核心功能(主要是代理对象的创建和使用)的api,有兴趣的可以看下源码。我这里重点看看生成的代理对象。

生成的文件有三个,第一个就是代理对象

其命名也是有规则的

Dog$$EnhancerByCGLIB$$259018ca

Dog是原对象的类名,EnhancerByCGLIB固定,后面是随机字符串。

代理对象直接继承原对象,回想一下jdk代理对象是如何和原对象产生联系的(提示,实现接口)。

j

cglib选择通过继承的方式来增强原对象,而在java中,final修饰的类不能被继承,cglib也因此不能代理被final修饰的类。

jdk代理继承了Proxy类,在Java不能多继承的前提下,只能实现接口。从这一点上来看,cglib就业更广,毕竟很多时候,强行实现一个接口有点累赘。

但其实不管是cglib还是jdk代理,都是为了增强被代理类。“增强”,意味着原代理类的功能还是要用的。

还记得在jdk代理中,我们是通过反射来调用原对象的方法的。cglib代理中,则完全不一样。

FastClass是对原生class类的一个包装。

这里涉及一个index的概念,cglib把原对象的method全部分装在一个数组里,代理对象可以通过方法名、方法的参数来定位到原对象的某个方法,从而直接调用该方法。

在jdk代理中,代理对象则通过反射来完成对原对象的方法调用,亦是一大区别。

性能对比

jdk动态代理的代理对象创建速度比较快,cglib比较慢,因为在创建代理对象的过程中需要操作字节码(ASM)。

但是cglib不强制要求对象实现接口,比较通用点,使用起来也较为便捷。

Spring中默认使用jdk代理,在遇到没有实现接口的时候,会使用cglib代理。

我在网上看到过一个争议,Spring中默认使用的究竟是jdk代理还是cglib代理?

最后看到一个网友做了测试,Spring确实默认用jdk代理,没办法的时候才选择cglib,但是SpringBoot不太一样,默认是开启了cglib代理。

地址:https://zhuanlan.zhihu.com/p/89057894

cglib api

cglib有很多使用的功能,具体的api可以参考下面的博客:

CGLIB(Code Generation Library)详解_小楼一夜听春雨-CSDN博客

如有错误,欢迎批评指正!

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值