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

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

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

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

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获取服务器上某一资源
HEADHEAD和GET本质是一样的,区别在于HEAD请求的响应不包含响应实体,而仅仅包含响应消息头
POST向服务器提交数据
PUTPUT和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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值