最近开发一个后台应用,之前一般都是使用 AJAX 来进行数据交互。但是项目中使用的是 dwr 来进行前后端交互。本文不是讲如何使用 dwr,而是想分享一下使用 dwr 遇到的问题以及解决问题的思路。

1、什么是 dwr

DWR 是一个开源的类库,可以帮助开发人员开发包含AJAX技术的网站.它可以允许在浏览器里的代码(javascript)使用运行在 WEB服务器上的 JAVA 函数,就像它就在浏览器里一样.

它包含两个主要的部分:允许 JavaScript 从 WEB 服务器上一个遵循了 AJAX 原则的 Servlet (小应用程序)中获取数据.另外一方面一个 JavaScript 库可以帮助网站开发人员轻松地利用获取的数据来动态改变网页的内容.

dwr 采取了一个类似 AJAX 的新方法来动态生成基于 JAVA 类的 JavaScript 代码.这样 WEB 开发人员就可以在 JavaScript 里使用Java 代码就像它们是浏览器的本地代码(客户端代码)一样;但是 Java 代码运行在 WEB 服务器端而且可以自由访问 WEB 服务器的资源.出于安全的理由,WEB 开发者必须适当地配置哪些 Java 类可以安全的被外部使用.

2、遇到的问题

在使用 dwr 的时候,定义了一个接口用于前后端交互。接口的定义如下:

Object method(String code, Integer id)

但是在 js 调用的时候,如果我传入空值也就是'',最终调用后面method方法的时候 id 会被设置成 0。后面我又尝试设置成null,就会报如下错误:

但是我就想传到 method 方法的时候 id 的值是 null;

3、分析问题

因为上面有错误提示,我就猜这个异常是 dwr 框架报出的。所以我就 copy 出Format error converting 这几个关键字,然后通过 idea 进行全局搜索。如果根据关键字搜索到了:

这些信息都存在于 dwr jar 包里的 message.properties 里,它其实是 dwr 里面用于定义信息的模板文件,类似于 i18n . 里面涉及到的 BigNumberConverterDateConverterPrimitiveConverter这三个类都有一个共同点都是实现于 Converter。到了这里大概就有一个思路就了,就在 dwr 在进行类型转换的时候抛的异常。然后在这三个类里面的convertInbound进行点断点,发现出现的问题类是
PrimitiveConverter。它的处理逻辑如下:

if (paramType == Integer.TYPE || paramType == Integer.class)
{if (value.length() == 0){return new Integer(0);}return new Integer(value.trim());
}

然后看了一下 PrimitiveConverter#convertInbound 的调用链,看一下是从哪里获取到这个转换器的。

