关于servlet的一些疑问

Servlet API

问题引入:

1、下面的HttpServletRequest 是个接口,这里request不是实现类为什么能直接用去调用类的方法。

public TargetPhone getTargetPhoneById(@RequestParam("id") Long id, HttpServletRequest  request) {
		if(id == null) {
			return null;
		}
		TargetPhone targetPhone = targetPhoneService.getTargetPhoneById(id);
		request.getSession().setAttribute("oldTargetPhone", targetPhone);
		return targetPhone;
	}

刚开始时被SpringIOC 框住了,以为你在这里声明个接口类型的参数,它自动给你装配。

后来一想不对啊,在没Spring的传统servlet 中也是这样写的啊。

public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
      // 设置响应内容类型
      response.setContentType("text/html");
      // 实际的逻辑是在这里
      PrintWriter out = response.getWriter();
      out.println("<h1>" + message + "</h1>");
  }

后来同学点拨了一下。感谢@林旺 

这不就是传统的方法调用中的方法参数吗。你在这个方法里写的接口类型的参数,会在有人调用他的时候传入实参,实参就是具体的实现类了。根本没IOC什么事。

由此又引发了问题

HttpServletRequest 的实现类是谁。是谁调用doGet()方法。又是谁,什么时候把HttpServletRequest的实现类作为实参传入的?

2、HttpServletRequest 的实现类是谁

刚开始的时候直接在IDE内按住ctrl点击HttpServletRequest,去看他的源码,没有找到相关实现类的介绍。

只发现他确实是个接口,实现自ServletRequest,里面有很多常见的方法

113846_b2Q9_2885163.png

再点ServletRequest 就到头了

114132_pM2H_2885163.png

然后去查阅servletAPI 发现了他的实现类 HttpServletRequestWrapper

114317_htig_2885163.png

原来是有默认的实现类的,我还以为用代理模式动态生成的实现类呢。、

3、谁调用doGet()方法

通过上面问题一中的doGet()方法可以看到,在这个方法里就已经用了这个形参。

所以先看下doGet是怎么被调用

自定义的servlet的 doGet,doPost是怎样被调用到的

  1. 浏览发送请求到服务器的容器,容器调用Servlet
  2. servlet调用构造方法与init方法初始化
  3. servlet调用service方法,由于自定义的没有service方法,就去调用父类HttpServlet中的servcie方法
  4. 父类HttpServlet中的servcie方法,调用了重载后的service方法
  5. 重载后的service方法,判断请求的方法后,分别调用 doGet,doPost,doPut,doDelete之类的方法
  6. 在被调用时,由于子类已经重写的以上的方法,所以要执行子类重写后的, doGet,doPost方法 

好了调用doGet 的问题解决了。但又是谁,什么时候把HttpServletRequest的实现类作为实参传入的?还没解决。

4、又是谁,什么时候把HttpServletRequest的实现类作为实参传入的?

上面说了doGet()由servlet的service()方法调用的,所以看下service方法的源码。

