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;
     }
} 

另一方面,合约只提供了避免需要实现完整接口的好处。再一次,重构代理方法会默默地使动态代理无效。

结论

所以我在这里看到的是一个真实的用例和一个有问题的用例。你怎么看?

4

3 回答 3

13

除了您所描述的之外,动态代理还有许多潜在用途 -

  1. 事件发布 - 在方法 x() 上,透明地调用 y() 或发送消息 z。
  2. 事务管理(用于数据库连接或其他事务操作)
  3. 线程管理 - 透明地执行昂贵的操作。
  4. 性能跟踪 - 例如,由 CountdownLatch 检查的计时操作。
  5. 连接管理 - 考虑像 Salesforce 的 Enterprise API 这样的 API,要求其服务的客户端在执行任何操作之前启动会话。
  6. 更改方法参数 - 如果您想传递空值的默认值,如果那是您的事情。

这些只是您上面描述的验证和日志记录之外的几个选项。FWIW,JSR 303,一个 bean 验证规范,在 Hibernate Validator 中有一个 AOP 风格的实现,所以你不需要专门为你的数据对象实现它。Spring 框架还内置了验证功能,并与 AspectJ 很好地集成了此处描述的一些内容。

于 2010-09-12T20:46:47.933 回答
11

事实上,AOP 使大多数动态代理受益。那是因为您可以围绕您事先不知道的对象创建动态代理。

动态代理的另一个有用的方面是当您想对所有方法应用相同的操作时。使用静态代理,您需要大量重复的代码(在每个代理方法上,您需要对方法进行相同的调用,然后委托给代理对象),而动态代理可以最大限度地减少这种情况。

另请注意,适配器和装饰器是独立的模式。它们的实现方式(通过对象组合)看起来像代理模式,但它们的用途不同:

  • 装饰器模式允许您拥有多个具体的装饰器,从而在运行时添加功能
  • 适配器模式旨在使对象适应不匹配的接口。我能想到的最好的例子是EnumetationIterator——它适应EnumerationIterator界面。
于 2010-09-12T18:51:44.113 回答
0

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

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

于 2014-11-25T23:25:30.237 回答