然后看了一下获取转换器的逻辑:

    private Converter getConverter(Class paramType){// Can we find a converter assignable to paramType in the HashMap?Converter converter = getConverterAssignableFrom(paramType);...}private Converter getConverterAssignableFrom(Class paramType){if (paramType == null){return null;}String lookup = paramType.getName();// Can we find the converter for paramType in the converters HashMap?Converter converter = (Converter) converters.get(lookup);if (converter != null){return converter;}}

它是根据类的类全名 (Integer 对应 java.lang.Integer) , 从DefaultConverterManager#converters属性中获取,converters 是一个 HashMap。因这个属性并没有初始化,所以我就猜测应该有方法来添加这个值。然后我就看了一下DefaultConverterManager的方法列表。

然后就看到了 addConverter 方法。

public void addConverter(String match, String type, Map params) {Class clazz = (Class) converterTypes.get(type);if (clazz == null){return;}Converter converter = (Converter) clazz.newInstance();converter.setConverterManager(this);for (Iterator it = params.entrySet().iterator(); it.hasNext();){Map.Entry entry = (Entry) it.next();String key = (String) entry.getKey();Object value = entry.getValue();try{LocalUtil.setProperty(converter, key, value);}catch (NoSuchMethodException ex){...}}// add the converter for the specified matchaddConverter(match, converter);
}public void addConverter(String match, Converter converter) {// Check that we don't have this one alreadyConverter other = (Converter) converters.get(match);if (other != null){log.warn("Clash of converters for " + match + ". Using " + converter.getClass().getName() + " in place of " + other.getClass().getName());}converters.put(match, converter);
}

首先会从 converterTypes 里面根据 type,拿到这个对应的 Class。然后以 match 为 key,转换器为 value 保存到 converters 用于参数转换的时候使用。converterTypes 其实就是是一个 Map,然后我在 addConverter 方法里面打了一个断点。查看了 converterTypes 以及converters这个属性里面的值:

下面是 converterTypes 属性的值:

下面是 converters 属性的值:

通过前面我们可以看到 java.lang.Integer 对应的转换器是 DefaultConverterManager#converterTypes Map 里面 primitive 为 key 的值 PrimitiveConverter。然后我看了一下```DefaultConverterManager#addConverterType`` 的调用链。


一共会有两个地方会到 DefaultConverterManager#addConverterType 方法。其实最终都是在 AbstractDWRServlet#init 进行资源加载。

首先 AbstractDWRServlet 是一个 Servlet,在 Servlet 初始化的时候会调用且仅会调用一次 Servlet#init方法。然后两次调用DefaultConverterManager#addConverterType都会从AbstractDWRServlet#init发起。

在分析这个配置文件之前我们先来看一下 DefaultConverterManager#addConverter 的调用链:

同样我们可以看到,它也是从 /uk/ltd/getahead/dwr/dwr.xml 以及 /WEB-INF/dwr.xml 配置文件里面读取数据加载的。下面我们就来分析一下 /uk/ltd/getahead/dwr/dwr.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd"><dwr><init><creator id="jsf" class="uk.ltd.getahead.dwr.create.JsfCreator"/><creator id="none" class="uk.ltd.getahead.dwr.create.NullCreator"/><creator id="new" class="uk.ltd.getahead.dwr.create.NewCreator"/><creator id="pageflow" class="uk.ltd.getahead.dwr.create.PageFlowCreator"/><creator id="spring" class="uk.ltd.getahead.dwr.create.SpringCreator"/><creator id="script" class="uk.ltd.getahead.dwr.create.ScriptedCreator"/><creator id="struts" class="uk.ltd.getahead.dwr.create.StrutsCreator"/><converter id="null" class="uk.ltd.getahead.dwr.convert.NullConverter"/><converter id="enum" class="uk.ltd.getahead.dwr.convert.EnumConverter"/><converter id="primitive" class="uk.ltd.getahead.dwr.convert.PrimitiveConverter"/><converter id="bignumber" class="uk.ltd.getahead.dwr.convert.BigNumberConverter"/><converter id="string" class="uk.ltd.getahead.dwr.convert.StringConverter"/><converter id="array" class="uk.ltd.getahead.dwr.convert.ArrayConverter"/><converter id="map" class="uk.ltd.getahead.dwr.convert.MapConverter"/><converter id="collection" class="uk.ltd.getahead.dwr.convert.CollectionConverter"/><converter id="date" class="uk.ltd.getahead.dwr.convert.DateConverter"/><converter id="dom" class="uk.ltd.getahead.dwr.convert.DOMConverter"/><converter id="dom4j" class="uk.ltd.getahead.dwr.convert.DOM4JConverter"/><converter id="jdom" class="uk.ltd.getahead.dwr.convert.JDOMConverter"/><converter id="xom" class="uk.ltd.getahead.dwr.convert.XOMConverter"/><converter id="servlet" class="uk.ltd.getahead.dwr.convert.ServletConverter"/><converter id="bean" class="uk.ltd.getahead.dwr.convert.BeanConverter"/><converter id="object" class="uk.ltd.getahead.dwr.convert.ObjectConverter"/><converter id="hibernate" class="uk.ltd.getahead.dwr.convert.HibernateBeanConverter"/></init><allow><convert converter="null" match="void"/><convert converter="null" match="java.lang.Void"/><convert converter="primitive" match="boolean"/><convert converter="primitive" match="byte"/><convert converter="primitive" match="short"/><convert converter="primitive" match="int"/><convert converter="primitive" match="long"/><convert converter="primitive" match="float"/><convert converter="primitive" match="double"/><convert converter="primitive" match="char"/><convert converter="primitive" match="java.lang.Boolean"/><convert converter="primitive" match="java.lang.Byte"/><convert converter="primitive" match="java.lang.Short"/><convert converter="primitive" match="java.lang.Integer"/><convert converter="primitive" match="java.lang.Long"/><convert converter="primitive" match="java.lang.Float"/><convert converter="primitive" match="java.lang.Double"/><convert converter="primitive" match="java.lang.Character"/><convert converter="bignumber" match="java.math.BigInteger"/><convert converter="bignumber" match="java.math.BigDecimal"/><convert converter="string" match="java.lang.String"/><convert converter="date" match="java.util.Date"/><convert converter="array" match="[Z"/><convert converter="array" match="[B"/><convert converter="array" match="[S"/><convert converter="array" match="[I"/><convert converter="array" match="[J"/><convert converter="array" match="[F"/><convert converter="array" match="[D"/><convert converter="array" match="[C"/><convert converter="array" match="[L*"/><!--The catch for the next 2 is that we really mean java.util.Collection<String>and java.util.Map<String, String> but we need to do more work before thissyntax is enabled--><convert converter="collection" match="java.util.Collection"/><convert converter="map" match="java.util.Map"/><convert converter="dom" match="org.w3c.dom.Node"/><convert converter="dom" match="org.w3c.dom.Element"/><convert converter="dom" match="org.w3c.dom.Document"/><convert converter="dom4j" match="org.dom4j.Document"/><convert converter="dom4j" match="org.dom4j.Element"/><convert converter="dom4j" match="org.dom4j.Node"/><convert converter="jdom" match="org.jdom.Document"/><convert converter="jdom" match="org.jdom.Element"/><convert converter="xom" match="nu.xom.Document"/><convert converter="xom" match="nu.xom.Element"/><convert converter="xom" match="nu.xom.Node"/><convert converter="servlet" match="javax.servlet.ServletConfig"/><convert converter="servlet" match="javax.servlet.ServletContext"/><convert converter="servlet" match="javax.servlet.http.HttpServletRequest"/><convert converter="servlet" match="javax.servlet.http.HttpServletResponse"/><convert converter="servlet" match="javax.servlet.http.HttpSession"/></allow></dwr>

init元素会初始化完成 DefaultConverterManager#converterTypes 属性的值 key 为initid,然后再解析allow 元素里面的convert子元素,通过converter为 key 去拿 DefaultConverterManager#converterTypes 里面的值,最终以 match 为 key,获取到的值也就是对应的转换器为值,初始化完成DefaultConverterManager#converters 属性的值。

然后 /WEB-INF/dwr.xml 里面的解析逻辑与上面的一样。

4、解决问题

通过上面的的分析,进行转换器添加的时候,也就是调用DefaultConverterManager#addConverter方法的时候,当遇到DefaultConverterManager#converters 已有参数转换器的时候 dwr 的逻辑是直接覆盖:

所以我们只需要在自定义的配置文件中定义好 Integer 的转换逻辑,然后在覆盖DefaultConverterManager#converters里面 java.lang.Integer 的转换器就行了。

定义一个java.lang.Integer 的转换器:

public class MyPrimitiveConverter extends PrimitiveConverter {@Overridepublic Object convertInbound(Class paramType, InboundVariable iv, InboundContext inctx) throws ConversionException {if(paramType == Integer.class || paramType == Integer.TYPE) {String value = iv.getValue();if("null".equals(value) || StringUtil.isBlank(value)) {return null;}}return super.convertInbound(paramType, iv, inctx);}
}

其实类很简单,就是把 PrimitiveConverter 的逻辑替换成上面的逻辑。

if (paramType == Integer.TYPE || paramType == Integer.class)
{if (value.length() == 0){return new Integer(0);}return new Integer(value.trim());
}

配置自定义解析文件/WEB-INF/dwr.xml

/WEB-INF/dwr.xml

<?xml version="1.0" encoding="UTF-8"?>
<dwr><init><converter id="primitive" class="com.weihui.basis.web.config.dwr.MyPrimitiveConverter" /></init></allow>...<convert match="java.lang.Integer" converter="primitive" /></allow>
</dwr>

然后我们再来看一下 DefaultConverterManager#converters里面 java.lang.Integer 对应的转换器:

ok ! 大功告成。希望这篇 blog 不仅是让你学会了替换 dwr 里面的参数转换器,还能够从我的解决问题的思考方式获得收获。

dwr 自定义转换器相关推荐

  1. java list 自定义类型转换_java – 可以从List生成平面XML结构的XStream自定义转换器?...

    我正在使用XStream,并且有一个类如下的字段: private Map data; 我想生成这样的XML输出: test data test data test data 所以我希望map键成为元 ...

  2. Flask自定义转换器,实现路由匹配正则表达式参数

    Flask框架动态路由实现参数传递和Django框架有类似之处,但是相比于Django框架,Flask实现复杂的参数就需要自己自定义转换器来实现了,而不能向Django那样直接使用正则表达式 1 # ...

  3. 【Flask】自定义转换器

    Flask中提供的转换器 DEFAULT_CONVERTERS = { 'default': UnicodeConverter, 'string': UnicodeConverter, 'any': ...

  4. java converter转换器_springboot 自定义转换器(converter)

    springboot 自定义转换器(converter) 应用:实现一对一转换,如将字符串转换为pojo.枚举等 *********************** 相关类与接口 Converter @F ...

  5. java dozer_java – Dozer双向映射(String,String)与自定义转换器不可能?

    我有一个自定义转换器的推土机映射: com.xyz.Customer com.xyz.CustomerDAO customerName customerName 和转换器: public class ...

  6. java学习笔记——springmvc 之 数据自定义转换器 数据格式化 JSR303数据校验返回与接收JSON(@RequestBody 和 @ResponseBody)

    九.数据绑定流程分析 1.提出问题 日期字符串格式的表单参数,提交后转换为Date类型 <!--    解决问题: 1.数据类型转换 2.数据格式 3.数据校验 --> BirthDay ...

  7. easyexcel的动态表头和自定义转换器

    https://www.yuque.com/easyexcel/doc/easyexcel 依赖 <dependency><groupId>com.alibaba</gr ...

  8. springMVC学习笔记(自定义转换器)

    springMVC 非常的灵活,它帮我们内置了转换器,比如我们从页面传到后台参数的都是String 类型的,但是我们可以这样接受: @PathVariable("Id") Inte ...

  9. fme创建自定义转换器

    创建自定义转换器 创建自定义转换器的最简单方法是从现有工作区创建,并选择经常重复使用的转换器或占用画布上大量空间的转换器. 可以借鉴B站视频:创建自定义转换器 操作流程 1.打开工作区. **2.选择 ...

最新文章

  1. linux 下清空tomcat catalina.out内容,释放磁盘空间
  2. WCF中使用HttpContext.Current的办法
  3. 计算高效,时序一致,超清还原!清华NYU 提出 RRN:视频超分新型递归网络
  4. 计算机无法检测电池损耗怎么办,笔记本电脑无法充电怎么办?笔记本电池损耗如何修复?...
  5. Redis 5.0新功能介绍
  6. An Introduction to Our Code Breaking Team
  7. 分布式数据库基础:分布式数据库故障
  8. 百度输入法、QQ 浏览器竟都在窃取用户隐私?
  9. 压测 broken pipe 之类
  10. 微信这项功能即将下线,赶快导出数据!
  11. 重磅!Intel联合OpenStack基金会发布Kata Container项目
  12. 计算机now函数,玩转NOW函数 日期时间随心变
  13. 模拟退火(SA)算法实例介绍(JAVA)
  14. pc游戏平台_如何提高您在PC游戏中的目标
  15. 研报笔记:光大证券多因子系列1-3
  16. 大数据与云计算之间的联系,一篇文章搞明白!
  17. kali Linux 2021 新版安装
  18. IDEA:如何彻底删除工程文件(转载)
  19. Python制作卡点视频
  20. [转]跨站漏洞解析及预防

热门文章

  1. P2P技术如何将实时视频直播带宽降低75%?
  2. 老毛桃WINPE U盘系统安装详解
  3. python怎么安装pandas-datareader_使用Python的pandas-datareader包下载雅虎财经股价数据...
  4. C# Winform的panel控件添加背景图片后窗体闪烁问题解决办法
  5. java 耦合度_代码的耦合度
  6. 网页计算器功能【案例】【两种实现】
  7. 获取短信数据 并且备份
  8. Mybatis中的#{}占位符
  9. 阿里云Link ID²加持云丁鹿客门锁 通过公安部一所安全测评
  10. flex-grow的用法