http請求的參數和屬性
練習搭建和使用SSH框架的時候,遇到一個http方面很簡單的問題。一個form的表單登錄請求,表單爲最基本的html表單內容,代碼如下
在struts的action中需要獲取輸入的用戶名和密碼,最開始我採用的代碼如下
但是在運行的時候,一直在user.setUserName(request.getAttribute("user_name").toString());這裏報空指針異常,後來改成
user.setUserName(request.getParameter("user_name"));之後就可以正常運行了。
還是上面的SSH項目,爲了瞭解這其中的區別,我將parameters和attributes都打印出來,結果如下
the actual type for request org.apache.struts2.dispatcher.StrutsRequestWrapper
attribute:< __cleanup_recursion_counter,1>
attribute:< struts.actionMapping,org.apache.struts2.dispatcher.mapper.ActionMapping@19a4e8e>
attribute:< struts.valueStack,com.opensymphony.xwork2.ognl.OgnlValueStack@19b7b9d>
parameter:<passwd,sdfg>
parameter:<user_name,lynn>
由此可見,表單傳遞請求的方式主要是通過parameters來完成。而attributes的使用就主要是框架內部使用。
雖然修改之後系統成功運行,但對於java的Http以及表單請求卻產生了許多問題,藉此機會做個瞭解。
以上是來自tomcat源代碼中整理出來的Servlet系統的一部分接口、類的繼承和實現關係。但由於這裏使用的是HttpServletRequest,所以研究的重點要放在HttpServletRequest類上,下面是根據tomat源代碼整理出來的HttpServletRequest系統的結構圖。
從上圖可以看出,本項目中的request.getAttribute和request.getParameter方法是繼承自父接口ServletRequest的。這上面解釋了tomcat所提供的http系統的大致框架結構。要理解這兩個方法之間具體的差別,需要知道這兩個對象的實際類型和對象實例化的點。使用
System. out.println("the
actual type for request "+request.getClass().getName());將request對象的類型打印出來,顯示的結果爲“the actual type
for request org.apache.struts2.dispatcher.StrutsRequestWrapper”
爲什麼是request的類型是org.apache.struts2.dispatcher.StrutsRequestWrapper呢?
StrutsRequestWrapper類是來自Struts框架,由於在項目中採用了SSH框架,Struts框架對tomcat的http系統進行了擴充。StrutsRequestWrapper是繼承自HttpServletRequestWrapper類,這個類又繼承自ServletRequestWrapper,並且實現了HttpServletRequest接口
public class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest{/*....*/},它提供一個帶HttpServletRequest類型參數的構造函數。代碼如下
public HttpServletRequestWrapper(HttpServletRequest
request) {
super(request);
}
其父類ServletRequestWrappet實現了ServletRequest接口,並且提供了一個帶ServletRequest類型參數的構造函數,相關代碼如下。
private ServletRequest request;
/**
* Creates a ServletRequest adaptor wrapping the given request object.
*
* @throws java.lang.IllegalArgumentException
* if the request is null
*/
public ServletRequestWrapper(ServletRequest
request) {
if (request
== null) {
throw new IllegalArgumentException("Request
cannot be null");
}
this.request =
request;
}
在StrutsRequestWrapper類中重寫了getAttribute方法,沒有重寫getParameter方法。其父類HttpServletRequestWrapper中對這兩個方法都沒有進行重寫。所以,本項目中request對象調用的getParameter方法是來自於ServletRequestWrapper的,代碼如下
@Override
public String
getParameter(String name) {
return this .request .getParameter(name);
}
結合其構造函數來看,具體其作用的還是在生成StrutsRequestWrapper對象時傳遞進來的HttpServletRequest對象。
那就從Struts框架的啓動配置入手,通過在web.xml中配置filter來將Struts框架引入項目。在web.xml中雨filter相關的配置代碼如下。也就是說,tomcat在加載項目的時候,會以StrutsPrepareAndExecuteFilter作爲入口,下面順藤摸瓜。
<filter>
<filter-name> struts</ filter-name>
<filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </filter-class>
</filter>
StrutsPrepareAndExecuteFilter實現了Filter接口,每次進行請求的時候,tomcat會調用其doFilter方法。在doFilter代碼如下
public void doFilter(ServletRequest
req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
try {
prepare.setEncodingAndLocale(request,
response);
prepare.createActionContext(request,
response);
prepare.assignDispatcherToThread();
if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns ))
{
chain.doFilter(request, response);
} else {
request = prepare.wrapRequest(request);
ActionMapping mapping = prepare.findActionMapping(request,
response, true);
if (mapping
== null) {
boolean handled
= execute.executeStaticResourceRequest(request, response);
if (!handled)
{
chain.doFilter(request, response);
}
} else {
execute.executeAction(request,
response, mapping);
}
}
} finally {
prepare.cleanupRequest(request);
}
}
其中先調用createActionContext創建了一個ActionContext對象。prePare是PrepareOpearation類型的對象,類的createAction方法中部分代碼如下
ActionContext oldContext = ActionContext.getContext();
if (oldContext
!= null) {
// detected existing context, so we are probably in a forward
ctx = new ActionContext(new HashMap<String,
Object>(oldContext.getContextMap()));
} else {
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
stack.getContext().putAll( dispatcher.createContextMap(request,
response, null, servletContext ));
ctx = new ActionContext(stack.getContext());
}
request.setAttribute( CLEANUP_RECURSION_COUNTER,
counter);
ActionContext. setContext(ctx);
return ctx;
也就是說,這裏生成的Map<String,Object> context對象來自舊的ActionContext對象。如果舊的不存在,那就用最初的配置文件構造一個出來,也就是servletContext對象,在PrepareOperations類的構造函數中被初始化,也就是根據tomcat調用StrutsPrepareAndExecuteFilter的init方法時,傳遞進來的配置內容產生。
然後parepare.wrapRequest方法實現對reques對象的包裝,轉成StrutsRequestWrapper對象。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.