Java Web后台入门实战(一)-YuaneQi Sharing

来源:互联网 发布:网络市场调研报告要素 编辑:程序博客网 时间:2024/06/09 16:25

  很多小伙伴对Java Web后台开发感兴趣,但是又苦于入门,一是书上和网络上的其他资料大多是千篇一律,要么就是偏知识,大量篇幅介绍Servlet相关内容,看不进去,要么太浅,看完又觉得好像并没有学到什么。所以从大家的需求出发,内容主要包括以下四个部分:

  • 第一部分从javax.servlet.http.HttpServlet类源码开始,了解一次http请求过来后,服务器的处理逻辑是什么样儿的。
  • 第二部分是动手实践部分,如何在Eclipse中创建Web工程,并对web工程的目录结构进行一个分析。
  • 第三部分结合Tomcat中的examples了解Servlet相关类的应用。
  • 第四部分设计模式篇来看下Template Pattern在Servlet中的应用,不要觉得设计模式很高深,是无法理解的知识,我们平时在写代码的过程中已经不知不觉在应用它们了。

  需要的小伙伴可以下载此分享对应的PPT和FirstJavaWeb工程来对照着看

HttpServlet源码

  Servlet是运行在服务器端的程序,用于处理及响应客户端的请求,它是个特殊的Java类,这个Java类必须继承javax.servlet.http.HttpServlet,HttpServlet类提供了不同的方法用于处理客户端的请求。
这里写图片描述

方法 描述 doDelete 用于处理DELETE请求 doGet 用于处理GET请求 doHead 用于处理HEAD请求 doOptions 用于处理OPTIONS请求 doPost 用于处理POST请求 doPut 用于处理PUT请求 doTrace 用于处理TRACE请求 getLastModified 返回一个long整数,值为所请求数据的最后修改时间相对于GMT时间1970年1月1号0时0分0秒的毫秒数 service 用于映射请求,根据请求的HTTP方法,调用do Method

  根据HttpServlet service方法的处理逻辑,HttpServlet目前只可响应客户端的GET,HEAD,POST,PUT,DELETE,OPTIONS,TRACE请求。

http请求 描述 GET 获取服务器上某一资源 HEAD HEAD和GET本质是一样的,区别在于HEAD请求的响应不包含响应实体,而仅仅包含响应消息头 POST 向服务器提交数据 PUT PUT和POST极为相似,PUT通常指定了资源的存放位置,向指定资源位置上传其最新内容 DELETE 删除服务器上某一个资源 OPTIONS 获取服务器针对特定资源所支持的HTTP请求方法,请求头等 TRACE 回显服务器收到的请求,主要用于测试或诊断

  PUT,DELETE,OPTIONS,TRACE请求并不常使用,OPTIONS请求作者只在处理非简单CORS(Cross-origin resource sharing)的时候遇见过。
  通常我们的请求只有GET和POST两种,为了响应这两种请求,一般需要重写doGet和doPost两个方法,也可以通过重写service方法的方式。但是注意如果你重写的service方法没有调用do method 方法,即使你在Servlet中又重写了其他do method 方法也是不会被调用的,原因我们看了HttpServlet类的source code就会明白。

