a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 116|回复: 0

[专业语言] Java认证之Java实践用动态代理进行修饰(3)

[复制链接]
发表于 2012-8-4 12:44:44 | 显示全部楼层 |阅读模式
Java认证之Java实践用动态代理进行修饰(3)
  }5 ]- Z5 y2 _9 j, k9 K; t0 V) X; F4 C8 M( Y8 V. u

* k+ m! O' B- h/ _7 o0 j# o动态代理作为适配器
2 F8 E% L2 N! ?0 `; x代理也可以用作真正的适配器,提供了对象的一个视图,导出与底层对象实现的接口不同的接口。调用句柄不需要把每个方法调用都分派给相同的底层对象;它可以检查名称,并把不同的方法分派给不同的对象。例如,假设有一组表示持久实体(Person、Company 和 PurchaseOrder) 的 JavaBean 接口,指定了属性的 getter 和 setter,而且正在编写一个持久层,把数据库记录映射到实现这些接口的对象上。现在不用为每个接口编写或生成类,可以只用一个 JavaBean 风格的通用代理类,把属性保存在 Map 中。
5 v7 N! U3 }$ s7 h3 u清单 7 显示的动态代理检查被调用方法的名称,并通过查询或修改属性图直接实现 getter 和 setter 方法。现在,这一个代理类就能实现多个 JavaBean 风格接口的对象。
4 w7 q+ J5 @: @) U% A清单 7. 用于把 getter 和 setter 分派给 Map 的动态代理类4 k# `2 O# S, R$ g
public class JavaBeanProxyFactory { private static class JavaBeanProxy implements InvocationHandler { Map《String, Object》 properties = new HashMap《String, Object》(); public JavaBeanProxy(Map《String, Object》 properties) { this.properties.putAll(properties); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String meth = method.getName(); if (meth.startsWith(“get”)) { String prop = meth.substring(3); Object o = properties.get(prop); if (o != null && !method.getReturnType().isInstance(o)) throw new ClassCastException(o.getClass().getName() + “ is not a ” + method.getReturnType().getName()); return o; } else if (meth.startsWith(“set”)) { // Dispatch setters similarly } else if (meth.startsWith(“is”)) { // Alternate version of get for boolean properties } else { // Can dispatch non get/set/is methods as desired } } } public static《T》 T getProxy(Class《T》 intf, Map《String, Object》 values) { return (T) Proxy.newProxyInstance (JavaBeanProxyFactory.class.getClassLoader(), new Class[] { intf }, new JavaBeanProxy(values)); } }
. ?( Y  n" ^8 y8 d: Y+ G( v, s虽然因为反射在 Object 上工作会有潜在的类型安全性上的损失,但是,JavaBeanProxyFactory 中的 getter 处理会进行一些必要的额外的类型检测,就像我在这里用 isInstance() 对 getter 进行的检测一样。
: E# I( i1 O: L2 t性能成本' j: j7 U; }. o6 {
正如已经看到的,动态代理拥有简化大量代码的潜力 —— 不仅能替代许多生成的代码,而且一个代理类还能代替多个手写的类或生成的代码。什么是成本呢?因为反射地分派方法而不是采用内置的虚方法分派,可能有一些性能上的成本。在早期的 JDK 中,反射的性能很差(就像早期 JDK 中几乎其他每件事的性能一样),但是在近 10 年,反射已经变得快多了。
( z' w: \) I7 p7 ~. o. X不必进入基准测试构造的主题,我编写了一个简单的、不太科学的测试程序,它循环地把数据填充到 Set,随机地对 Set进行插入、查询和删除元素。我用三个 Set 实现运行它:一个未经修饰的 HashSet,一个手写的、只是把所有方法转发到底层的 HashSet 的 Set 适配器,还有一个基于代理的、也只是把所有方法转发到底层 HashSet 的 Set 适配器。每次循环迭代都生成若干随机数,并执行一个或多个 Set 操作。手写的适配器比起原始的 HashSet 只产生很少百分比的性能负荷(大概是因为 JVM 级有效的内联缓冲和硬件级的分支预测);代理适配器则明显比原始 HashSet 慢,但是开销要少于两个量级。- }" `/ }: R- {  d) O& y
我从这个试验得出的结论是:对于大多数情况,代理方式即使对轻量级方法也执行得足够好,而随着被代理的操作变得越来越重量级(例如远程方法调用,或者使用序列化、执行 IO 或者从数据库检索数据的方法),代理开销就会有效地接近于 0。当然也存在一些代理方式的性能开销无法接受的情况,但是这些通常只是少数情况。" K& V' v0 n5 D( a
结束语( k& }* {; b9 P$ O
动态代理是强大而未充分利用的工具,可以用于实现许多设计模式,包括 Proxy、Decorator 和 Adapter。这些模式基于代理的实现容易编写,更难出错,并且具备更好的通用性;在许多情况下,一个动态代理类可以充当所有接口的 Decorator 或 Proxy,这样就不用每个接口都编写一个静态类。除了最关注性能的应用程序之外,动态代理方式可能比手写或机器生成 stub 的方式更可取。
回复

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-5-13 08:53 , Processed in 0.388258 second(s), 21 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

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