JDK 动态代理的实现方式是反射。
反射机制
是指程序在运行期间可以访问、检测和修改其本身状态或行为的一种能力,使用反射我们可以调用任意一个类对象,以及类对象中包含的属性及方法。
CGLib 实现动态代理是基于 ASM(一个 Java 字节码操作框架)而非反射实现的。
动态代理是一种行为方式,反射或 ASM 只是它的一种实现手段而已。
JDK Proxy 和 CGLib 的区别主要体现在以下几个方面:
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 接口来拥有动态代理的能力。
示例代码:
public class CGLibDemo {
static class Car {
public void run() {
System.out.println("the car is running");
}
}
static class CglibProxy<T> 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<Car> cglibProxy = new CglibProxy<>();
Car car = cglibProxy.getInstance(new Car());
car.run();
}
}
执行结果:
前置处理...
the car is running
后置处理...
可以看出 CGLib 和 JDK Proxy 的实现代码比较类似,都是通过实现代理器的接口,再调用某一个方法完成动态代理的,唯一不同的是,CGLib 在初始化被代理类时,是通过 Enhancer 对象把代理对象设置为被代理类的子类来实现动态代理的。因此被代理类不能被关键字 final 修饰,如果被 final 修饰,再使用 Enhancer 设置父类时会报错,动态代理的构建会失败。
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 的执行原理。
本章介绍了 JDK Proxy 和 CGLib 的区别