您的位置:首页 > 编程语言 > Java开发

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

2016-12-26 10:46 281 查看
  很多小伙伴对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
d668
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,浏览器窗口中显示的字符串的时间值也一直在随着时间推进。





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





  演示完了http协议中与缓存相关的头部,第一部分源码解读的任务内容结束~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java web http servlet