版本:
Struts 2.0.14
問題描述:
當開放 Struts2 的 Filter 能服務 INCLUDE 轉來的需求時,即
<filter>
<filter-name>struts</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
此時,若在 JSP 的頁面上有任何與 include 相關的運作,如:<jsp:include>、<s:include> 甚至是 <tiles:insertAttribute>,則在之後若再使用任何一種 Struts2 的 Tag 時,都會導致ComponentTagSupport 類別內的 doStartTag() 方法中,取得 Container 的程式碼 (Dispatcher.getInstance().getContainer()) 丟出 NullPointerException。
問題追蹤:
會讓 ComponentTagSupport 類別丟出 NullPointerException 的主因在 Dispatcher.getInstance() 時取出的物件為 null,而再進一步追查,發現 FilterDispatcher 的 doFilter() 方法的最後,一定會去執行 ActionContextCleanUp.cleanUp(req),而這個方法正是會將 Dispatcher 的 instance 清成 null,進而導致在 include 之後的 struts2 tag 會發生 NullPointerException 的問題。
但仔細去看 ActionContextCleanUp.cleanUp() 方法,發現它有個例外機制可以避免將 Dispatcher 的 instance 清空,而啟動這個例外機制的方式,是將 ActionContextCleanUp 也加到 Filter 的行列,並且必需要加在 Struts2 的 Filter 之前。即:
<filter>
<filter-name>ActionContextCleanUp</filter-name>
<filter-class>org.apache.struts2.dispatcher.ActionContextCleanUp</filter-class>
</filter>
<filter-mapping>
<filter-name>ActionContextCleanUp</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>struts</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
後續的觀察:
設定好之後使用 eclipse 的 debug 機制去觀察運作的結果,發現當 struts2 的 filter 被 <jsp:include> 觸發,在 doFilter() 最後呼叫 ActionContextCleanUp.cleanUp() 時,ActionContextCleanUp 的避免清除機制有被啟動,所以 Dispatcher 的 instance 沒被清成 null,因此在 <jsp:include> 之後的 struts2 tag 也不會發生 NullPointerException 的錯誤。
即使在同一個 jsp 頁面上有多個 <jsp:include> 時也都會如此運作,但請放心的是,當整個 request 結束時 (也可以說主要的這個 action -> jsp 運作結束時),Dispatcher 的 instance 以及ActionContext 的 context 都確定能被清成 null。