Java代理模式之动态代理
·
一、动态代理的核心原理
- JDK动态代理
- 原理:通过
Proxy类和InvocationHandler接口,动态生成一个实现目标接口的代理类。代理类继承自Proxy,并持有InvocationHandler实例,调用方法时通过反射调用目标对象的方法。 - 限制:必须基于接口实现,无法代理
final类或未实现接口的类。 - 底层机制:利用JVM反射机制生成代理类字节码,并通过类加载器加载。
- 原理:通过
- CGLIB动态代理
- 原理:基于字节码生成技术(如ASM框架),动态创建目标类的子类作为代理类,通过覆盖方法实现增强逻辑。
- 限制:无法代理
final类、final方法或静态方法。 - 底层机制:通过修改字节码生成代理类,无需依赖接口。
二、动态代理的实现步骤
1. JDK动态代理实现
步骤:
- 定义接口:被代理类需实现接口。
- 创建InvocationHandler:实现
invoke方法,在方法调用前后添加增强逻辑。 - 生成代理对象:通过
Proxy.newProxyInstance()方法,传入类加载器、接口列表和InvocationHandler实例。
示例代码(登录权限控制):
// 接口定义
public interface LoginManager {
boolean login(String username, String password);
}
// 目标类实现接口
public class LoginService implements LoginManager {
@Override
public boolean login(String username, String password) {
System.out.println("验证用户名密码");
return true;
}
}
// InvocationHandler实现类
public class LoginHandler implements InvocationHandler {
private Object target;
public LoginHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(target, args);
if ((boolean) result) {
System.out.println("权限验证通过");
} else {
System.out.println("权限不足");
}
return result;
}
}
// 生成代理对象
LoginManager proxy = (LoginManager) Proxy.newProxyInstance(
LoginService.class.getClassLoader(),
LoginService.class.getInterfaces(),
new LoginHandler(new LoginService())
);
2. CGLIB动态代理实现
步骤:
- 引入依赖:需添加CGLIB库(如
cglib-nodep)。 - 创建MethodInterceptor:实现
intercept方法,通过proxy.invokeSuper()调用父类方法。 - 生成代理对象:使用
Enhancer类设置父类并创建子类实例。
示例代码(日志记录):
// 目标类
public class UserDao {
public void save() {
System.out.println("保存用户数据");
}
}
// MethodInterceptor实现类
public class UserDaoInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开始执行方法");
Object result = proxy.invokeSuper(obj, args); // 调用父类方法
System.out.println("方法执行结束");
return result;
}
}
// 生成代理对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserDao.class);
enhancer.setCallback(new UserDaoInterceptor());
UserDao proxy = (UserDao) enhancer.create();
三、JDK与CGLIB动态代理的对比
| 维度 | JDK动态代理 | CGLIB动态代理 |
|---|---|---|
| 代理方式 | 基于接口生成代理类 | 基于类生成子类 |
| 性能 | JDK 8后性能优于CGLIB(反射优化) | 方法调用性能较高(无反射开销) |
| 适用场景 | 有接口的类增强 | 无接口的类增强 |
| 限制 | 无法代理final类/方法 | 无法代理final类/方法/静态方法 |
| 依赖 | JDK内置 | 需引入CGLIB库 |
四、动态代理的典型应用场景
- 事务管理
在方法调用前后开启和提交事务,如Spring AOP中的@Transactional注解实现。 - 日志记录
自动记录方法执行时间、参数及返回值,例如:public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { long startTime = System.currentTimeMillis(); Object result = method.invoke(target, args); System.out.println("方法执行耗时:" + (System.currentTimeMillis() - startTime) + "ms"); return result; } - 权限控制
在方法调用前验证用户权限,如文章1中示例的登录权限校验。 - 缓存优化
缓存高频调用结果,减少重复计算:public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String cacheKey = method.getName() + Arrays.toString(args); if (cache.containsKey(cacheKey)) { return cache.get(cacheKey); } Object result = method.invoke(target, args); cache.put(cacheKey, result); return result; }
五、动态代理的选型建议
- 优先使用JDK动态代理:
- 目标类已实现接口且性能要求较高(如JDK 8+环境)。
- 需要与Spring AOP无缝集成(默认使用JDK代理)。
- 使用CGLIB动态代理:
- 目标类未实现接口(如第三方库类)。
- 需要强制使用(如Spring配置
proxy-target-class=true)。
- 混合使用场景:
- 在Spring中,若需同时支持接口和非接口类,可通过
@EnableAspectJAutoProxy(proxyTargetClass=true)强制使用CGLIB。
- 在Spring中,若需同时支持接口和非接口类,可通过
六、总结
动态代理通过运行时生成代理类,实现了代码的解耦与功能增强,是AOP框架(如Spring)的核心技术。JDK动态代理适合接口驱动的场景,而CGLIB则弥补了无接口类的代理需求。实际开发中需根据目标类特性、性能要求及框架集成需求选择合适方案。
更多推荐

所有评论(0)