public abstract class HttpServlet extends GenericServlet{    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    {        if(DispatcherType.INCLUDE.equals(req.getDispatcherType()))        {            doGet(req, resp);        } else        {            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 static 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 = "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";        StringBuilder buffer = (new StringBuilder("TRACE ")).append(req.getRequestURI()).append(" ").append(req.getProtocol());        String headerName;        for(Enumeration reqHeaderEnum = req.getHeaderNames(); reqHeaderEnum.hasMoreElements(); buffer.append(CRLF).append(headerName).append(": ").append(req.getHeader(headerName)))            headerName = (String)reqHeaderEnum.nextElement();        buffer.append(CRLF);        int responseLength = buffer.length();        resp.setContentType("message/http");        resp.setContentLength(responseLength);        ServletOutputStream out = resp.getOutputStream();        out.print(buffer.toString());        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;                try                {                    ifModifiedSince = req.getDateHeader("If-Modified-Since");                }                catch(IllegalArgumentException iae)                {                    ifModifiedSince = -1L;                }                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 long serialVersionUID = 1L;    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是一个不包含任何抽象方法的抽象类,继承GenericServlet类,很巧妙的编码,既不能够直接创建它的实例,也不强迫子类去实现任何方法,就是说我们自己的Servlet类可以通过继承它,不重写任何方法就能够直接对外提供服务。
  HttpServlet类中有两个的service方法,public void service(ServletRequest req, ServletResponse res)方法是其父类GenericServlet类的抽象方法实现,作用是接受客户端的请求并将其传递给重载的service方法,protected void service(HttpServletRequest req, HttpServletResponse resp)。容器会针对每个客户端请求创建一个处理线程,准确来说应该是使用线程池中空闲的线程,并创建Request和Response对象传递给处理线程,就是说浏览器的一次http请求的所有信息都封装在HttpServletRequest中,而HttpServletResponse对象代表服务器对客户端的响应,可以通过操作这两个对象来交换数据。
  protected void service(HttpServletRequest req, HttpServletResponse resp),是HttpServlet类定义的重载方法,访问权限是protected,可用于子类继承。方法首先获取本次请求的http方法,并根据方法的类型去进入相应的if else分支处理,除了GET请求处理相对复杂一些,其他处理很简单,直接调用相应请求的do method 方法,当用户的请求不是上面列出的请求方法时,会向客户端返回501状态码,Method is not implemented by this servlet for this URI。在service中的GET请求逻辑部分首先调用getLastModified(HttpServletRequest req)方法得到一个long整数lastModified,如果为-1L则调用doGet方法,否则获取本次请求的“If-Modified-Since”头部得值,“If-Modified-Since”带一个时间值代表请求数据的上次修改时间,它会与lastModified值比较,如果lastModified值比较新,容器调用maybeSetLastModified(resp, lastModified)方法,这个方法的作用是在响应头中加一个“Last-Modified”头,告诉浏览器你应该更新本地缓存了。除此来之外的任何结果,则返回304状态码,告诉浏览器从你上次访问我之后,请求的网页未修改,你可以使用本地缓存直接加载页面,节省带宽和开销。我们看到子类如果不重写getLastModified方法的话,这个方法将永远返回-1L,每次GET请求都只是调用doGet方法而已。
  当浏览器发现响应中有“Last-Modified”头部,那么它在下一次请求同一数据时会加上“If-Modified-Since”请求头,值为上一次响应的“Last-Modified”时间值。304状态码告诉客户端自从上次请求后,请求的网页未修改,本地cache的页面是最新的。服务器返回此响应时,不会返回网页内容,也就是说浏览器只在每次启动后第一次访问这个页面时,才向服务器发出请求,对于后续的访问都是直接加载本地缓存的页面,这在提高网站性能方面特别有用。
  带大家演示一下,我们先写一个servlet,代码贴上,重写了父类的doGet方法,逻辑也很简单,就是打印系统当前时间相对GMT1970年1月1日0点0时0分的毫秒数。

@WebServlet(urlPatterns = ("/example/one"))public class ExampleOneServlet extends HttpServlet {    /**     * serialVersionUID:TODO.     */    private static final long serialVersionUID = 6686766899053457864L;    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {        resp.getWriter().append("Frist Servlet " + System.currentTimeMillis());    }//  @Override//  protected long getLastModified(HttpServletRequest req) {//      long now = System.currentTimeMillis();//      System.out.println("getLastModified: " + now);//      return now;//  }//  @Override//  protected long getLastModified(HttpServletRequest req) {//      return req.getDateHeader("If-Modified-Since");//  }}

  Tomcat中运行,浏览器查看,此时请求头和响应头中并没有任何特殊的头部。


浏览器运行

  解注释第一个重写的getLastModified方法并运行,这个getLastModified方法每次返回一个新的时间值,并且总是大于请求头”If-Modified-Since”的时间值。这次我们看到响应头中多了“Last-Modified”字段,刷新下浏览器,此时请求头中多了”If-Modified-Since”,并且响应头中依然包含“Last-Modified”头,响应状态码为200,浏览器窗口中显示的字符串的时间值也一直在随着时间推进。


多了响应头“Last-Modified”
这里写图片描述

  注释这个getLastModified方法,并解注释第二个重写的getLastModified方法,这个方法每次返回的值为请求头”If-Modified-Since”的值。刷新下浏览器,我们看到此时的响应头中并没有”Last-Modified”头,并且响应的状态码为304。再刷新下浏览器,我们看到浏览器窗口中显示的字符串中的时间值没变,证明了此时浏览器加载的是缓存的数据,并不是服务器端传回来的新数据。还有”If-Modified-Since”的时间值也没有改变。


304状态码
这里写图片描述

  演示完了http协议中与缓存相关的头部,第一部分源码解读的任务内容结束~


2 0
原创粉丝点击