问题的背景:
代理对象和代理设计模式在Java中出现,是为了创建可以控制对真实对象的访问的替代对象,这些对象可以更改其行为或注入横切功能(如安全性、日志记录、指标)。从Java 1.3开始,标准的动态代理在java.lang.reflect包中得到了支持,后来出现了流行的库CGLIB和ByteBuddy。
问题:
并不总是可以/方便直接修改现有类的逻辑。常常需要透明地添加行为(例如日志记录、缓存、事务),而不改变对象的源代码,这在标准继承方式下是不可能的。
解决方案:
代理对象可以通过静态方式(手动子类化)和动态方式(通过反射机制或字节码)实现。动态代理允许在运行时创建实现所需接口的对象,并在invoke()方法内将调用委托给实际对象,从而提供了注入外部行为的灵活性。
代码示例:
import java.lang.reflect.*; interface Service { void doWork(); } class RealService implements Service { public void doWork() { System.out.println("Doing work!"); } } class LoggingInvocationHandler implements InvocationHandler { private final Object target; public LoggingInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Calling: " + method.getName()); return method.invoke(target, args); } } Service real = new RealService(); Service proxy = (Service) Proxy.newProxyInstance( real.getClass().getClassLoader(), new Class[] {Service.class}, new LoggingInvocationHandler(real)); proxy.doWork(); // 记录调用
关键特点:
Java中的标准动态代理可以与不实现接口的类一起工作吗?
不能。Java中的标准代理只能与接口一起使用。对于没有接口的类(例如,如果您想代理一个普通类),需要第三方工具(例如CGLIB、ByteBuddy)。
可以为具有私有方法的接口创建代理吗?
不能。Java中的接口不能包含需要代理的私有方法。动态代理实现接口的公共方法。从Java 9开始出现了私有默认方法,但它们无法通过代理访问。
InvocationHandler与MethodInterceptor(CGLIB)之间有什么区别?
InvocationHandler是用于动态代理的标准JDK接口。它接受接口方法的调用。MethodInterceptor是CGLIB的接口,允许通过动态继承拦截任何类的方法调用。它们的适用性和级别不同:JDK仅适用于接口,CGLIB适用于任何类。
工程师在所有服务的每个方法中手动重复日志记录代码。在添加新方法时不断复制逻辑,并常常忘记添加必要的调用。
优点:
缺点:
项目通过动态代理实现AOP:日志记录和事务控制通过中心化实现的代理进行。
优点:
缺点: