搜索 
 设为首页 加入收藏
当前位置>>首页-学院
来源: 作者: 日期:2009-06-26 14:22 网友评论:0条 点击:188
  DWR 的 Converter 实现原理简单分析及应用

我们在应用 DWR 调用远程方法时涉及到 JS 与 JAVA 之间参数和返回值的数据转换,例如:

JS 的 123 与 Java 的 int  或 Integer、long 间的转换
JS 的 "2009-06-23" 与 Java 的 java.util.Date 之间的转换
JS 的 "[1,2,3]" 与 Java 的 int[] 间的转换
JS 的 "{id:123, name: 'Unmi'}" 与 Java 的 Class Person{int id; String name} 间的转换

或 者更复杂的嵌套类型( "{id:123, name: 'Unmi', blogs:['http://unmi.blogjava.net','http://blog.csdn.net/kypfos']}" ) 与 Java 类型间的转换,等等。那么这一切是怎么进行的呢?其实我们见识过很多组件的类型映射,如 Java 的 PropertyEditor、Hibernate(UserType)、iBatis(TypeHandler) 的类型映射,Struts1/2 中 Form/Model 用的 Converter 等。

这里我来稍稍分析 DWR 的 Converter 实现,以及说明如何定制自己的 Converter。本文所用 DWR 是 2.0.5 版。

1. DWR 内置的 Converter 及应用类型
 

