java的动态代理
Java 中的动态代理是一种设计模式,允许程序在运行时动态地生成代理类,而不是在编译时确定。它的主要作用是对接口的实现进行增强,使得我们可以在调用方法前后添加一些操作,比如日志、事务控制等。动态代理有两种常见的实现方式:
- JDK 动态代理:基于接口实现的代理,使用
java.lang.reflect.Proxy
和InvocationHandler
。 - CGLIB 动态代理:基于子类继承的代理,使用字节码生成,适用于没有接口的类。
以下是对这两种实现方式的原理和示例的详细介绍。
1. JDK 动态代理
JDK 动态代理基于 Java 的 Proxy
类和 InvocationHandler
接口来实现,只能代理实现了接口的类。其原理是通过生成代理类在调用方法时拦截,并由 InvocationHandler
中的 invoke()
方法处理。
实现原理
- 代理类生成:在运行时,
Proxy
类使用Proxy.newProxyInstance()
方法生成代理对象。 - 方法拦截:代理对象调用方法时会被
InvocationHandler
接口的invoke()
方法拦截,invoke()
方法可以在调用前后插入逻辑。 - 字节码生成:JDK 动态代理利用字节码生成技术动态生成代理类,在 JVM 中加载执行。
实现示例
假设我们有一个 Service
接口和它的实现类 ServiceImpl
:
public interface Service {void performTask();
}public class ServiceImpl implements Service {public void performTask() {System.out.println("Executing task...");}
}
创建动态代理类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class DynamicProxyHandler implements InvocationHandler {private final Object target;public DynamicProxyHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method invocation");Object result = method.invoke(target, args);System.out.println("After method invocation");return result;}public static void main(String[] args) {// 创建目标对象Service target = new ServiceImpl();// 创建代理对象Service proxy = (Service) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new DynamicProxyHandler(target));// 调用代理方法proxy.performTask();}
}
执行结果:
Before method invocation
Executing task...
After method invocation
适用场景
- 适用于代理接口类,不能代理普通类。
- 常用于拦截器、权限控制、日志记录、事务管理等。
2. CGLIB 动态代理
CGLIB(Code Generation Library)是一种基于字节码生成的动态代理实现方式,能够代理普通类,适用于没有实现接口的类。CGLIB 动态代理通过生成目标类的子类,并重写其中的方法来实现。
实现原理
- 子类继承:CGLIB 通过生成目标类的子类实现代理,目标类的方法被拦截。
- 方法拦截:代理类调用方法时会被
MethodInterceptor
拦截,MethodInterceptor
可以在方法调用前后加入自定义逻辑。 - 字节码增强:CGLIB 通过 ASM 字节码生成框架动态生成字节码,并在 JVM 中加载。
实现示例
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 {private final Object target;public CGLibProxy(Object target) {this.target = target;}public Object createProxy() {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(target.getClass());enhancer.setCallback(this);return enhancer.create();}@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method invocation");Object result = proxy.invokeSuper(obj, args);System.out.println("After method invocation");return result;}public static void main(String[] args) {// 目标对象ServiceImpl target = new ServiceImpl();// 创建代理对象ServiceImpl proxy = (ServiceImpl) new CGLibProxy(target).createProxy();// 调用代理方法proxy.performTask();}
}
执行结果:
Before method invocation
Executing task...
After method invocation
适用场景
- 适用于没有接口的普通类。
- 由于 CGLIB 是通过继承实现的,所以无法代理
final
方法和final
类。
JDK 动态代理和 CGLIB 的对比
特性 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|
代理方式 | 基于接口 | 基于继承 |
性能 | 较高,适合频繁调用的小方法 | 较低,但适合复杂的调用 |
代理目标 | 接口 | 普通类(但不能代理 final 类和方法) |
实现库 | Java 自带 | 第三方库(cglib 或 spring-core ) |
常用场景 | AOP、拦截器等 | 代理没有接口的类,AOP |
总结
动态代理是一种重要的设计模式,允许在运行时动态增强对象的功能。JDK 动态代理适合代理接口,CGLIB 则适合没有接口的类。通过了解两者的原理和实现,可以灵活运用动态代理技术来增强 Java 应用的功能,如日志、事务管理、权限控制等。