在jsp中的request
今天在知乎看到一個小問題,可能這也是前期我們都有疑問的,答主的解答比較好,於是就乾脆收錄過來,自己消化,別方便大家理解。
原問題:
在jsp介面用request.setAttribute("aaa","bbb")提交到servlet後,在servlet中用
request.getAttribute("aaa");語句卻為什麼接受不到任何值啊!
解答(以下附上了作者資訊):
作者:chainho
連結:https://www.zhihu.com/question/39518170/answer/81742503
來源:知乎
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
首先先上關於setAttribute這個方法的javadoc
/** * Stores an attribute in this request. Attributes are reset between * requests. This method is most often used in conjunction with * {@link RequestDispatcher}. * <p> * Attribute names should follow the same conventions as package names. * Names beginning with <code>java.*</code> and <code>javax.*</code> are * reserved for use by the Servlet specification. Names beginning with * <code>sun.*</code>, <code>com.sun.*</code>, <code>oracle.*</code> and * <code>com.oracle.*</code>) are reserved for use by Oracle Corporation. * <br> * If the object passed in is null, the effect is the same as calling * {@link #removeAttribute}. <br> * It is warned that when the request is dispatched from the servlet resides * in a different web application by <code>RequestDispatcher</code>, the * object set by this method may not be correctly retrieved in the caller * servlet. * * @param name * a <code>String</code> specifying the name of the attribute * @param o * the <code>Object</code> to be stored */
注意這一句:
Attributes are reset between
* requests. This method is most often used in conjunction with
* {@link RequestDispatcher}.
主要是用在重定向這塊的。
而為什麼當你提交之後就不能再取到值呢?這是因為你在提交之後,這個setAttribute是在jsp頁面渲染的時候已經設定,這個請求響應完成之後,對於requet和response都會執行一個這樣的動作
request.recycle(); response.recycle();
這個recycle會將之前的設定清空的。
又深入原始碼分析了下重定向和轉發兩者的實現方式,來從本質上說明為什麼這些資料為什麼不能被保留下來。
說起重定向,轉發,每個Web開發者估計都能頭頭是道的說上幾句類似於重定向會顯示真實路徑,轉發不會以及其它一類教科書式的概念。這些概念也都沒錯,但卻沒從本質上說明這兩者的區別。更別提其根本原理是怎麼實現的,以及為何重定向之後,原來的request中的資料都丟掉了,而轉發卻還是能保證request中的資料依然保留呢?
作為一個有追求的程式設計師,希望本文可以讓你深入瞭解這兩者的本質區別,從而知其所以然。
定義
首先來看兩者的javadoc。
sendRedirect()
/** * Sends a temporary redirect response to the client using the specified redirect location URL. This method can accept relative URLs; the servlet container must convert the relative URL to an absolute URL before sending the response to the client. If the location is relative without a leading '/' the container interprets it as relative to the current request URI. If the location is relative with a leading '/' the container interprets it as relative to the servlet container root. */ public void sendRedirect(String location) throws IOException;
重定向是向客戶端傳送一個指定URL的臨時重定向的響應。
forward()
/** * Forwards a request from a servlet to another resource (servlet, JSP file, or HTML file) on the server. This method allows one servlet to do preliminary processing of a request and another resource to generate the response. The request and response parameters must be either the same objects as were passed to the calling servlet's service method . */ public void forward(ServletRequest request, ServletResponse response);
轉發,則是將一個請求轉到伺服器的另一個資源。在處理完初步請求另外的資源之後生成響應。
定義基本說明了轉發操作為何可以保持request內的parameter,attribute這些值都可以保留,而重定向操作卻會丟棄的原因:
轉發是在服務端完成的,並沒有經過客戶端
轉發整個操作完成後才生成響應
重定向是服務端向客戶端傳送指定的URL
重定向是在客戶端完成的
我們再來看Tomcat內部,對於兩者是怎樣一種實現方式。
2. 容器實現
我們在servlet內部一般對於這兩者的使用形式也相當直觀,例如對於hello.jsp的請求:
sendRedirct方法
response.sendRedirect("/hello.jsp");
此時,內部的處理方式如下:
public void sendRedirect(String location, int status) throws IOException {
// Generate a temporary redirect to the specified location
try {
String absolute = toAbsolute(location);
setStatus(status); //這裡,重定向是返回302狀態碼以及Location和對應的url
setHeader("Location", absolute);
} catch (IllegalArgumentException e) {
setStatus(SC_NOT_FOUND);
}}
展現在瀏覽器中的結果如下:
&lt;img src="https://pic3.zhimg.com/50/83b51cd54b22b4c6342f18a5c99ade7c_hd.jpg" data-rawwidth="403" data-rawheight="169" class="content_image" width="403"&gt;
即根據Location,瀏覽器最終再發起新的請求,最終展現在瀏覽器中的即為新請求的URL,也就是大家常說的重定向會顯示最終的URL。
有上這些並不能造成重定向操作將之前request中已經繫結的一系列parameter和attribute丟掉。最根本的原因是一個請求完整處理完成之後,整個請求會有一個release的過程,即CoyoteAdapter的service方法執行完的finally塊中執行release這一過程,基本如下:
finally {
if (!comet && !async || error.get()) {
request.recycle(); //注意這兩行程式碼
response.recycle();
}
}
具體request的recycle部分程式碼如下:
/**
* Release all object references, and initialize instance variables, in preparation for reuse of this object.
*/
public void recycle() {
attributes.clear();
requestedSessionId = null;
requestedSessionURL = false;
parameterMap.clear();
pathParameters.clear();
}
我們看到用於儲存setAttribute方法設定的和setParameter方法設定的資料在這裡都clear掉了。這也是重定向不能夠保留資料的真正原因。
forward方法
forward方法一般使用如下:
request.getRequestDispatcher("/hello.jsp").forward(request, response);
forward方法內部最終會呼叫dispatcher的doForward方法
void doForward(ServletRequest request, ServletResponse response){
// Set up to handle the specified request and response
State state = new State(request, response, false);
wrapResponse(state);
ApplicationHttpRequest wrequest =
(ApplicationHttpRequest) wrapRequest(state);
String contextPath = context.getPath();
HttpServletRequest hrequest = state.hrequest;
if (hrequest.getAttribute(
RequestDispatcher.FORWARD_REQUEST_URI) == null) {
wrequest.setAttribute(RequestDispatcher.FORWARD_PATH_INFO,hrequest.getPathInfo());
wrequest.setAttribute(RequestDispatcher.FORWARD_QUERY_STRING, hrequest.getQueryString());}
wrequest.setContextPath(contextPath);
wrequest.setRequestURI(requestURI);
wrequest.setServletPath(servletPath);
wrequest.setPathInfo(pathInfo);
if (queryString != null) {
wrequest.setQueryString(queryString);
wrequest.setQueryParams(queryString);
}
processRequest(request,response,state); //進行第二個資源的請求
}
}
第二個資源的請求處理與一般的請求處理類似,只是在第一個請求之上,並沒有返回響應時繼續發起第二個請求,此時第一個請求的各類引數會繼續向後傳遞,最終資料全部處理完成之後,整個響應傳送回客戶端。此時上面的release流程也依然會走,但並沒有什麼影響,畢竟第二個資源已經請求處理完成。
而由於瀏覽器發請求的時候是一個固定的URL,整個重定向是服務端內部進行的,瀏覽器並沒有感知到,因此也不會顯示出來。
整個應用伺服器內部處理過程再加上清晰的定義,相信這兩者的本質區別已經顯露無疑。