当前位置: 首页 > >

JDK Proxy 和 CGLib 有什么区别?

发布时间:

JDK Proxy 和 CGLib 有什么区别?



文章目录
JDK Proxy 和 CGLib 有什么区别?前言项目环境1.主要区别2.JDK Proxy 动态代理3.CGLib 的实现4.Lombok5.总结6.参考



前言

JDK 动态代理的实现方式是反射。



反射机制 是指程序在运行期间可以访问、检测和修改其本身状态或行为的一种能力,使用反射我们可以调用任意一个类对象,以及类对象中包含的属性及方法。



CGLib 实现动态代理是基于 ASM(一个 Java 字节码操作框架)而非反射实现的。


动态代理是一种行为方式,反射或 ASM 只是它的一种实现手段而已。


项目环境
Java 8Spring framework 5.2.2.RELEASEgithub 地址:https://github.com/huajiexiewenfeng/spring-aop-demos
1.主要区别

JDK Proxy 和 CGLib 的区别主要体现在以下几个方面:


JDK Proxy 是 Java 语言自带的功能,无需通过加载第三方类实现;Java 对 JDK Proxy 提供了稳定的支持,并且会持续的升级和更新 JDK Proxy;JDK Proxy 是通过拦*骷臃瓷涞姆绞绞迪值模籎DK Proxy 只能代理继承接口的类;JDK Proxy 实现和调用起来比较简单;CGLib 是第三方提供的工具,基于 ASM 实现的,性能比较高;CGLib 无需通过接口来实现,它是通过实现子类的方式来完成调用的。
2.JDK Proxy 动态代理

JDK Proxy 动态代理的实现无需引用第三方类,只需要实现 InvocationHandler 接口,重写 invoke() 方法即可,示例代码如下:


public class DynamicProxyDemo {

public static void main(String[] args) {
Subject subject = new RealSubject();
Subject proxyInstance = (Subject) java.lang.reflect.Proxy.newProxyInstance(
subject.getClass().getClassLoader(),
new Class[]{Subject.class},
new InvocationHandlerImpl(subject));
proxyInstance.request();
}

static class InvocationHandlerImpl implements InvocationHandler {

private Object target;

public InvocationHandlerImpl(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object reuslt = null;
postOperation();
reuslt = method.invoke(target, args);
preOperation();
return reuslt;
}

private void postOperation() {
System.out.println("后置处理...");
}

private void preOperation() {
System.out.println("前置处理...");
}
}

}

执行结果:


后置处理...
request 方法执行
前置处理...

可以看出 JDK Proxy 实现动态代理的核心是实现 InvocationHandler 接口,我们查看 InvocationHandler 的源码,会发现里面其实只有一个 invoke() 方法,源码如下:


public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}

这是因为在动态代理中有一个重要的角色也就是代理器,它用于统一管理被代理的对象,显然 InvocationHandler 就是这个代理器,而 invoke() 方法则是触发代理的执行方法,我们通过实现 InvocationHandler 接口来拥有动态代理的能力。


3.CGLib 的实现

示例代码:


public class CGLibDemo {

static class Car {
public void run() {
System.out.println("the car is running");
}
}

static class CglibProxy implements MethodInterceptor {

private T target;// 代理对象

public T getInstance(T target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return (T) (enhancer.create());
}

@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
preOperation();
Object retVal = methodProxy.invoke(this.target, args);
postOperation();
return retVal;
}

private void postOperation() {
System.out.println("后置处理...");
}

private void preOperation() {
System.out.println("前置处理...");
}

}

public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy<>();
Car car = cglibProxy.getInstance(new Car());
car.run();
}

}

执行结果:


前置处理...
the car is running
后置处理...

可以看出 CGLib 和 JDK Proxy 的实现代码比较类似,都是通过实现代理器的接口,再调用某一个方法完成动态代理的,唯一不同的是,CGLib 在初始化被代理类时,是通过 Enhancer 对象把代理对象设置为被代理类的子类来实现动态代理的。因此被代理类不能被关键字 final 修饰,如果被 final 修饰,再使用 Enhancer 设置父类时会报错,动态代理的构建会失败。


4.Lombok

Lombok 属于 Java 的一个热门代码生成工具类,使用它可以自动生成 Setter、Getter、toString、equals 和 hashCode 等等方法。


看一个实际的示例如下:

反编译的 class 文件如下:

可以看出 Lombok 是在编译期就为我们生成了对应的字节码。


Lombok 是基于 Java 1.6 实现的 JSR 269: Pluggable Annotation Processing API 来实现的,也就是通过编译期自定义注解处理器来实现的,它的执行步骤如下:

从流程图中可以看出,在编译期阶段,当 Java 源码被抽象成语法树(AST)之后,Lombok 会根据自己的注解处理器动态修改 AST,增加新的代码(节点),在这一切执行之后就生成了最终的字节码(.class)文件,这就是 Lombok 的执行原理。


5.总结

本章介绍了 JDK Proxy 和 CGLib 的区别


JDK Proxy 是 Java 语言内置的动态代理,必须要通过实现接口的方式来代理相关的类;CGLib 是第三方提供的基于 ASM 的高效动态代理类,它通过实现被代理类的子类来实现动态代理的功能,因此被代理的类不能使用 final 修饰。
6.参考
《Java 源码剖析 34 讲》- 王磊



友情链接: