CGLib(Code Generation Library)是一个强大的、高性能、高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。
CGLib比Java的java.lang.reflect.Proxy 类更强大的地方在于它不仅可以接管接口类的方法,还可以接管普通类的方法,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口时或者为了更好的性能,当然CGLIB是一个更好的选择,在Spring、Hibernate框架中都使用到了CGLIB动态代理技术。
CGLib的底层是Java字节码操作框架——ASM https://asm.ow2.io
CGLIB 类库结构
- net.sf.cglib.core:底层字节码处理类,他们大部分与ASM有关系。
- net.sf.cglib.transform:编译期或运行期类和类文件的转换
- net.sfcglib.proxy: 实现创建代理和方法拦截器的类
- net.sf.cglib.reflect:实现快速反射类
- net.sf.cglib.util:集合排序等工具类
- net.sf.cglib.beans:javabean 相关的工具类
CGLIB实现动态代理
静态代理:
public interface TargetInterface {
public String sayHello(String name);
public String sayThanks(String name);
}
public class TargetInterfaceImpl implements TargetInterface{
@Override
public String sayHello(String name) {
return "Hello" + name;
}
@Override
public String sayThanks(String name) {
return "Thanks" + name;
}
}
//代理类
public class TargetProxy implements TargetInterface{
@Autowired
private TargetInterface targetInterface;
@Override
public String sayHello(String name) {
System.out.println("Hello 服务限流");
return targetInterface.sayHello(name);
}
@Override
public String sayThanks(String name) {
System.out.println("Thanks 服务限流");
return targetInterface.sayThanks(name);
}
}
动态代理(CGLib):
public class TargetProxy implements MethodInterceptor {
//获取真正的代理类
public <T> T getProxy(Class clazz) {
//字节码增强
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(clazz);
//设置回调,会执行下面的intercept方法
enhancer.setCallback(this);
//创建代理类
return (T) enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("服务限流");
return methodProxy.invokeSuper(o, objects);
}
}
public class test {
public static void main(String[] args) {
TargetProxy targetProxy = new TargetProxy();
//拿到目标类的代理类
TargetInterfaceImpl proxy = targetProxy.getProxy(TargetInterfaceImpl.class);
System.out.println(proxy.sayHello("111"));
}
}
若,对接口使用代理则会产生异常
注意:生成的代理类时目标类的 子类
public class test {
public static void main(String[] args) {
TargetProxy targetProxy = new TargetProxy();
//拿到目标类的代理类
TargetInterface proxy = targetProxy.getProxy(TargetInterface.class);
System.out.println(proxy.sayHello("111"));
}
}
因为目标类没有实现,所以若需要对接口进行代理,需要对接口进行实现
CGlib缺点:
使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGkib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,如果委托类被final修饰,那么它不可被继承,即不可被代理;同样,如果委托类中存在final修饰的方法,那么该方法也不可被代理;
CGLib 代理原理是什么?(面试)
CGuB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术栏截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。
CGuB缺点:对于final方法,无法进行代理。
JDK动态代理与CGLib代理的区别?
JDK动态态代理
public interface TargetInterface {
public String sayHello(String name);
public String sayThanks(String name);
}
public class TargetInterfaceImpl implements TargetInterface {
@Override
public String sayHello(String name) {
return "Hello" + name;
}
@Override
public String sayThanks(String name) {
return "Thanks" + name;
}
}
public class TargetProxy implements InvocationHandler {
//目标接口类的引用
private Object target;
public TargetProxy(Object target) {
this.target = target;
}
public <T> T getProxy(Class clazz) {
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz}, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName() + "数据缓存");
return method.invoke(target, args);
}
}
public class Test {
public static void main(String[] args) {
TargetProxy targetProxy = new TargetProxy(new TargetInterfaceImpl());
TargetInterface targetInterface = targetProxy.getProxy(TargetInterface.class);
System.out.println(targetInterface.sayHello("111"));
}
}
原理区别:java动态代理是利用反射机制生成一个实现代理接口的匿名类(看不见摸不着,在JVM内存中),在调用具体方法前调用InvokeHandler 来处理。核心是实现InvocationHandler接口,使用invoke0方法进行面向切面的处理,调用相应的通知。
而cglib动态代理是利用asm 开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类(继承自目标类)来处理。核心是实现Methodlnterceptor接口,使用intercept0方法进行面向切面的处理,调用相应的通知。
在Spring中:
- 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
- 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
- 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
性能区别:
- CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类(声明final后子类就不能继承自父类)。
- 在jdk6、jdk7、jdk逐步对UDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候jdk代理效率高于CGLIB代理。
各自局限:
- JDK的动态代理机制只能代理实现了接口的类了而不能实现接口的类就不能实现JDK的动态代理。
- cglib他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
JDK Proxy的优势:
最小化依赖关系,减少依赖意味着简化开发和维护,JDK本身的支持,可能比cglib更加可靠。
平滑进行JDK版本升级,而字节码类库通常需要进行更新以保证在新版Java上能够使用。代码实现简单。
cglib的优势:
从某种角度看,jdk动态代理限定调用者实现接口是有些侵入性的实践,类似cglib动态代理就没有这种限制。只操作我们关心的类,而不必为其他相关类增加工作量。另外高性能。