问题历史:
动态代理模式出现在Java 1.3,允许实现AOP模式、日志记录、安全性、动态代码生成,而无需手动编写代理类。这在Spring、Hibernate和其他框架中变得非常流行。
问题:
有时需要为已存在的接口添加功能,或在运行时更改方法的行为,而不改变源代码。例如:记录每个方法调用、权限检查、性能分析。主要问题是无法手动为所有类做到这一点,特别是在类很多或动态生成的情况下。
解决方案:
Java提供了标准API,位于java.lang.reflect包中,可以通过Proxy.newProxyInstance为任何接口动态创建代理对象。通过实现InvocationHandler对象来处理每个方法调用并实现自己的逻辑。
代码示例:
import java.lang.reflect.*; interface Service { void serve(); } class ServiceImpl implements Service { public void serve() { System.out.println("Serving..."); } } class LoggingHandler implements InvocationHandler { private final Object target; public LoggingHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Calling " + method.getName()); return method.invoke(target, args); } } Service service = (Service) Proxy.newProxyInstance( Service.class.getClassLoader(), new Class[]{Service.class}, new LoggingHandler(new ServiceImpl())); service.serve();
主要特点:
可以通过标准的动态代理"包装"普通类,而不实现接口吗?
不可以。动态代理仅适用于接口。对于类,需要字节码操作(例如,CGLIB)。
如果接口包含返回类型与InvocationHandler返回值不兼容的方法,会发生什么?
将抛出ClassCastException,因此返回值必须严格与接口的定义兼容。
可以对final方法使用动态代理吗?
不可以。代理仅在接口级别工作,而final方法在接口中是不存在的。final方法不能被重写。
负面案例
开发人员尝试通过Proxy对没有接口的类实施审计日志记录。
优点:
缺点:
积极案例
通过接口定义了合同,已经为此创建了代理封装。
优点:
缺点: