Java动态代理的两种主要形式
动态代理在Java中有两种主要的实现方式,它们各有特点和使用场景:
1. JDK动态代理 (基于接口)
特点:
- Java标准库自带的功能(
java.lang.reflect.Proxy
) - 只能代理接口,不能代理普通类
- 运行时动态生成代理类
- 性能较好
使用场景:
- 当你的类已经实现了接口时
- Spring AOP默认使用这种方式
示例代码:
java
// 接口
interface UserService {
void saveUser();
}
// 实现类
class UserServiceImpl implements UserService {
public void saveUser() {
System.out.println("保存用户");
}
}
// 创建代理
UserService proxy = (UserService) Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[]{UserService.class},
(proxyObj, method, args) -> {
System.out.println("前置处理");
Object result = method.invoke(new UserServiceImpl(), args);
System.out.println("后置处理");
return result;
}
);
proxy.saveUser();
2. CGLIB动态代理 (基于继承)
特点:
- 需要引入第三方库(cglib)
- 可以代理普通类,不要求有接口
- 通过继承目标类并重写方法实现代理
- 生成速度比JDK代理稍慢,但执行效率高
使用场景:
- 当要代理的类没有实现接口时
- Spring AOP当目标类没有接口时自动使用CGLIB
示例代码:
java
// 普通类(没有接口)
class ProductService {
public void addProduct() {
System.out.println("添加产品");
}
}
// 创建代理
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ProductService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println("前置处理");
Object result = proxy.invokeSuper(obj, args);
System.out.println("后置处理");
return result;
});
ProductService proxy = (ProductService) enhancer.create();
proxy.addProduct();
两种方式的对比
特性 | JDK动态代理 | CGLIB动态代理 |
---|---|---|
实现方式 | 基于接口 | 基于继承 |
是否需要接口 | 必须 | 不需要 |
性能 | 生成快,调用稍慢 | 生成慢,调用快 |
限制 | 只能代理接口方法 | 不能代理final类/方法 |
依赖 | Java内置 | 需要第三方库 |
如何选择?
- 如果目标对象有接口,优先用JDK动态代理(Spring默认策略)
- 如果目标对象没有接口,只能用CGLIB
- 对性能要求极高时,可以考虑CGLIB
- 需要代理final方法时,只能用JDK动态代理
现代框架如Spring会根据情况自动选择最合适的代理方式。