动态代理在 Java 中有着广泛的应用,例如:Spring AOP 面向切面编程,Hibernate 数据查询、以及 RPC Dubbo 远程调用等,都有非常多的实际应用@mikechen
目录
按照代理的创建时期,代理类可以分为两种:
动态代理类与静态代理类最主要的不同点是:代理类的字节码不是在程序运行前生成的,而是在程序运行时再虚拟机中程序自动创建的。
动态代理的实现方式很多。例如:JDK 自身提供的动态代理,就利用了上面提到的反射机制。除了反射,动态代理还可以通过 CGLib 来实现,而 CGLib 是基于 ASM(一个 Java 字节码操作框架)而非反射实现的。
简单来说,动态代理是一种行为方式,而 反射或 ASM 只是它的一种实现手段而已。
本文我主要详解 Java 动态代理的 2 种主流现方式:JDK 原生动态代理与 CGLib 。
JDK Proxy 动态代理的实现无需引用第三方类,只需要实现 InvocationHandler 接口,重写 invoke() 方法即可,整个实现代码如下所示:、
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK Proxy 相关示例
*/
public class ProxyExample {
static interface Car {
void running();
}
static class Bus implements Car {
@Override
public void running() {
System.out.println("The bus is running.");
}
}
static class Taxi implements Car {
@Override
public void running() {
System.out.println("The taxi is running.");
}
}
/**
* JDK Proxy
*/
static class JDKProxy implements InvocationHandler {
private Object target; // 代理对象
// 获取到代理对象
public Object getInstance(Object target) {
this.target = target;
// 取得代理对象
return Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
/**
* 执行代理方法
* @param proxy 代理对象
* @param method 代理方法
* @param args 方法的参数
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws InvocationTargetException, IllegalAccessException {
System.out.println("动态代理之前的业务处理.");
Object result = method.invoke(target, args); // 执行调用方法(此方法执行前后,可以进行相关业务处理)
return result;
}
}
public static void main(String[] args) {
// 执行 JDK Proxy
JDKProxy jdkProxy = new JDKProxy();
Car carInstance = (Car) jdkProxy.getInstance(new Taxi());
carInstance.running();
以上程序的执行结果是:
动态代理之前的业务处理。
The taxi is running.
可以看出, JDK Proxy 实现动态代理的核心是实现 Invocation 接口,我们查看 Invocation 的源码,会发现里面其实只有一个 invoke() 方法,源码如下:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
这是因为在动态代理中有一个重要的角色,也就是代理器,它用于统一管理被代理的对象,显然 InvocationHandler 就是这个代理器。而 invoke() 方法,则是触发代理的执行方法,我们通过实现 Invocation 接口来拥有动态代理的能力。
CGLIB (Code Generation Library) 是一个基于 ASM 的字节码生成库,它允许我们在运行时对字节码进行修改、和动态生成 CGLIB 通过继承方式实现代理。
在使用 CGLib 之前,我们要先在项目中引入 CGLib 框架,在 pom.xml 中添加如下配置:
cglib
cglib
3.3.0
CGLib 的实现代码:
package com.mikechen.proxydemo;
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 CGLibExample {
static class Car {
public void running() {
System.out.println("The car is running.");
}
}
/**
* CGLib 代理类
*/
static class CGLibProxy implements MethodInterceptor {
private Object target; // 代理对象
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
// 设置父类为实例类
enhancer.setSuperclass(this.target.getClass());
// 回调方法
enhancer.setCallback(this);
// 创建代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method,
Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("方法调用前业务处理.");
Object result = methodProxy.invokeSuper(o, objects); // 执行方法调用
return result;
}
}
// 执行 CGLib 的方法调用
public static void main(String[] args) {
// 创建 CGLib 代理类
CGLibProxy proxy = new CGLibProxy();
// 初始化代理对象
Car car = (Car) proxy.getInstance(new Car());
// 执行方法
car.running();
以上程序的执行结果是:
方法调用前业务处理。
The car is running.
可以看出:
CGLib 和 JDK Proxy 的实现代码比较类似,都是通过实现代理器的接口,再调用某一个方法完成动态代理的。
唯一不同的是,CGLib 在初始化被代理类时,是通过 Enhancer 对象把代理对象设置为被代理类的子类,来实现动态代理的。
因此,被代理类不能被关键字 final 修饰,如果被 final 修饰,再使用 Enhancer 设置父类时会报错,动态代理的构建会失败。
1. JDK 动态代理具体实现原理
2. CGLib 动态代理
CGLib 是一个强大、高性能的 Code 生产类库,可以实现运行期动态扩展 java 类,Spring 在运行期间通过 CGlib 继承要被动态代理的类,重写父类的方法,实现 AOP 面向切面编程。
3. 两者对比
4. 性能对比
因此,对于 singleton 的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,更适合采用 CGLib 动态代理,反之,则比较适用 JDK 动态代理。
以上,是关于 Java 动态代理原理、以及动态代理2 种实现方式的解析。
谢谢【关注+点赞+转发】支持下。
陈睿 | mikechen , 10年+大厂架构经验,「mikechen 的互联网架构」系列文章作者,专注于互联网架构技术。
阅读「mikechen 的互联网架构」40W 字技术文章合集
Java并发 | JVM | MySQL | Spring | Redis | 分布式 | 高并发
--- end ---