【struts2】ActionContext與ServletActionContext


  1 再探ActionContext

  我們知道,ActionContext是Action執行時的上下文,里面存放着Action在執行時需要用到的對象,也稱之為廣義值棧。Struts2在每次執行Action之前都會創建新的ActionContext,在同一個線程里ActionContext里面的屬性是唯一的,這樣Action就可以在多線程中使用。

  1.1 ActionContext的線程安全性

  那么Struts2是如何保證ActionContext的線程安全性呢?看看ActionContext對象的代碼,示例如下:

public class ActionContext implements Serializable {  
  static ThreadLocal actionContext = new ThreadLocal();  
  ……  
}  

  ThreadLocal又稱為“線程局部變量”,它為每一個使用該變量的線程都提供一個變量值的副本,使每一個線程都可以獨立地改變自己的副本,而不會和其它線程的副本沖突。存放在ActionContext里的數據都存放在這個ThreadLocal的屬性中,而這個屬性只會在對應的當前請求線程中可見,從而保證數據是線程安全的。

  1.2 訪問的是Map

  回顧前面在使用ActionContext來訪問Session中數據的程序,你會發現,其實在程序里面訪問的是一個Map,而非HttpSession對象,這是為什么呢?

  原來,Struts2框架將與Web相關的很多對象重新進行了包裝,比如將HttpSession對象重新包裝成了一個Map對象,里面存放着Session中的數據,提供這個Map給Action使用,而不用Action直接和底層的HttpSession打交道。也正是因為框架的包裝,讓Action可以完全的和Web層解耦。但是要注意一點,ActionContext不能在普通的Java應用程序中使用。我們知道Action和Servlet API是解耦的,因此可以在Java應用程序中調用Action的execute方法來進行測試。但是如果使用了ActionContext來獲取session數據,那么就不能這樣運行了。因為ActionContext包裝的都是Web的數據,在Java應用程序中運行的時候,沒有Web的環境和響應的數據,因而會拋出空指針的異常。訪問其它的Web對象的值也是與此類似的,你通過ActionContext去訪問的都是包裝后的Map。

  1.3 使用SessionAware接口

  Struts2還提供另外一種簡單的方式,使用SessionAware接口來訪問存儲於ActionContext中的數據,該接口通過使用IoC/DI來為Action注入Session Map,就可以在程序里面直接使用這個Map來操作數據了。

  (1)在Action中不再需要訪問ActionContext了,取而代之,Action實現SessionAware接口,該接口告知Struts2在Action執行之前要設置Session Map,是通過servletConfig 攔截器來實現的,這個攔截器在defaultStack里面就有。示例代碼如下:

public class OgnlAction extends ActionSupport implements SessionAware{  
    private Map<String, Object> session;  
    public void setSession(Map<String, Object> session) {  
        this.session = session;  
    }  
      
    public String execute(){  
        session.put("sessionTestKey", "測試SessionAware");  
        return this.SUCCESS;  
    }  
}  

  在上面的代碼中:

  • Action類實現SessionAware接口
  • 這個接口要求Action類實現一個方法setSession(Map<String, Object> session),通過這個方法注入Session的數據
  • 在execute方法中,通過這個私有屬性就可以操作會話中的數據,注意一點,這個Map中的值也是與HttpSession聯動的。

  (2)我們可以再前台獲取session中的屬性,以便看出Action操作session后的效果,示例如下:

<%@ taglib prefix="s" uri="/struts-tags"%>  
會話中的值:<s:property value="#session['sessionTestKey']"/>  
<br>  
通過Servlet的Api獲取會話中的值:<%=session.getAttribute("sessionTestKey") %>  

  為了能夠在普通的Java應用中運行並測試Action,推薦大家使用SessionAware的方式來訪問HttpSession。因為這樣一來,在通過main方法運行或測試的時候,可以直接調用setSession方法,傳入模擬的會話數據,就不會出現execute方法中拋出空指針的異常了。因此,推薦大家使用SessionAware的方式來訪問HttpSession。

  1.4 使用其它包裝接口

  跟SessionAware類似,你可以使用RequestAware來獲取包裝請求對象的attribute中的值的Map;使用ApplicationAware來獲取包裝ServletContext對象的attribute中的值的Map;使用ParameterAware來獲取包裝請求對象的參數中的值的Map,等等,這里只羅列這幾個常見和常用的,還有更多的請參見Struts2的API文檔。

  ServletActionContext

  在實際應用開發中,光是獲取數據就夠了嗎?答案顯然是否定的,有些時候,根據功能需要,在Action中必須要能獲取到Servlet相關的API,比如要操作Cookie。這個時候,就需要用ServletActionContext了。

  2.1 ServletActionContext概述

  這個類直接繼承了ActionContext,當然也繼承了它父類的很多功能,比如:對OgnlValueStack、Action名字等的訪問。更重要的是,它還提供了直接訪問Servlet的相關對象的功能,它可以取得的對象有:

  • HttpServletRequest:請求對象
  • HttpServletResponse:響應對象
  • ServletContext:Servlet上下文信息
  • PageContext:Http頁面上下文

  2.2 基本使用

HttpServletRequest request = ServletActionContext.getRequest();  
HttpServletResponse response = ServletActionContext.getResponse();  
ServletContext servletContext = ServletActionContext.getServletContext();  
PageContext pageContext = ServletActionContext.getPageContext();  
HttpSession session = ServletActionContext.getRequest().getSession();  

  這里要注意的是HttpSession對象的獲取,是在取得HttpRequest對象過后,通過HttpRequest對象來獲取會話對象。當然,取得相應的對象后,就直接使用這些對象的方法來進行開發,這里就不去贅述了。

  2.3 通過IoC/DI的方式來獲取相應的Servlet對象

  還可以通過IoC/DI的方式來獲取相應的Servlet對象,對應關系是:

  • ServletRequestAware:通過這個接口來獲取HttpServletRequest對象
  • ServletResponseAware:通過這個接口來獲取HttpServletResponse對象

  用ServletRequestAware來示例一下。

  (1)修改Action,讓其實現ServletRequestAware接口,示例代碼如下:

public class OgnlAction extends ActionSupport implements ServletRequestAware{  
    private HttpServletRequest request = null;    
    public void setServletRequest(HttpServletRequest request) {  
        this.request = request;  
    }  
      
    public String execute(){          
        request.setAttribute("request", "Request的屬性值");  
        request.getSession().setAttribute("sessionTestKey", "測試SessionAware");  
        return this.SUCCESS;  
    }     
}  

  (2)對應的結果頁面也需要稍作修改,要把Action中設置的值顯示出來,示例如下:

<%@ taglib prefix="s" uri="/struts-tags"%>  
Request的屬性值:<s:property value="#request['request']"/>  
<br>  
會話的屬性值:<s:property value="#session['sessionTestKey']"/>  

  當然,你也可以以同樣的方式去使用ServletResponseAware,這里就不去贅述了。

  ActionContextServletActionContext

  根據前面的講述,你會發現,ActionContext和ServletActionContext有着一些重復的功能,都能夠獲取到Web對象的數據,但是又有些不同。通常情況下,可以這么認為:ActionContext主要負責值的操作;ServletActionContext主要負責獲取Servlet對象。那么在Action中,該如何去抉擇呢?建議的原則是:

  • 優先使用ActionContext
  • 只有ActionContext不能滿足功能要求的時候,才使用ServletActionContext

  總之,要盡量讓Action與Web無關,這對於Action的測試和復用都是極其有好處的。另外還有一點需要注意:在使用ActionContext時,不要在Action的構造函數里使用ActionContext.getContext(),因為這個時候ActionContext里的一些值也許還沒有設置,這時通過ActionContext取得的值也許是null。

  參考資料:http://www.iteye.com/topic/1124526


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM