dwr cover converter

最近開發(fā)一個后臺應(yīng)用奕纫,之前一般都是使用 AJAX 來進(jìn)行數(shù)據(jù)交互。但是項目中使用的是 dwr 來進(jìn)行前后端交互辞做。本文不是講如何使用 dwr莺掠,而是想分享一下使用 dwr 遇到的問題以及解決問題的思路露懒。

1闯冷、什么是 dwr

DWR 是一個開源的類庫,可以幫助開發(fā)人員開發(fā)包含AJAX技術(shù)的網(wǎng)站.它可以允許在瀏覽器里的代碼(javascript)使用運(yùn)行在 WEB服務(wù)器上的 JAVA 函數(shù),就像它就在瀏覽器里一樣.

它包含兩個主要的部分:允許 JavaScript 從 WEB 服務(wù)器上一個遵循了 AJAX 原則的 Servlet (小應(yīng)用程序)中獲取數(shù)據(jù).另外一方面一個 JavaScript 庫可以幫助網(wǎng)站開發(fā)人員輕松地利用獲取的數(shù)據(jù)來動態(tài)改變網(wǎng)頁的內(nèi)容.

dwr 采取了一個類似 AJAX 的新方法來動態(tài)生成基于 JAVA 類的 JavaScript 代碼.這樣 WEB 開發(fā)人員就可以在 JavaScript 里使用Java 代碼就像它們是瀏覽器的本地代碼(客戶端代碼)一樣;但是 Java 代碼運(yùn)行在 WEB 服務(wù)器端而且可以自由訪問 WEB 服務(wù)器的資源.出于安全的理由,WEB 開發(fā)者必須適當(dāng)?shù)嘏渲媚男?Java 類可以安全的被外部使用.

2、遇到的問題

在使用 dwr 的時候懈词,定義了一個接口用于前后端交互蛇耀。接口的定義如下:

Object method(String code, Integer id)

但是在 js 調(diào)用的時候,如果我傳入空值也就是''坎弯,最終調(diào)用后面method方法的時候 id 會被設(shè)置成 0蒂窒。后面我又嘗試設(shè)置成null,就會報如下錯誤:

dwr-error.png

但是我就想傳到 method 方法的時候 id 的值是 null;

3荞怒、分析問題

因?yàn)樯厦嬗绣e誤提示,我就猜這個異常是 dwr 框架報出的秧秉。所以我就 copy 出Format error converting 這幾個關(guān)鍵字褐桌,然后通過 idea 進(jìn)行全局搜索。如果根據(jù)關(guān)鍵字搜索到了:

dwr.png

這些信息都存在于 dwr jar 包里的 message.properties 里象迎,它其實(shí)是 dwr 里面用于定義信息的模板文件荧嵌,類似于 i18n . 里面涉及到的 BigNumberConverter呛踊、DateConverterPrimitiveConverter這三個類都有一個共同點(diǎn)都是實(shí)現(xiàn)于 Converter啦撮。到了這里大概就有一個思路就了谭网,就在 dwr 在進(jìn)行類型轉(zhuǎn)換的時候拋的異常。然后在這三個類里面的convertInbound進(jìn)行點(diǎn)斷點(diǎn)赃春,發(fā)現(xiàn)出現(xiàn)的問題類是
PrimitiveConverter愉择。它的處理邏輯如下:

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

然后看了一下 PrimitiveConverter#convertInbound 的調(diào)用鏈,看一下是從哪里獲取到這個轉(zhuǎn)換器的织中。

converter-manager.png

然后看了一下獲取轉(zhuǎn)換器的邏輯:

    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;
        }
    }

它是根據(jù)類的類全名 (Integer 對應(yīng) java.lang.Integer) , 從DefaultConverterManager#converters屬性中獲取锥涕,converters 是一個 HashMap。因這個屬性并沒有初始化狭吼,所以我就猜測應(yīng)該有方法來添加這個值层坠。然后我就看了一下DefaultConverterManager的方法列表。

converter-add.png

然后就看到了 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 match
    addConverter(match, converter);
}

