Struts2 訪問request、session和application對象
在 傳統的Web開發中,經常會用到Servlet API中的HttpServletRequest、HttpSession和ServletContext。Struts 2框架讓我們可以直接訪問和設置action及模型對象的數據,這降低了對HttpServletRequest對象的使用需求,但在某些應用中,我們可 能會需要在action中去訪問HttpServletRequest對象以及其他兩種對象,例如,用戶登錄成功後,我們應該將用戶信息保存到 Session中。
Struts 2提供了多種方式來訪問上述的三種對象,歸結起來,可以劃分爲兩大類:與Servlet API解耦的訪問方式和與Servlet API耦合的訪問方式。
與Servlet API解耦的訪問方式
爲了避免與Servlet API耦合在一起,方便Action類做單元測試,Struts 2對HttpServletRequest、HttpSession和ServletContext進行了封裝,構造了三個Map對象來替代這三種對象, 在Action中,直接使用HttpServletRequest、HttpSession和ServletContext對應的Map對象來保存和讀取 數據。
要獲取這三個Map對象,可以使用com.opensymphony.xwork2.ActionContext類(參看第
4
章
4.2節)。
ActionContext是action執行的上 下文,在ActionContext中保存了action執行所需的一組對象,包括parameters、request、session、 application和locale等。ActionContext類定義瞭如下方法,用於獲取HttpServletRequest、 HttpSession和ServletContext對應的Map對象。
Ø public Object get(Object key)
ActionContext類沒有提供類似getRequest()這樣的方法來獲取封裝了HttpServletRequest的Map對象。要得到請求Map對象,你需要爲get()方法傳遞參數“request”。
Ø public Map getSession()
獲取封裝了HttpSession的Map對象。
Ø public Map getApplication()
獲取封裝了ServletContext的Map對象。
我們看例3-11。
例3-11 通過ActionContext來獲取request、session和application對象的LoginAction1
package org.sunxin.struts2.ch03.action.way1;
import java.util.Map;
import org.sunxin.struts2.ch03.model.User;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionContext;
public class LoginAction1 implements Action
{
private User user;
public User getUser()
{
return user;
}
public void setUser(User user)
{
this.user = user;
}
@SuppressWarnings("unchecked")
@Override
public String execute() throws Exception
{
if("zhangsan".equals(user.getUsername())
&& "1234".equals(user.
getPassword()))
{
ActionContext context = ActionContext.getContext();
Map request = (Map)context.get("request");
Map session = context.getSession();
Map application = context.getApplication();
//在請求中放置歡迎信息。
request.put("greeting", "歡迎您來到程序員之家");
//在session中保存user對象
session.put("user", user);
//統計用戶訪問量,在application中保存用戶訪問量數據
Integer count = (Integer)application.get("counter");
if(null == count)
count=1;
else
count++;
application.put("counter", count);
return SUCCESS;
}
else
{
return ERROR;
}
}
}
在成功頁面中,可以使用JSP內置的表達式語言來訪問request、session和application範圍的數據,代碼如例3-12所示。
例3-12 success.jsp
<%@ page contentType="text/html;charset=GBK" %>
<html>
<head><title>歡迎頁面</title></head>
<body>
<h3>${sessionScope.user.username},${requestScope.greeting}。<br>
本站的訪問量是:${applicationScope.counter}</h3>
</body>
</html>
如果頁面能夠正常顯示我們保存在request、session和 application對象中的數據(當然是能夠正常顯示的,要不然筆者也不會給出這個例子了。☺),就說明在Action中,對 ActionContext返回的Map對象的操作,等價於對Servlet API中的HttpServletRequest、HttpSession和ServletContext對象的操作。
利用請求對象來傳遞數據還有一種方式,你可以直接使用ActionContex類的put()方法將數據保存到ActionContext中,如下:
ActionContext.getContext().put("greeting", "歡迎您來到http://www. sunxin.org");
然後在結果頁面中,從請求對象中取出greeting屬性,如下:
${requestScope.greeting} 或者 <%=request.getAttribute("greeting")%>
ActionContext 中保存的數據能夠從請求對象中得到,這讓人太不可思議了。其中的奧妙就在於Struts 2中的org.apache.struts2.dispatcher.StrutsRequestWrapper類,這個類是 HttpServletRequest的包裝類,它重寫了getAttribute()方法(在頁面中獲取request對象的屬性就要調用這個方法), 在這個方法中,它首先在請求對象中查找屬性,如果沒有找到(如果你在ActionContext中保存數據,當然就找不到了),則到 ActionContext中去查找。這就是爲什麼在ActionContext中保存的數據能夠從請求對象中得到的原因。
當然具體的實現還有很多細節,感興趣的讀者可以跟蹤一下Struts 2的源代碼。
除了利用ActionContext來獲取request、 session和application對象這種方式外,Action類還可以實現某些特定的接口,讓Struts 2框架在運行時向Action實例注入request、session和application對象。與之對應的三個接口和它們的方法如下所示:
Ø org.apache.struts2.interceptor.RequestAware
框架利用該接口,向Action實例注入request Map對象。該接口只有一個方法,如下:
— public void setRequest(Map request)
Ø org.apache.struts2.interceptor.SessionAware
框架利用該接口,向Action實例注入session Map對象。該接口只有一個方法,如下:
— void setSession(Map session)
Ø org.apache.struts2.interceptor.ApplicationAware
框架利用該接口,向Action實例注入application Map對象。該接口只有一個方法,如下:
— void setApplication(Map application)
我們看例3-13。
例3-13 通過接口注入來獲取request、session和application對象的LoginAction2
package org.sunxin.struts2.ch03.action.way1;
import java.util.Map;
import org.apache.struts2.interceptor.ApplicationAware;
import org.apache.struts2.interceptor.RequestAware;
import org.apache.struts2.interceptor.SessionAware;
import org.sunxin.struts2.ch03.model.User;
import com.opensymphony.xwork2.Action;
public class LoginAction2 implements Action, RequestAware, SessionAware, ApplicationAware
{
private Map request;
private Map session;
private Map application;
…
@SuppressWarnings("unchecked")
@Override
public String execute() throws Exception
{
if("zhangsan".equals(user.getUsername())
&& "1234".equals(user.
getPassword()))
{
//在請求中放置歡迎信息。
request.put("greeting", "歡迎您來到程序員之家");
//在session中保存user對象
session.put("user", user);
//統計用戶訪問量,在application中保存用戶訪問量數據
Integer count = (Integer)application.get("counter");
if(null == count)
count=1;
else
count++;
application.put("counter", count);
return SUCCESS;
}
else
{
return ERROR;
}
}
@Override
public void setRequest(Map request)
{
this.request = request;
}
@Override
public void setSession(Map session)
{
this.session = session;
}
@Override
public void setApplication(Map application)
{
this.application = application;
}
}
LoginAction2類實現了RequestAware、 SessionAware和ApplicationAware接口,框架在運行時會調用這三個接口中的方法,向LoginAction2注入 request、session和application對象。在execute()方法中不再需要訪問ActionContext,因此我們刪除了與之 相關的代碼。
雖然利用Struts 2提供的request、session和application對象就可以對HttpServletRequest、HttpSession和 ServletContext對象中的數據進行操作,但它們畢竟是Map類型,如果我們需要調用HttpServletRequest、 HttpSession和ServletContext中的特定操作,例如獲取請求方法的名字(調用HttpServletRequest中的 getMethod()方法),那麼可以使用下一節介紹的方式來獲取Servlet環境中的對象。
與Servlet API耦合的訪問方式
直接訪問Servlet API將使你的Action與Servlet環境耦合在一起,我們知道對於HttpServletRequest、 HttpServletResponse和ServletContext這些對象,它們都是由Servlet容器來構造的,與這些對象綁定在一起,測試時 就需要有Servlet容器,不便於Action的單元測試。但有時候,我們又確實需要直接訪問這些對象,那麼當然是以完成任務需求爲主。
要直接獲取HttpServletRequest和ServletContext對象,可以使用org.apache.struts2. ServletActionContext類,該類是ActionContext的子類,在這個類中定義下面兩個靜態方法:
Ø public static HttpServletRequest getRequest()
得到HttpServletRequest對象。
Ø public static ServletContext getServletContext()
得到ServletContext對象。
此外,ServletActionContext類還給出了獲取HttpServletResponse對象的方法,如下:
Ø public static HttpServletResponse getResponse()
ServletActionContext類並沒有給出直接得到HttpSession對象的方法,HttpSession對象可以通過HttpServletRequest對象來得到。
除了上述的方法調用得到 HttpServletRequest和ServletContext對象外,還可以調用ActionContext對象的get()方法,傳遞 ServletActionContext.HTTP_REQUEST和ServletActionContext.SERVLET_CONTEXT鍵值 來得到HttpServletRequest和ServletContext對象,如下所示:
Ø ActionContext.getContext().get(ServletActionContext.HTTP_REQUEST);
得到與ServletActionContext.HTTP_REQUEST鍵值綁定的HttpServletRequest對象。
Ø ActionContext.getContext().get(ServletActionContext.SERVLET_CONTEXT);
得到與ServletActionContext.SERVLET_CONTEXT鍵值綁定的ServletContext對象。
同樣的,也可以向ActionContext的get()方法傳遞ServletActionContext.HTTP_ RESPONSE鍵值來得到HttpServletResponse對象,如下:
Ø ActionContext.getContext().get(ServletActionContext.HTTP_RESPONSE);
建議讀者採用第一種方式來獲取HttpServletRequest和ServletContext對象,這樣簡單而又清晰。
我們看例3-14。
例3-14 通過ServletActionContext來獲取HttpServletRequest和ServletContext對象的LoginAction3
package org.sunxin.struts2.ch03.action.way2;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.struts2.ServletActionContext;
import org.sunxin.struts2.ch03.model.User;
import com.opensymphony.xwork2.Action;
public class LoginAction3 implements Action
{
...
@Override
public String execute() throws Exception
{
if("zhangsan".equals(user.getUsername())
&& "1234".equals(user.
getPassword()))
{
HttpServletRequest request = ServletActionContext.getRequest();
HttpSession session = request.getSession();
ServletContext context = ServletActionContext.getServletContext();
/*ActionContext ctx = ActionContext.getContext();
HttpServletRequest request = (HttpServletRequest)ctx.get(Servlet ActionContext.HTTP_REQUEST);
HttpSession session = request.getSession();
ServletContext context = (ServletContext)ctx.get(ServletAction Context.
SERVLET_CONTEXT);*/
//在請求中放置歡迎信息。
request.setAttribute("greeting", "歡迎您來到程序員之家");
//在session中保存user對象
session.setAttribute("user", user);
//統計用戶訪問量,在application中保存用戶訪問量數據
Integer count = (Integer)context.getAttribute("counter");
if(null == count)
count=1;
else
count++;
context.setAttribute("counter", count);
return SUCCESS;
}
else
{
return ERROR;
}
}
}
除了利用ServletActionContext來獲取 HttpServletRequest對象和ServletContext對象這種方式外,Action類還可以實現 ServletRequestAware和ServletContextAware接口,由Struts 2框架向Action實例注入HttpServletRequest和ServletContext對象。
org.apache.struts2.interceptor.ServletRequestAware接口只有一個方法,如下所示:
Ø void setServletRequest(HttpServletRequest request)
org.apache.struts2.util.ServletContextAware接口也只有一個方法,如下所示:
Ø void setServletContext(ServletContext context)
ServletRequestAware接口和ServletContextAware接口不屬於同一個包,前者在org.apache.struts2.interceptor包中,後者在org.apache.struts2.util包中,這很讓人迷惑。
我們看例3-15。
例3-15 通過接口注入來獲取HttpServletRequest和ServletContext對象的LoginAction4
package org.sunxin.struts2.ch03.action.way2;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.struts2.interceptor.ServletRequestAware;
import org.apache.struts2.util.ServletContextAware;
import org.sunxin.struts2.ch03.model.User;
import com.opensymphony.xwork2.Action;
public class LoginAction4 implements Action, ServletRequestAware, ServletContextAware
{
private HttpServletRequest request;
private ServletContext context;
…
@Override
public String execute() throws Exception
{
if ("zhangsan".equals(user.getUsername())
&& "1234".equals(user.
getPassword()))
{
HttpSession session = request.getSession();
//在請求中放置歡迎信息。
request.setAttribute("greeting", "歡迎您來到程序員之家");
//在session中保存user對象
session.setAttribute("user", user);
//統計用戶訪問量,在application中保存用戶訪問量數據
Integer count = (Integer) context.getAttribute("counter");
if (null == count)
count = 1;
else
count++;
context.setAttribute("counter", count);
return SUCCESS;
}
else
{
return ERROR;
}
}
@Override
public void setServletRequest(HttpServletRequest request)
{
this.request = request;
}
@Override
public void setServletContext(ServletContext context)
{
this.context = context;
}
}
LoginAction4類實現了 ServletRequestAware和ServletContextAware接口,框架在運行時會調用這兩個接口中的方法,向 LoginAction4注入HttpServletRequest和ServletContext對象。在execute()方法中不再需要訪問 ServletActionContext,因此我們刪除了與之相關的代碼。