java动态代理与常规代理的有用性

时间:2010-09-12 18:44:34

标签: java design-patterns proxy dynamic-proxy use-case

我需要一些建议,动态代理比常规代理更有用。

我已经投入了大量精力来学习如何有效地使用动态代理。在这个问题中,抛开像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 个答案:

答案 0 :(得分:12)

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

  1. 事件发布 - 在方法x()上,透明地调用y()或发送消息z。
  2. 事务管理(用于数据库连接或其他事务操作)
  3. 线程管理 - 透明地解决昂贵的操作。
  4. 性能跟踪 - 例如,由CountdownLatch检查的计时操作。
  5. 连接管理 - 考虑像Salesforce的Enterprise API这样的API,要求其服务的客户在执行任何操作之前启动会话。
  6. 更改方法参数 - 如果您想传递空值的默认值,如果这是您的事情。
  7. 除了上面描述的验证和日志记录之外,这些只是一些选项。 FWIW,JSR 303,一个bean验证规范,在Hibernate Validator中有一个AOP风格的实现,所以你不需要专门为你的数据对象实现它。 Spring框架还内置了验证,并且与AspectJ集成了很好的集成,用于描述这里描述的一些内容。

答案 1 :(得分:11)

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

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

另请注意,Adapter和Decorator是单独的模式。它们在实现方式上看起来像代理模式(通过对象组合),但它们有不同的用途:

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

答案 2 :(得分:0)

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

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