编程Java架构师

什么是代理,Java中的代理类型,以及如何通过它实现对象的动态行为?

用 Hintsage AI 助手通过面试

答案。

问题的背景:

代理对象和代理设计模式在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(); // 记录调用

关键特点:

  • 静态代理需要为每个功能预先定义代码
  • 动态代理可以在运行时为任何接口创建
  • 对于没有接口的类,需要第三方库(如CGLIB)

误导性问题。

Java中的标准动态代理可以与不实现接口的类一起工作吗?

不能。Java中的标准代理只能与接口一起使用。对于没有接口的类(例如,如果您想代理一个普通类),需要第三方工具(例如CGLIB、ByteBuddy)。

可以为具有私有方法的接口创建代理吗?

不能。Java中的接口不能包含需要代理的私有方法。动态代理实现接口的公共方法。从Java 9开始出现了私有默认方法,但它们无法通过代理访问。

InvocationHandler与MethodInterceptor(CGLIB)之间有什么区别?

InvocationHandler是用于动态代理的标准JDK接口。它接受接口方法的调用。MethodInterceptor是CGLIB的接口,允许通过动态继承拦截任何类的方法调用。它们的适用性和级别不同:JDK仅适用于接口,CGLIB适用于任何类。

常见错误和反模式

  • 类型错误:试图对不实现接口的类使用代理
  • 隐式循环调用导致StackOverflow
  • 代理不适合代理的类,或者试图代理final类

生活中的示例

负面案例

工程师在所有服务的每个方法中手动重复日志记录代码。在添加新方法时不断复制逻辑,并常常忘记添加必要的调用。

优点:

  • 透明,容易理解执行过程

缺点:

  • 巨大的时间浪费,重复性问题代码,难以维护

正面案例

项目通过动态代理实现AOP:日志记录和事务控制通过中心化实现的代理进行。

优点:

  • 更快地引入和更改任何横切功能,无需重复

缺点:

  • 需要理解代理的工作原理,调试时可能存在潜在复杂性