Java动态代理与常规代理的实用性

14

我需要一些建议,了解哪些情况下动态代理比普通代理更有用。

我已经花了很多精力学习如何有效地使用动态代理。在这个问题中,暂时不考虑像AspectJ这样的框架可以执行基本上我们尝试使用动态代理实现的所有功能,或者例如CGLIB可以用于解决动态代理的某些缺点。

使用案例

  • 装饰器-例如,在方法调用时执行日志记录,或缓存复杂操作的返回值
  • 维护契约 - 即,确保参数在接受范围内并且返回类型符合接受的值。
  • 适配器 - 看到某篇聪明的文章描述了这个有用的设计模式。不过我很少遇到这种情况。

还有其他用例吗?

动态代理优势

  • 装饰器:记录所有方法调用,例如:

public Object invoke(Object target, Method method, Object[] arguments) {
         System.out.println("before method " + method.getName());
         return method.invoke(obj, args);
     }
}

装饰者模式确实很有用,因为它允许在所有代理方法中产生副作用(尽管这种行为是使用方面的经典示例..)。

  • 契约:与常规代理相比,我们不需要实现完整的接口。例如,

public Object invoke(Object target, Method method, Object[] arguments) {
     if ("getValues".equals(method.getName()) {
         // check or transform parameters and/or return types, e.g., 
         return RangeUtils.validateResponse( method.invoke(obj, args) );
     }

     if ("getVersion".equals(method.getName()) {
         // another example with no delegation
         return 3;
     }
} 

另一方面,合同只是为了避免实现完整接口而获得好处。然而,重构代理方法将会默默地使动态代理失效。

结论

因此,我在这里看到一个真正的用例和一个可疑的用例。你的意见是什么?


3
该死,我们需要在Java中使用方法字面量!我讨厌使用方法名称的代码。 - dty
3个回答

13

动态代理有许多潜在的用途,超越了你所描述的范围,包括:

  1. 事件发布 - 在方法x()上,透明地调用y()或发送消息z。
  2. 事务管理(针对数据库连接或其他事务性操作)
  3. 线程管理 - 透明地进行大量操作。
  4. 性能追踪 - 计时操作通过CountdownLatch检查,例如。
  5. 连接管理 - 考虑到像Salesforce企业API这样的API,要求其服务的客户端在执行任何操作之前启动会话。
  6. 更改方法参数 - 如果你想为null传递默认值,那就是你想要的。

除了如上所述的验证和日志记录之外,这些只是一些其他选项。JSR 303是一个bean验证规范,Hibernate Validator中具有AOP样式的实现,因此您不需要专门为数据对象实现它。Spring框架还内置了验证,并与AspectJ进行良好的集成,可以完成上述部分功能。


感谢您提供的全面列表,我肯定可以看到其中的有效用例。您说得对,像Hibernate和Spring这样的框架提供了非常好的实现来解决其中一些问题,但是当项目中没有使用它们时,我可以证明直接使用动态代理是有道理的。 - Johan Sjöberg
除了第四点之外,其他原因在一般系统模式设计方面都不是很好的实践。 - Bryan Fok

11

实际上,AOP最适用于动态代理。这是因为您可以在不提前知道对象的情况下创建动态代理。

动态代理的另一个有用的方面是,当您想要对所有方法应用相同的操作时。使用静态代理,您需要大量重复的代码(在每个代理方法中,您需要对方法进行相同的调用,然后委托给代理对象),而动态代理会将此最小化。

还要注意,适配器模式和装饰器模式是单独的模式。它们看起来像代理模式一样实现(通过对象组合),但它们具有不同的目的:

  • 装饰器模式允许您拥有多个具体装饰器,从而在运行时添加功能
  • 适配器模式旨在将一个对象适配到不匹配的接口上。我能想到的最好的例子是EnumetationIterator - 它将 Enumeration适配到Iterator接口。

实际上,当对所有方法应用相同的操作时,动态代理可能非常有用。我可以看到在验证或执行安全检查时可以使用它。 - Johan Sjöberg

0

我能想到的另一个用例是在运行时动态实现接口,这是一些框架的工作方式。

例如 Retrofit,它是一个用于消费 REST 服务的 Java 库。您定义一个反映 REST API 中可用操作的 Java 接口,并使用注释装饰方法以配置请求的特定内容。很容易看出,在这种情况下,接口中定义的所有方法都必须执行针对某个服务器的 HTTP 请求,将方法参数转换为请求参数;然后将响应解析为作为方法返回类型定义的 java 对象。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接