package javax.servlet.http;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.ResourceBundle;
import javax.servlet.*;
// Referenced classes of package javax.servlet.http:
//            NoBodyResponse, HttpServletRequest, HttpServletResponse
public abstract class HttpServlet extends GenericServlet
    implements Serializable
{
    public HttpServlet()
    {
    }
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_get_not_supported");
        if(protocol.endsWith("1.1"))
            resp.sendError(405, msg);
        else
            resp.sendError(400, msg);
    }
    protected long getLastModified(HttpServletRequest req)
    {
        return -1L;
    }
    protected void doHead(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        NoBodyResponse response = new NoBodyResponse(resp);
        doGet(req, response);
        response.setContentLength();
    }
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_post_not_supported");
        if(protocol.endsWith("1.1"))
            resp.sendError(405, msg);
        else
            resp.sendError(400, msg);
    }
    protected void doPut(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_put_not_supported");
        if(protocol.endsWith("1.1"))
            resp.sendError(405, msg);
        else
            resp.sendError(400, msg);
    }
    protected void doDelete(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String protocol = req.getProtocol();
        String msg = lStrings.getString("http.method_delete_not_supported");
        if(protocol.endsWith("1.1"))
            resp.sendError(405, msg);
        else
            resp.sendError(400, msg);
    }
    private Method[] getAllDeclaredMethods(Class c)
    {
        if(c.equals(javax/servlet/http/HttpServlet))
            return null;
        Method parentMethods[] = getAllDeclaredMethods(c.getSuperclass());
        Method thisMethods[] = c.getDeclaredMethods();
        if(parentMethods != null && parentMethods.length > 0)
        {
            Method allMethods[] = new Method[parentMethods.length + thisMethods.length];
            System.arraycopy(parentMethods, 0, allMethods, 0, parentMethods.length);
            System.arraycopy(thisMethods, 0, allMethods, parentMethods.length, thisMethods.length);
            thisMethods = allMethods;
        }
        return thisMethods;
    }
    protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        Method methods[] = getAllDeclaredMethods(getClass());
        boolean ALLOW_GET = false;
        boolean ALLOW_HEAD = false;
        boolean ALLOW_POST = false;
        boolean ALLOW_PUT = false;
        boolean ALLOW_DELETE = false;
        boolean ALLOW_TRACE = true;
        boolean ALLOW_OPTIONS = true;
        for(int i = 0; i < methods.length; i++)
        {
            Method m = methods[i];
            if(m.getName().equals("doGet"))
            {
                ALLOW_GET = true;
                ALLOW_HEAD = true;
            }
            if(m.getName().equals("doPost"))
                ALLOW_POST = true;
            if(m.getName().equals("doPut"))
                ALLOW_PUT = true;
            if(m.getName().equals("doDelete"))
                ALLOW_DELETE = true;
        }
        String allow = null;
        if(ALLOW_GET && allow == null)
            allow = "GET";
        if(ALLOW_HEAD)
            if(allow == null)
                allow = "HEAD";
            else
                allow = (new StringBuilder()).append(allow).append(", HEAD").toString();
        if(ALLOW_POST)
            if(allow == null)
                allow = "POST";
            else
                allow = (new StringBuilder()).append(allow).append(", POST").toString();
        if(ALLOW_PUT)
            if(allow == null)
                allow = "PUT";
            else
                allow = (new StringBuilder()).append(allow).append(", PUT").toString();
        if(ALLOW_DELETE)
            if(allow == null)
                allow = "DELETE";
            else
                allow = (new StringBuilder()).append(allow).append(", DELETE").toString();
        if(ALLOW_TRACE)
            if(allow == null)
                allow = "TRACE";
            else
                allow = (new StringBuilder()).append(allow).append(", TRACE").toString();
        if(ALLOW_OPTIONS)
            if(allow == null)
                allow = "OPTIONS";
            else
                allow = (new StringBuilder()).append(allow).append(", OPTIONS").toString();
        resp.setHeader("Allow", allow);
    }
    protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String CRLF = "\r\n";
        String responseString = (new StringBuilder()).append("TRACE ").append(req.getRequestURI()).append(" ").append(req.getProtocol()).toString();
        for(Enumeration reqHeaderEnum = req.getHeaderNames(); reqHeaderEnum.hasMoreElements();)
        {
            String headerName = (String)reqHeaderEnum.nextElement();
            responseString = (new StringBuilder()).append(responseString).append(CRLF).append(headerName).append(": ").append(req.getHeader(headerName)).toString();
        }
        responseString = (new StringBuilder()).append(responseString).append(CRLF).toString();
        int responseLength = responseString.length();
        resp.setContentType("message/http");
        resp.setContentLength(responseLength);
        ServletOutputStream out = resp.getOutputStream();
        out.print(responseString);
        out.close();
    }
    protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();
        if(method.equals("GET"))
        {
            long lastModified = getLastModified(req);
            if(lastModified == -1L)
            {
                doGet(req, resp);
            } else
            {
                long ifModifiedSince = req.getDateHeader("If-Modified-Since");
                if(ifModifiedSince < (lastModified / 1000L) * 1000L)
                {
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else
                {
                    resp.setStatus(304);
                }
            }
        } else
        if(method.equals("HEAD"))
        {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);
        } else
        if(method.equals("POST"))
            doPost(req, resp);
        else
        if(method.equals("PUT"))
            doPut(req, resp);
        else
        if(method.equals("DELETE"))
            doDelete(req, resp);
        else
        if(method.equals("OPTIONS"))
            doOptions(req, resp);
        else
        if(method.equals("TRACE"))
        {
            doTrace(req, resp);
        } else
        {
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object errArgs[] = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(501, errMsg);
        }
    }
    private void maybeSetLastModified(HttpServletResponse resp, long lastModified)
    {
        if(resp.containsHeader("Last-Modified"))
            return;
        if(lastModified >= 0L)
            resp.setDateHeader("Last-Modified", lastModified);
    }
    public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException
    {
        HttpServletRequest request;
        HttpServletResponse response;
        try
        {
            request = (HttpServletRequest)req;
            response = (HttpServletResponse)res;
        }
        catch(ClassCastException e)
        {
            throw new ServletException("non-HTTP request or response");
        }
        service(request, response);
    }
    private static final String METHOD_DELETE = "DELETE";
    private static final String METHOD_HEAD = "HEAD";
    private static final String METHOD_GET = "GET";
    private static final String METHOD_OPTIONS = "OPTIONS";
    private static final String METHOD_POST = "POST";
    private static final String METHOD_PUT = "PUT";
    private static final String METHOD_TRACE = "TRACE";
    private static final String HEADER_IFMODSINCE = "If-Modified-Since";
    private static final String HEADER_LASTMOD = "Last-Modified";
    private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
    private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");
}

