難經3:Struts2,攔截器攔不住Result?

[問題]

使用Struts2作爲web框架,知道它的攔截器(Interceptor)機制,類似與Filter和Spring的AOP,於是實現了一個爲Action增加自定義前置(before)動作和後置動作(after)的攔截器(曰:WInterceptor),不過用一段時間發現,在WInterceptor的after中,對Action對象的屬性修改在頁面看不到,對請求對象的屬性設置也無效。爲什麼在調用了Action之後(invokeAction())之後,request就不能使用了呢,攔截器不能改變Action的Result麼?

 

[探幽]

 

在重看了Struts2的攔截器的官方文檔以後,還是不明白上面的問題是爲什麼。地球人都知道,Struts2其實就是Webwork2,而攔截器的核心實現在XWork,利用XWork的攔截器框架,Struts2在外圍通過線程上下文,綁定了Request和Response對象的包裝類,哪問題到底在Struts2,還是在XWork?

 

在看到下面這張調用圖,我才突然反應過來,“我真笨,真的,我只知道攔截器調用棧的最底層,是Action方法的調用,卻不知道Result的調用也是在棧底調用,之後才返回給上一個攔截器,層層退出”:



        感謝這張圖的作者,它簡單,但有效。

 

 問題的關鍵在於,在調用actionInvocation.invoke()的之後,不僅執行類Action,也執行類Result。因而,等退回到攔截器的調用代碼時,Result已經生成,View已經確定,這時你再修改模型(Action的屬性)或請求對象的屬性,對視圖不會有任何影響。

 

另,爲什麼Result的執行不放到攔截器鏈的外面呢?這是我開始的直覺,有知道的朋友煩告知一聲。

[解難]

 方法一:使用現成的PreResultListener監聽器事件

搞清楚原因,捲起袖子幹吧,只要讓WInterpretor的after事件,放在Result的生成之前就行了。

看看XWork的攔截器接口注入的actionInvocation,其實就提供增加Result執行的前置監聽事件-PreResultListener:

    /**
     * Register a {@link PreResultListener} to be notified after the Action is executed and
     * before the Result is executed.
     * <p/>
     * The ActionInvocation implementation must guarantee that listeners will be called in
     * the order in which they are registered.
     * <p/>
     * Listener registration and execution does not need to be thread-safe.
     *
     * @param listener the listener to add.
     */
    void addPreResultListener(PreResultListener listener);

 

因此,讓攔截器實現這個接口,就可以自然實現Action執行after事件了。

 

 

方法二,實現自己的 ActionInvocation ,手動分離Action和Result的執行

本來前面的方法已經很好了,可是,可是啊,在addPreResultListener裏的異常,不會被Struts的框架捕獲,而且,addPreResultListener接口不能傳遞自己的上下文參數,難道動用ThreadLocal傳參?

 

研究了一下XWork的ActionInvocation 接口默認實現類DefaultActionInvocation, 寫了一個包裝類,將Action的執行和Result的生成完全分開,或許有人用的着,放上來,見附件(ActionInvocationWrapper),如有不妥之處請告知。

 

exeucteAction是執行Action,executeResult是執行Result

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章