public void addConverter(String match, Converter converter) {
    // Check that we don't have this one already
    Converter 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 里面根據(jù) type破花,拿到這個對應(yīng)的 Class。然后以 match 為 key疲吸,轉(zhuǎn)換器為 value 保存到 converters 用于參數(shù)轉(zhuǎn)換的時候使用座每。converterTypes 其實(shí)就是是一個 Map,然后我在 addConverter 方法里面打了一個斷點(diǎn)磅氨。查看了 converterTypes 以及converters這個屬性里面的值:

下面是 converterTypes 屬性的值:

converter-types.png

下面是 converters 屬性的值:

converters.png

通過前面我們可以看到 java.lang.Integer 對應(yīng)的轉(zhuǎn)換器是 DefaultConverterManager#converterTypes Map 里面 primitive 為 key 的值 PrimitiveConverter尺栖。然后我看了一下```DefaultConverterManager#addConverterType`` 的調(diào)用鏈。

init.png

一共會有兩個地方會到 DefaultConverterManager#addConverterType 方法烦租。其實(shí)最終都是在 AbstractDWRServlet#init 進(jìn)行資源加載延赌。

init.png

首先 AbstractDWRServlet 是一個 Servlet,在 Servlet 初始化的時候會調(diào)用且僅會調(diào)用一次 Servlet#init方法叉橱。然后兩次調(diào)用DefaultConverterManager#addConverterType都會從AbstractDWRServlet#init發(fā)起挫以。

在分析這個配置文件之前我們先來看一下 DefaultConverterManager#addConverter 的調(diào)用鏈:

init.png

同樣我們可以看到,它也是從 /uk/ltd/getahead/dwr/dwr.xml 以及 /WEB-INF/dwr.xml 配置文件里面讀取數(shù)據(jù)加載的窃祝。下面我們就來分析一下 /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 this
    syntax 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,獲取到的值也就是對應(yīng)的轉(zhuǎn)換器為值探膊,初始化完成DefaultConverterManager#converters 屬性的值杠愧。

然后 /WEB-INF/dwr.xml 里面的解析邏輯與上面的一樣。

4逞壁、解決問題

通過上面的的分析流济,進(jìn)行轉(zhuǎn)換器添加的時候锐锣,也就是調(diào)用DefaultConverterManager#addConverter方法的時候,當(dāng)遇到DefaultConverterManager#converters 已有參數(shù)轉(zhuǎn)換器的時候 dwr 的邏輯是直接覆蓋:

xxx.png

所以我們只需要在自定義的配置文件中定義好 Integer 的轉(zhuǎn)換邏輯绳瘟,然后在覆蓋DefaultConverterManager#converters里面 java.lang.Integer 的轉(zhuǎn)換器就行了雕憔。

定義一個java.lang.Integer 的轉(zhuǎn)換器:

public class MyPrimitiveConverter extends PrimitiveConverter {

    @Override
    public 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);
    }
}

其實(shí)類很簡單,就是把 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 對應(yīng)的轉(zhuǎn)換器:

xxx.png

ok ! 大功告成斤彼。希望這篇 blog 不僅是讓你學(xué)會了替換 dwr 里面的參數(shù)轉(zhuǎn)換器,還能夠從我的解決問題的思考方式獲得收獲姨丈。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末畅卓,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蟋恬,更是在濱河造成了極大的恐慌翁潘,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件歼争,死亡現(xiàn)場離奇詭異拜马,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)沐绒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門俩莽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人乔遮,你說我怎么就攤上這事扮超。” “怎么了蹋肮?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵出刷,是天一觀的道長。 經(jīng)常有香客問我坯辩,道長馁龟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任漆魔,我火速辦了婚禮坷檩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘改抡。我一直安慰自己矢炼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布阿纤。 她就那樣靜靜地躺著裸删,像睡著了一般。 火紅的嫁衣襯著肌膚如雪阵赠。 梳的紋絲不亂的頭發(fā)上涯塔,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機(jī)與錄音清蚀,去河邊找鬼匕荸。 笑死,一個胖子當(dāng)著我的面吹牛枷邪,可吹牛的內(nèi)容都是我干的榛搔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼东揣,長吁一口氣:“原來是場噩夢啊……” “哼践惑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嘶卧,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤尔觉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后芥吟,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侦铜,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年钟鸵,在試婚紗的時候發(fā)現(xiàn)自己被綠了钉稍。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡棺耍,死狀恐怖贡未,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蒙袍,我是刑警寧澤俊卤,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站左敌,受9級特大地震影響瘾蛋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜矫限,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一哺哼、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叼风,春花似錦取董、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至孽鸡,卻和暖如春蹂午,著一層夾襖步出監(jiān)牢的瞬間栏豺,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工豆胸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奥洼,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓晚胡,卻偏偏與公主長得像灵奖,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子估盘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理瓷患,服務(wù)發(fā)現(xiàn),斷路器遣妥,智...
    卡卡羅2017閱讀 134,656評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法擅编,類相關(guān)的語法,內(nèi)部類的語法燥透,繼承相關(guān)的語法沙咏,異常的語法,線程的語...
    子非魚_t_閱讀 31,631評論 18 399
  • 1. 簡介 1.1 什么是 MyBatis 班套? MyBatis 是支持定制化 SQL肢藐、存儲過程以及高級映射的優(yōu)秀的...
    笨鳥慢飛閱讀 5,520評論 0 4
  • 讀《花田半畝》有感 倘若,這世上從來沒有我吱韭, 那么又有什么遺憾吆豹,什么悲傷, 生命是跌撞的曲折理盆,死亡是寧靜的星痘煤。 歸...
    尋租空間閱讀 279評論 0 0
  • 接入 接入準(zhǔn)備 參考http://baichuan.taobao.com/docs/doc.htm?spm=a3c...
    reezy閱讀 29,341評論 6 14