通过HttpServlet的源码我们发现,该类有两个service()方法.

但Servlet接口中只定义了一个service方法

143031_UTYj_2885163.png

下面我们一一说明:

第一个service方法 : service(ServletRequest req, ServletResponse res)

实现了相应的接口方法。将ServletRequest和ServletResponse转换为HttpServletRequest和HttpServletResponse。并调用重载后的service(HttpServletRequest req, HttpServletResponse resp)方法

public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException {
        HttpServletRequest  request;
        HttpServletResponse response;
        try {
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
            throw new ServletException("non-HTTP request or response");
        }
        service(request, response);
}

第二个service 方法:service(HttpServletRequest req, HttpServletResponse resp)

protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
        String method = req.getMethod();
        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }
        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);
        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);        
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);
        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //
            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
}
 

通过该service()方法可以发现,客户端的请求先提交给Service方法时,先获取提交的方法名,根据方法名调用相应的具体方法。如果重写了该方法,那么就会调用子类复写后的方法。

我们本想解决是谁什么时候把HttpServletRequest实现类作为实参传入的” 这个问题才翻看的有service()方法的HttpServlet类的源码。发现这里也是直接接收的这个参数。那说明还得找源头。

而且还引出了另一个问题 “为啥有HttpServletRequest , 还有 ServletRequest。他们是什么关系

继续翻阅API

144814_diI1_2885163.png

发现ServletRequest, 是HttpServletRequest父接口。

ServletRequest 注释如下:

Defines an object to provide client request information to a servlet. The servlet container creates a ServletRequest object and passes it as an argument to the servlet's service method.

A ServletRequest object provides data including parameter name and values, attributes, and an input stream. Interfaces that extend ServletRequest can provide additional protocol-specific data (for example, HTTP data is provided by HttpServletRequest.

定义一个对象来向servlet提供客户端请求信息。 servlet容器创建一个ServletRequest对象,并将其作为参数传递给servlet的服务方法。

ServletRequest对象提供的数据包括参数名称和值,属性和输入流。 扩展ServletRequest的接口可以提供额外的协议特定数据(例如,HTTP数据由HttpServletRequest提供。

HttpServletRequest 注释如下

Extends the ServletRequest interface to provide request information for HTTP servlets.

The servlet container creates an HttpServletRequest object and passes it as an argument to the servlet's service methods (doGet, doPost, etc).

扩展ServletRequest接口,为HTTP Servlet提供请求信息。

servlet容器创建一个HttpServletRequest对象,并将其作为参数传递给servlet的服务方法(doGet,doPost等)。

 

到这里就解决了原来的是谁什么时候把HttpServletRequest实现类作为实参传入的”的问题

同时HttpServletRequest、和ServletRequest的关系也解决了

是servlet容器本身创建的HttpServletRequest接口的实现类,将其作为参数传递给service方法的。

时间,大胆猜测应该是http请求到后,servlet 执行 init()方法时。

HttpServletRequest这个对象本身应该是对于http请求信息的解析封装。

 

至此自己心里的疑问全部解决。由于这篇文章是解决个人的问题,所以我就按顺序写了。有点凌乱。

应该没人看吧。。。。我就先这样写了。

最后劝大家一句,写业务就写业务,别瞎想。要不然一上午又过去了。。。。。。。。(手动滑稽)

 

 

 

转载于:https://my.oschina.net/zjllovecode/blog/1620205

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值