Struts2 第五講 -- Struts2與Servlet的API解耦
為了避免與 Servlet API 耦合在一起, 方便 Action 做單元測試, Struts2 對 HttpServletRequest, HttpSession 和 ServletContext 進行了封裝, 構造了 3 個 Map 對象來替代這 3 個對象, 在 Action 中可以直接使用 HttpServletRequest, HttpSession, ServletContext 對應的 Map 對象來保存和讀取數據。這裏大家註意,struts1是沒有提供與Servlet的API解耦的。
1.Struts2如何獲取request、response、session、ServletContext
在Struts2開發中,除了將請求參數自動設置到Action的字段中,我們往往也需要在Action裏直接獲取請求(Request)或會話(Session)的一些信息,甚至需要直接對JavaServlet Http的請求(HttpServletRequest),響應(HttpServletResponse)操作.Struts2提供了三種用於獲取這些對象的方法,下面來一一介紹一下;
非IOC方法:通過ActionContext,ServletActionContext類直接獲取,實現與Servlet解耦
- l ActionContext是 Action 執行的上下文對象, 在 ActionContext
static ThreadLocal actionContext = new ActionContextThreadLocal()
(ActionContext) actionContext.get();來獲取
- l 獲取 HttpServletRequest 對應的 Map 對象:public Object get(Object key): ActionContext 類中沒有提供類似 getRequest() 這樣的方法來獲取 HttpServletRequest
ActionContext.get(org.apache.struts2.StrutsStatics.HTTP_REQUEST);
- 獲取HTTPServletResponse,該獲取同request相似;
ActionContext.get(org.apache.struts2.StrutsStatics.HTTP_RESPONSE);
- l 獲取 HttpSession 對應的 Map 對象:
public Map getSession()
- l 獲取 ServletContext 對應的 Map 對象:
public Map getApplication()
測試代碼:獲取request、response、session、application對象
@SuppressWarnings("serial") public class ContextAction extends ActionSupport{ @Override public String execute() throws Exception { System.out.println("歡迎訪問ContextAction中的execute方法!"); /**request對象(與servletAPI解耦的方式)*//* ActionContext.getContext().put("username", "request_username"); *//**session對象(與servletAPI解耦的方式)*//* ActionContext.getContext().getSession().put("username", "session_username"); *//**application對象(ServletContext對象)(與servletAPI解耦的方式)*//* ActionContext.getContext().getApplication().put("username", "application_username"); */ //使用與ServletActionContext的方式操作上述
ServletActionContext.getRequest().setAttribute("username", "request_username");
ServletActionContext.getServletContext().setAttribute("username", "application_username");
ServletActionContext.getRequest().getSession().setAttribute("username", "session_username"); System.out.println("request:"+ServletActionContext.getRequest()); System.out.println("response:"+ServletActionContext.getResponse()); System.out.println("session:"+ServletActionContext.getRequest().getSession()); System.out.println("servletContext:"+ServletActionContext.getServletContext()); //Action接口中常量 SUCCESS="success" return SUCCESS; } }
IOC方法:實現指定接口,由struts框架運行時註入,即使用Struts2 Aware攔截器
Action 類通過可以實現某些特定的接口, 讓Struts2 框架在運行時向 Action 實例註入 parameters, request, session 和 application 對應的 Map 對象:
測試代碼::獲取request、response、session、application對象
@SuppressWarnings("serial") public class ContextActionTwo extends ActionSupport implements ServletRequestAware, ServletContextAware,ServletResponseAware,SessionAware{ private HttpServletRequest request; private HttpServletResponse response; private Map<String, Object> session; private ServletContext application;//註意可以實現接口ApplicationAware同樣的功能 //使用第二種方式 @Override public String execute() throws Exception { System.out.println("歡迎訪問ContextActionTwo中的execute()!"); //向作用域中存值 request.setAttribute("username", "request_username2"); session.put("username", "session_username2"); application.setAttribute("username", "application_username2"); return SUCCESS; } @Override public void setSession(Map<String, Object> session) { // TODO Auto-generated method stub this.session = session; } @Override public void setServletResponse(HttpServletResponse response) { // TODO Auto-generated method stub this.response = response; } @Override public void setServletContext(ServletContext context) { // TODO Auto-generated method stub this.application = context; } @Override public void setServletRequest(HttpServletRequest request) { // TODO Auto-generated method stub this.request = request; } }
attr.jsp
<body>123 ${requestScope.username}<br> ${sessionScope.username}<br> ${applicationScope.username}<br> </body>
struts-context.xml文件的配置:
<struts> <package name="context" namespace="/context" extends="struts-default"> <default-action-ref name="contextAction_test"></default-action-ref> <action name="contextAction_test" class="cn.youric.you.two_context.ContextAction"> <result name="success">/context/success.jsp</result> </action> <action name="contextAction02_test" class="cn.youric.you.two_context.ContextActionTwo"> <result name="success">/context/attr.jsp</result> </action> </package> </struts>
別忘了include;
2.聊聊ActionContext
ActionContext(com.opensymphony.xwork.ActionContext)是Action執行時的上下文,上下文可以看作是一個容器(其實我們這裏的容器就是一個Map而已),它存放的是Action在執行時需要用到的對象. 一般情況, 我們的ActionContext都是通過: ActionContext context = (ActionContext) actionContext.get();來獲取的.我們再來看看這裏的actionContext對象的創建:
static ThreadLocal<ActionContext> actionContext = new ThreadLocal<>();
ThreadLocal可以命名為"線程局部變量",它為每一個使用該變量的線程都提供一個變量值的副本,使每一個線程都可以獨立地改變自己的副本,而不會和其它線程的副本沖突.這樣,我們ActionContext裏的屬性只會在對應的當前請求線程中可見,從而保證它是線程安全的.
3. ServletActionContext和ActionContext聯系
ServletActionContext和ActionContext有著一些重復的功能,在我們的Action中,該如何去抉擇呢?我們遵循的原則是:如果ActionContext能夠實現我們的功能,那最好就不要使用ServletActionContext,讓我們的Action盡量不要直接去訪問Servlet的相關對象.
註意:在使用ActionContext時有一點要註意: 不要在Action的構造函數裏使用ActionContext.getContext(),因為這個時候ActionContext裏的一些值也許沒有設置,這時通過ActionContext取得的值也許是null;同樣,HttpServletRequest req = ServletActionContext.getRequest()也不要放在構造函數中,也不要直接將req作為類變量給其賦值。至於原因,我想是因為前面講到的static ThreadLocal actionContext = new ActionContextThreadLocal(),從這裏我們可以看出ActionContext是線程安全的,而ServletActionContext繼承自ActionContext,所以ServletActionContext也線程安全,線程安全要求每個線程都獨立進行,所以req的創建也要求獨立進行,所以ServletActionContext.getRequest()這句話不要放在構造函數中,也不要直接放在類中,而應該放在每個具體的方法體中(eg:login()、queryAll()、insert()等),這樣才能保證每次產生對象時獨立的建立了一個req。
Struts2 第五講 -- Struts2與Servlet的API解耦