a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 124|回复: 1

[专业语言] Java认证辅导:关于用动态代理进行修饰(2)

[复制链接]
发表于 2012-8-4 12:44:44 | 显示全部楼层 |阅读模式
清单 2. 包装 Set 的简单的动态代办代庖
" {6 E: A& t8 H% {1 A9 j' a- Jpublic class SetProxyFactory { public static Set getSetProxy(final Set s) { return (Set) Proxy.newProxyInstance (s.getClass().getClassLoader(), new Class[] { Set.class }, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(s, args); } }); } }3 q0 A; M5 u' b2 R
SetProxyFactory 类包含一个静态工场体例 getSetProxy(),它返回一个实现了 Set 的动态代办代庖。代办代庖对象现实实现 Set —— 挪用者无法区分(除非经由过程反射)返回的对象是动态代办代庖。SetProxyFactory 返回的代办代庖只做一件事,把体例分拨给传递给工场体例的 Set 实例。虽然反射代码凡是斗劲难读,可是这里的内容很少,跟上节制流程并不难 —— 只要某个体例在 Set 代办代庖上被挪用,它就被分拨给挪用句柄,挪用句柄只是反射地挪用底层包装的对象上的方针体例。当然,绝对什么都不做的代办代庖可能有点傻,是不是呢?
  ]$ x" m# L- e& ?8 B什么都不做的适配器
7 W, A/ [# I. d$ k对于像 SetProxyFactory 这样什么都不做的包装器来说,现实有个很好的应用 —— 可以用它平安地把对象引用典型围缩小到特假寓口(或接口集)上,体例是,挪用者不能晋升引用的类型,使得可以更平安地把对象引用传递给不受信赖的代码(例如插件或回调)。清单 3 包含一组类界说,实现了典型的回调场景。从中会看到动态代办代庖可以更便利地替代凡是用手工(或用 IDE 供给的代码生成向导)实现的 Adapter 模式。" l& g6 a# A& a7 x6 n: v: K
清单 3. 典型的回调场景public interface ServiceCallback { public void doCallback(); } public interface Service { public void serviceMethod(ServiceCallback callback); } public class ServiceConsumer implements ServiceCallback { private Service service; 。.. public void someMethod() { 。.. service.serviceMethod(this); } }2 @1 |; Z" U- A
ServiceConsumer 类实现了 ServiceCallback(这凡是是撑持回调的一个便利路子)并把 this 引用传递给 serviceMethod() 作为回调引用。这种体例的问题是没有机制可以阻止 Service 实现把 ServiceCallback 晋升为 ServiceConsumer,并挪用 ServiceConsumer 不但愿 Service 挪用的体例。有时对这个风险并不关心 —— 但有时却关心。如不美观关心,那么可以把回调对象作为内部类,或者编写一个什么都不做的适配器类(请参阅清单 4 中的 ServiceCallbackAdapter)并用 ServiceCallbackAdapter 包装 ServiceConsumer。ServiceCallbackAdapter 防止 Service 把 ServiceCallback 晋升为 ServiceConsumer。5 a  R0 O1 ?* F! }. {& q
清单 4. 用于平安地把对象限制在一个接口上以便不被恶意代码不能的适配器类public class ServiceCallbackAdapter implements ServiceCallback { private final ServiceCallback cb; public ServiceCallbackAdapter(ServiceCallback cb) { this.cb = cb; } public void doCallback() { cb.doCallback(); } }3 i* m) w' b3 u1 [
/ V) Z) Q2 n8 r: g4 o! m3 [
编写 ServiceCallbackAdapter 这样的适配器类简单却乏味。必需为包装的接口中的每个体例编写重定向类。在 ServiceCallback 的示例中,只有一个需要实现的体例,可是某些接口,例如 Collections 或 JDBC 接口,则包含良多体例。现代的 IDE 供给了“Delegate Methods〗嵋导,降低了编写适配器类的工作量,可是仍然必需为每个想要包装的接口编写一个适配器类,而且对于只包含生成的代码的类,也有一些让人过错劲的处所。看起来理当有一种体例可以更紧凑地暗示“什么也不做的限制适配器模式”。
回复

使用道具 举报

 楼主| 发表于 2012-8-4 12:44:45 | 显示全部楼层

Java认证辅导:关于用动态代理进行修饰(2)

</p>通用适配器类
' u6 j8 C6 a4 G8 n2 B( I" Q清单 2 中的 SetProxyFactory 类当然比用于 Set 的等价的适配器类更紧凑,可是它仍然只合用于一个接口:Set。可是经由过程使用泛型,可以轻易地建树通用的代办代庖工场,由它为任何接口做同样的工作,如清单 5 所示。它几乎与 SetProxyFactory 不异,可是可以合用于任何接口。此刻再也不用编写限制适配器类了!如不美观想建树代办代庖对象平安地把对象限制在接口 T,只要挪用 getProxy(T.class,object) 就可以了,不需要一堆适配器类的额外累赘。7 d3 t) ?: E- v6 S2 B
清单 5. 通用的限制适配器工场类public class GenericProxyFactory { public static《T》 T getProxy(Class《T》 intf, final T obj) { return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(), new Class[] { intf }, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(obj, args); } }); } }
2 l( {: ?3 n2 m5 a3 I# Z动态代办代庖作为Decorator
" I. c7 U) q2 j( J- v; n& N当然,动态代办代庖工具能做的,远不仅仅是把对象类型限制在特假寓口上。从 清单 2 和 清单 5 中简单的限制适配器到 Decorator 模式,是一个小的飞跃,在 Decorator 模式中,代办代乱用额外的功能(例如平安检测或日志记实)包装挪用。清单 6 显示了一个日志 InvocationHandler,它在挪用方针对象上的体例之外,还写入一条日志信息,显示被挪用的体例、传递的参数,以及返回值。除了反射性的 invoke() 挪用之外,这里的全数代码只是生成调试信息的一部门 —— 还不是太多。代办代庖工场体例的代码几乎与 GenericProxyFactory 不异,区别在于它使用的是 LoggingInvocationHandler 而不是匿名的挪用句柄。
8 h( ?  G$ r: f1 I清单 6. 基于代办代庖的 Decorator,为每个体例挪用生成调试日志
" M. L) u- X; C0 M2 X! n; zprivate static class LoggingInvocationHandler《T》 implements InvocationHandler { final T underlying; public LoggingHandler(T underlying) { this.underlying = underlying; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { StringBuffer sb = new StringBuffer(); sb.append(method.getName()); sb.append(“(”); for (int i=0; args != null && i《args.length; i++) { if (i != 0) sb.append(“, ”); sb.append(args[i]); } sb.append(“)”); Object ret = method.invoke(underlying, args); if (ret != null) { sb.append(“ -》 ”); sb.append(ret); } System.out.println(sb); return ret; } }
  f- i+ v+ b' D9 Y$ m" t8 w如不美观用日志代办代庖包装 HashSet,并执行下面这个简单的测试轨范:
9 p" y1 u8 l4 S4 n6 G# o# rSet s = newLoggingProxy(Set.class, new HashSet()); s.add(“three”); if (!s.contains(“four”)) s.add(“four”); System.out.println(s);
7 M4 h8 S. g1 E" M/ e- _; m会获得以下输出:
4 [3 P0 e, u) J4 t! Wadd(three) -》 true contains(four) -》 false add(four) -》 true toString() -》 [four, three] [four, three]
  b/ E+ f/ q0 R' V5 n这种体例是给对象添加调试包装器的一种好的而且轻易的体例。它当然比生成代办代庖类并手工建树大量 println() 语句轻易得多(也更通用)。我进一步改良了这一体例;不必无前提地生成调试输出,相反,代办代庖可以发芽动态设置装备摆设存储(从设置装备摆设文件初始化,可以由 JMX MBean 动态改削),确定是否需要生成调试语句,甚至可能在逐个类或逐个实例的基本长进行。. z$ J3 \9 @/ l9 `/ W# j/ Q
在这一点上,我认为读者中的 AOP 快乐喜爱者们几乎要特出来说“这恰是 AOP 擅长的啊!”是的,可是解决问题的体例不止一种 —— 仅仅因为某项手艺能解决某个问题,并不意味着它就是最好的解决方案。在任何情形下,动态代办代庖体例都有完全在“纯 Java”规模内工作的优势,不是每个公司都用(或理当用) AOP 的。. s! C! T( O/ M3 C; X
动态代办代庖作为适配器1 r% V6 I. N: g$ o  O
代办代庖也可以用作真正的适配器,供给了对象的一个视图,导出与底层对象实现的接口分歧的接口。挪用句柄不需要把每个体例挪用都分拨给不异的底层对象;它可以搜检名称,并把分歧的体例分拨给分歧的对象。例如,假设有一组暗示持久实体(Person、Company 和 Purchaseorder) 的 JavaBean 接口,指定了属性的 getter 和 setter,而且正在编写一个持久层,把数据库记实映射到实现这些接口的对象上。此刻不用为每个接口编写或生成类,可以只用一个 JavaBean 气概的通用代办代庖类,把属性保留在 Map 中。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-4-29 22:17 , Processed in 0.214855 second(s), 23 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表