CGLib动态代理(实现原理全面详解)

CGLib动态代理(实现原理全面详解)-mikechen

CGLib简介

Cglib 是一个强大的高性能的代码生成包,它的底层就是asm(字节码框架),可以在运行期扩展 java 类,广泛的被许多 AOP 的框架使用,例如 Spring AOP,实现方法拦截。

 

Cglib优缺点

1.优点

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。

另一个优点是Cglib动态代理比使用java反射的JDK动态代理要快(Cglib的FastClass机制)。

2.缺点

对于被代理类中的final方法,无法进行代理,因为子类中无法重写final函数。
CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。

同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。

 

CGLib组成结构

CGLib包的底层是通过使用一个小而快的字节码处理框架ASM(Java字节码操控框架),来转换字节码并生成新的类,下图为CGLib与一些框架和语言的关系:

CGLib动态代理(实现原理全面详解)-mikechen

除了CGLib包,脚本语言例如 Groovy和BeanShell,也是使用ASM来生成java的字节码。

不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉,所以CGLib包要依赖于asm包,需要一起导入。

 

CGLib的应用场景

  1. CGLib被广泛使用在基于代理的AOP框架(例如Spring AOP和dynaop)提供方法拦截;
  2. hibernate使用CGLib对持久化对象创建代理;
  3. EasyMock和jMock作为流行的Java测试库,它们提供Mock对象的方式来支持测试,都使用了CGLib来对没有接口的类进行代理。

注意:Spring AOP和Hibernate同时使用JDK的动态代理和CGLib包,Spring AOP,如果不强制使用CGLib包,默认情况是使用JDK的动态代理来代理接口。

 

CGLib实现动态代理

CGLib实现动态代理的特点是针对类进行代理,具体来说是,cglib会对要被代理的类生成一个继承子类,在子类中对方法进行增强。

注意:关键点就在继承,所以无法对final类使用。

1.创建一个实现类

package com.demo.rpc.proxy;

public class HelloService {
    
    public void sayHello(){
        System.out.println("hello");
    }
}

2.新建一个代理类

package com.demo.rpc.proxy;

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 CGLIBProxy implements MethodInterceptor {


    Object targetObject;

    public Object newProxy(Object targetObject){
        this.targetObject = targetObject;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetObject.getClass());
        enhancer.setCallback(this);
        Object proxyObj = enhancer.create();
        return proxyObj;
    }


    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理之前");
        Object invoke = method.invoke(targetObject, args);
        System.out.println("代理之后");
        return invoke;
    }
}

3.代用测试

@Test
public void CGLIBProxy(){
    CGLIBProxy proxy = new CGLIBProxy();
    HelloService helloService = (HelloService)proxy.newProxy(new HelloService());
    helloService.sayHello();
}

4.运行结果

代理之前
hello
代理之后

 

CGLib总结

总结来说CGLib是一个强大的、高性能的Code生产类库,在Spring中就是通过CGLib方式继承要被代理的类,重写父类的方法,实现Aop编程。

CGLib创建动态代理对象的性能时机要比jdk动态代理的方式高很多,但是创建对象所花的时间却要比jdk动态代理方式多很多。

在应用方面单例模式更适合用CGLib,无需频繁的创建对象,相反,则使用jdk动态代理的方式更加合适。

作者简介

陈睿|mikechen,10年+大厂架构经验,BAT资深面试官,就职于阿里巴巴、淘宝、百度等一线互联网大厂。

👇阅读更多mikechen架构文章👇

阿里架构 |双11秒杀 |分布式架构 |负载均衡 |单点登录 |微服务 |云原生 |高并发 |架构师

以上

关注作者「mikechen」公众号,获取更多技术干货!

后台回复架构,即可获取《阿里架构师进阶专题全部合集》,后台回复面试即可获取《史上最全阿里Java面试题总结

评论交流
    说说你的看法