Java设计模式-----Cglib动态代理(Cglib Proxy)

接上文:4.2Java设计模式-----JDK动态代理(Dynamic Proxy)

Cglib动态代理

        百度百科:Cglib是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。

        Cglib动态代理,是JDK静态代理,JDK动态代理之后的另一种代理方式。通过名称我们可以发现,JDK静态代理和JDK动态代理都是由Java官方提供的代理方式;而Cglib动态代理则是第三方的开源项目。在使用时,我们需要引入第三方jar包来实现

为什么会有Cglib动态代理

        虽然JDK动态代理解决了JDK静态代理的一些缺点,但是JDK动态代理也还是存在一些缺点的。诸如:JDK动态代理必须要求代理类实现某个接口类,照这种情况,那么我们普通的类就无法使用动态代理了。按照JDK动态代理实现方法来看,是这么一回事,这明显就是JDK动态代理的缺点,所以Cglib动态代理也就应运而生了

JDK动态代理为什么必须要求代理类实现接口?

         因为JDK的动态代理,是通过接口来进行强制转换的。通过代理生成的代理对象,可以强制转换为接口类型,就可以直接调用该类的相关方法来完成一系列的操作

Cglib动态代理的优点

        ①相比JDK动态代理,我们可以省去声明接口类的这个步骤,可以解除JDK动态代理的限制

        ②性能方面:JDK低版本的情况下,Cglib动态代理性能明显高于JDK动态代理,有说快10倍以上,可能出现在更低的JDK版本上吧。随着JDK版本的升级,JDK7以后,JDK动态代理也做出了很大的优化,性能好像已经超出了Cglib动态代理。有网友测试:在运行次数较少(1,000,000)的情况下,jdk动态代理比 cglib 快了差不多30%;而当调用次数增加之后(50,000,000), 动态代理比 cglib 快了接近1倍。(数据来自网络,我没有做测试)

Cglib动态代理的原理

        Cglib的动态代理,就是子类继承父类。即:通过生成一个被代理对象的子类,然后重写父类的方法生成以后的对象,所以目标类和方法不能声明为final类型。可以强制转化为被代理对象(也就是自己写的类) 。即:将子类的引用赋值给父类

Cglib动态代理代码实现

①因为是三方开源项目,我们需要先引入相应Jar包,此处通过Maven导入依赖

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.12</version>
</dependency>

②先来一个被代理类对象,此处用到的Student被代理类同前文中JDK动态代理保持不变(Cglib动态代理,Person接口不需要了)

/**
 * 被代理类对象
 */
public class Student {
    public void findHouse(){
        System.out.println("被代理人需求:我是学生A,我现在想找一个10平米的小房间,用来学习");
    }
}

③来声明一个代理类对象CglibProxy

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

//使用Cglib,需要实现MethodInterceptor接口
public class CglibProxy implements MethodInterceptor {
    
    //getInstance方法,同JDK动态代理、静态代理一样,都是为了获取到被代理对象的引用
    public Object getInstance(Class clazz){
        //Enhancer是一个非常重要的类,它允许为非接口类型创建一个JAVA代理,Enhancer动态的创建给定类的子类并且拦截代理类的所有的方法,和JDK动态代理不一样的是不管是接口还是类它都能正常工作。
        Enhancer enhancer = new Enhancer();
        //把父类设置为谁?这一步就是告诉Cglib,生成的子类要继承哪个类
        enhancer.setSuperclass(clazz);
        //设置回调,使生成的代理类对象在每调用被代理类方法时,都去调用下面的intercept方法
        enhancer.setCallback(this);
        //enhancer.create()的工作:1.生成源代码   2.编译成class文件   3.加载到JVM中,生成并返回被代理对象......等等
        return enhancer.create();
    }

    @Override
    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("我是中介,我代理的用户需要租一间房屋,如下是用户的一些需求");
        System.out.println("-----------------------------------------------------");
        //这里有个疑问:代理是需要获取到被代理对象的引用,但是Cglib好像并没有获取被代理对象的引用。那怎么还能用代理呢?
        //其实这里这个object的引用,是由Cglib帮我们new出来的
        //Cglib new出来的对象,是被代理对象的子类(继承了我们自己写的那个被代理类)
        //面向对象编程(OOP)中,在new 子类之前,实际上是默认调用了super()方法的
        //也就是说,new了子类的同时,也必须先new出来父类。这就相当于是间接的持有了我们父类的引用
        //实际上就是:子类重写了父类的所有方法
        //我们改变子类对象的某些属性,是可以间接的操作父类的属性的
        //是这样子的,代理便可以通过new子类的形式,间接获取到我们被代理对象的引用了
        methodProxy.invokeSuper(object,args);
        System.out.println("-----------------------------------------------------");
        System.out.println("我是中介,如上就是我代理用户的需求");
        return null;
    }
}

 对代码进行的一些简单解释,请看代码中备注

④来一个测试类

/**
 * 测试类
 */
public class test {
    public static void main(String[] args) {
        //生成的代理类的class文件到指定路径
        //System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "指定要保存class文件的路径");
        Student student = (Student)new CglibProxy().getInstance(Student.class);
        System.out.println(student.getClass());
        student.findHouse();
    }
}

⑤代码运行结果

我是中介,我代理的用户需要租一间房屋,如下是用户的一些需求
-----------------------------------------------------
被代理人需求:我是学生A,我现在想找一个10平米的小房间,用来学习
-----------------------------------------------------
我是中介,如上就是我代理用户的需求

如何查看生成的代理类的class文件

//在测试类中,添加如下代码,则会将class文件生成到指定路径
//如若需要查看class文件,可以通过反编译工具来查看
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "指定要保存class文件的路径");

Cglib动态代理与JDK动态代理的区别

        ①JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象(方式不同:一个是实现,一个是继承)

        ②JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。

        ③JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。

至此,JDK静态代理,JDK动态代理,Cglib动态代理到此就告一段落了。

附链接:4. Java设计模式-----代理模式(Proxy Pattern)

              4.1 Java设计模式-----JDK静态代理(Static Proxy)

              4.2 Java设计模式-----JDK动态代理(Dynamic Proxy)

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

扛麻袋的少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值