名称 应用类型 转换器
null void,java.lang.Void NullConverter
enum   EnumConverter
primitive boolean,byte,short,int,long,float,double,char,
java.lang.Boolean,java.lang.Byte,java.lang.Short,
java.lang.Integer,java.lang.Long,java.lang.Float,
java.lang.Double,java.lang.Character
PrimitiveConverter
bignumber java.math.BigInteger,java.math.BigDecimal BigNumberConverter
string java.lang.String StringConverter
array [Z,[B,[S,[I,[J,[F,[D,[C,[L* ArrayConverter
map java.util.Map MapConverter
collection java.util.Collection CollectionConverter
date java.util.Date,java.sql.Date,java.sql.Time,
java.sql.Timestamp,java.util.Calendar
DateConverter
dom org.w3c.dom.Node,org.w3c.dom.Element,org.w3c.dom.Document DOMConverter
dom4j org.dom4j.Document,org.dom4j.Element,org.dom4j.Node DOM4JConverter
jdom org.jdom.Document,org.jdom.Element JDOMConverter
xom nu.xom.Document,nu.xom.Element,nu.xom.Node XOMConverter
servlet javax.servlet.ServletConfig,javax.servlet.ServletContext,
javax.servlet.http.HttpServletRequest,
javax.servlet.http.HttpServletResponse,
javax.servlet.http.HttpSession
ServletConverter
bean   BeanConverter
object   ObjectConverter
hibernate2   H2BeanConverter
hibernate3   H3BeanConverter
url java.net.URL URLConverter
exception   ExceptionConverter
miniException java.lang.Throwable MinimalistExceptionConverter


它们是应用启动的时候,通过 org.directwebremoting.servlet.DwrServlet 初始化 dwr-2.0.5.jar!/org/directwebremoting/dwr.xml 文件加载进来的。例如:

<converter id="date" class="org.directwebremoting.convert.DateConverter"/> 注册了 date 转换器
<convert converter="date" match="java.util.Date"/> 应用注册的 date 转换器应用到 java.util.Date 类型

看到上面,你也许会惊讶一下,我们平时可能也就用下 bean 转换器,其他用内置就行。然而 DWR 确为我们考虑的很周到的,包括 hibernate 相关的,URL、Servlet、Dom 等相关类型的转换器。

2. DWR 如何确定用哪个 Converter?

DWR 是根据方法参数来确定入口参数的 Converter、根据返回值类型确定传向 JS 的出口参数的 Converter。总之是以 Java 方法原型为基准来决定每一参数或返回值各自用哪个 Converter 来转换数据。

在 BaseCallMarshaller.marshallInbound(HttpServletRequest request, HttpServletResponse response) 方法中,使用

Class paramType = method.getParameterTypes()[j] 来获得参数的类型,然后从已加载的 Converter Map 中找到 Converter 名称,进而确定 Converter 类名。

而确定返回值类型就不是直接用反射的 method.getReturnType()。而是以反射方式调用方法后,根据具体返回值的类型来确定的。见:

Replay DefaultRemoter.execute(Call) 方法中的
Object reply = chain.doFilter(object, method, call.getParameters()); 再进入到
Object ExecuteAjaxFilter.doFilter(Object obj, Method method, Object[] params Ajax FilterChain){
      return method.invoke(obj, params);
}

就是根据上面的返回值,然后在

DefaultConverterManager.convertOutbound(Object, OutboundContext) 方法中的

Converter converter = getConverter(object);  //根据返回值 object  确定该用的 Converter。

3. DWR Converter 的调用

多 留意下 DWR 自带的 Converter,可以看到所有的 Converter 直接或简接的 extends BaseV20Converter implements Converter,其实 BaseV20Converter(DWR 1.x 中对应为 BaseV10Converter) 本身就实现了 Converter。在 BaseV20Converter 抽象类中默认实现了 Converter 的方法

public void setConverterManager(ConverterManager config)  {  }

具体的 Converter 只要专心去实现接口 Converter 中的另两个方法:

Object convertInbound(Class paramType, InboundVariable data, InboundContext inctx) throws MarshallException;
OutboundVariable convertOutbound(Object data, OutboundContext outctx) throws MarshallException;

运行时,它们相应的被 ConvertManager(默认为 DefaultConvertManager) 的
Object convertInbound(Class paramType, InboundVariable iv, InboundContext inctx, TypeHintContext incc) throws MarshallException
OutboundVariable convertOutbound(Object object, OutboundContext outctx) throws MarshallException
来调用。

DWR 对每个参数或返回值至少会应用一次 Converter,但对于复杂的类型会递归的调用 Converter,比如,要完成

JS "{id:123, name: 'Unmi', blogs:['http://unmi.blogjava.net','http://blog.csdn.net/kypfos']}"  到 Java 的 Person{int id, String name, String[] blogs;} 的转换,就会使用到 bean->primitive->array 三个 Converter。

4. 定制自己的 Converter

基本上 DWR  内置的 Converter 就够用的,但也有可能需要定定自己的 Converter。从 DWR 的 Converter 实现来看,一般会用两种方式:

1) extends BaseV20Converter implements Converter,实现 Converter 的 converterInbound() 和 converterOutbound() 方法
2) extends BasicObjectConverter implements Converter,或继承 BeanConverter,实现 BasicObjectConverter 的 getPropertyMapFromObject(),getPropertyMapFromObject() 和 createTypeHintContext() 方法。

前一种方式,请参照 org.directwebremoting.convert.DateConverter 的源码实现:

convertInbound() 由 JS 的字符串转换成要求的 Date、Time、Timestamp 或 Calender 对像。
convertOubound() 把 Java 的类型转换成 JS 的 new Date() 类型,注意返回值的写法:
                             return new SimpleOutboundVariable("new Date(" + millis + ")", outctx, true);

第二种继承 BasicObjectConverter 或是 BeanConverter 的做法,可参考 BeanConverter  的源码实现。表现在 JSON 和 Java 对象间的转换,要是引入解析 JSON 的 JAR 包或许能有不少帮助。

定制 Converter 的内容讲的很少,主要是真有这方面的需要的时候请参考 DWR  的相关源码,实际中理解各个接口方法参数的意义,及返回值的要求。对待开源组件还是要保持阅读源码的好习惯。

好 啦,自己的 Converter 写好,需要注册,需要应用。我们还是参考 DWR 的做法,写在自己的 dwr.xml 中。例如定制了 com.unmi.dwr.converter.SpecialConverter,要对 com.unmi.model.SpecialObject 进行出入类型的转换,就这么写:

<converter id="special" class="com.unmi.dwr.converter.SpecialConverter"/> 注册了 special 转换器
<convert converter="special" match="com.unmi.model.SpecialObject"/> 应用注册的 special 转换器应用到 com.unmi.model.SpecialObject  类型

5. 小结

用 DWR 其实也有段时日了,未曾系统的学,总是遇一问题、扫除一个,不免也会去找找相关更系统的资料。然而着下此篇的动机是上周六在书城翻了下 《 DWR 实战》,它实际讲 DWR 本身的较少。最后我第一个想了解了是 DWR 能完成 JS 与 Java 间什么类型的转换,第一手的资料网上也没搜索到,于是进到源码中去,亲身历练,也更加深了印象。

读者也许和我一样目的,只想看看内置的转 换器有哪些,能转换哪些类型,那就只需看最为抢眼的那张表格吧。需要定制 Converter 应该很少,就像我们很少定制 Struts 的 Converter、Hibernate 的 UserType 和 iBatis 的 TypeHandler 一样。因此也就对定制 DWR 的 Converter 所用篇幅不多。

对待开源,自己总有个习惯就是必须有相关的源代码伴随在它身边。开源组件的使用一般不难,碰到问题,既然源码都掌握了,我想总能从源码中找出原因来。尚且,对这样的知名组件越发深入,就更能嚼出许多味多。


参考:DWR 2.0.5 的源代码,对 DWR 项目进行单步调试


【发表评论 0条】
发表评论
最新评论:已有0位感兴趣的读者发表了看法

验证码:请输入前面图中的四位验证码,字母不区分大小写


网站简介 | 广告服务 | VIP资费标准 | 银行汇款帐号 | 网站地图 | 帮助 | 联系方式
地址:成都八宝街一号万和苑C座1203 电话:028-86272612 传真: 028-86272612
开源人网站版权所有  渝ICP备06004507号  建议使用1024*768分辨率