在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中的数据依然保留呢?



作为一个有追求的程序员,希望本文可以让你深入了解这两者的本质区别,从而知其所以然。


  1. 定义


首先来看两者的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);
    }}

展现在浏览器中的结果如下:
&amp;lt;img src=&quot;https://pic3.zhimg.com/50/83b51cd54b22b4c6342f18a5c99ade7c_hd.jpg&quot; data-rawwidth=&quot;403&quot; data-rawheight=&quot;169&quot; class=&quot;content_image&quot; width=&quot;403&quot;&amp;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,整个重定向是服务端内部进行的,浏览器并没有感知到,因此也不会显示出来。



整个应用服务器内部处理过程再加上清晰的定义,相信这两者的本质区别已经显露无疑。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值