简要说明
HttpServlet 抽象类是GenericServlet的子类,HttpServlet类为servlet接口提供了与HTTP协议相关的通用实现,也就是说HttpServlet对象适合运行在与客户端采用HTTP协议通信的servlet容器或web服务器中。
请求类型
(仅包含HttpServlet里面默认实现):
GET: 请求指定的文档的内容。
POST: 请求服务器接受所指定的文档作为对所标识的URI的新的从属实体。
PUT: 从客户端向服务器传送的数据取代指定的文档的内容。
DELETE: 请求服务器删除指定的页面。
HEAD: 仅返回头部信息。
OPTIONS: 返回服务器的请求类型列表。
TRACE: 用作调试,返回请求 URL, 版本信息以及请求的头信息。
解析service()方法
- 作用:被Servlet容器调用,完成请求
1) 把请求转发到具体的方法上,通过调用重载的service(HttpServletRequest,HttpServletResponse)
2) 完成这个方法做的事情就是把request和response转换成HttpServerRequest,HttpServletResponse,
3) 具体的转发工作由重载的service方法完成(通过method类型,转发请求给不同的方法处理,如:doGet、doPut等) - GET方式的缓存逻辑(图见底部)
1)首先通过getLastModified(req)获得修改时间lastModified
2)如果修改时间为-1直接调用doGet方法处理请求
3)如果不是-1,则获取请求中的Modified_Since的值ifModifiedSince,
3.1) ifModifiedSince 大于lastModified,缓存未过期,返回304通知浏览器,直接使用缓存
3.2) ifModifiedSince小于lastModified,缓存失效,调用doGet,同时ifModifiedSince = lastModified
init()方法
- 谁来调用
由servlet容器调用,以指示将servlet放入服务中的servlet。 - ServletConfig 是什么
servlet容器在初始化期间将信息传递给servlet的servlet配置对象。 init方法能做什么
在servlet的生命周期中,init()方法仅会被执行一次,可以被覆盖;因此,可用来初始化一些配置信息信息,如:http数据库的连接,spring容器初始化配置信息等等destory()方法
当Servlet对象退出声明周期时,负责释放占有的资源
源码解析
- doGet、doPost、doPut、doDelete默认实现(相同)
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(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
- doHead()方法解析
//仅返回头部信息,不返回实体
protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
//申明一个空实体
NoBodyResponse response = new NoBodyResponse(resp);
//调用doGet方法
doGet(req, response);
//将实体长度为空
response.setContentLength();
}
- doTrace()方法解析
//这个方法返回一个字符串到 HTTP 响应体里面,这个字符串包含请求 URL, 版本信息以及请求的头信息,主要是用来调试
protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
int responseLength;
//回车换行符
String CRLF = "\r\n";
//链接URI字符串和版本协议信息
String responseString = "TRACE "+ req.getRequestURI()+ " " + req.getProtocol();
Enumeration reqHeaderEnum = req.getHeaderNames();
//遍历所有请求头信息
while( reqHeaderEnum.hasMoreElements() ) {
String headerName = (String)reqHeaderEnum.nextElement();
// 拼接所有的请求头到字符串中,并且使用:分割名值对,每对头信息之间使用回车换行进行分隔
responseString += CRLF + headerName + ": " + req.getHeader(headerName);
}
// 附着回车换行符到字符串结尾
responseString += CRLF;
// 取得字符串字节长度信息
responseLength = responseString.length();
// 设置响应类型为 message/http
resp.setContentType("message/http");
// 设置响应体的长度
resp.setContentLength(responseLength);
ServletOutputStream out = resp.getOutputStream();
// 输出字符串消息到响应中
out.print(responseString);
// 关闭相应流,结束操作
out.close();
return;
}
- service()方法解析
//实际处理请求类,可以被重写
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);
}
//默认实现
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 从 HTTP 请求中取得这次请求所使用的 HTTT 方法
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
// 如果这次请求使用 GET 方法
// 取得这个 Servlet 的最后修改的时间
long lastModified = getLastModified(req);
if (lastModified == -1) {
//-1 代表这个 Servlet 不支持最后修改操作,直接调用 doGet() 进行处理 HTTP GET 请求
doGet(req, resp);
} else {
// 如果这个 Servlet 支持最后修改操作,取得请求头中包含的请求的最后修改时间
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// 如果请求头中包含的修改时间早于这个 Servlet 的最后修改时间,说明这个 Servlet 自从客户上一次 HTTP 请求已经被修改了 , 设置最新修改时间到响应头中
maybeSetLastModified(resp, lastModified);
// 调用 doGet 进行进行处理 HTTP GET 请求
doGet(req, resp);
} else {
// 如果请求头中包含修改时间晚于这个 Servlet 的最后修改时间,说明这个 Servlet 自从请求的最后修改时间后没有更改过,这种情况下,仅仅返回一个 HTTP 响应状态 SC_NOT_MODIFIED
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
………
} else {
// 如果这次请求是其他未知方法,返回错误代码 SC_NOT_IMPLEMENTED 给 HTTP 响应,并且显示一个错误消息,说明这个操作是没有实现的
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);
}
}
- doOptions()方法解析
//返回服务器的请求类型列表
protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//取得当前这个 Servlet 以及父类 Servlet 声明的所有方法,这些方法不包括本类 HTTP Servlet 所生命的方法
Method[] methods = getAllDeclaredMethods(this.getClass());
boolean ALLOW_GET = false;
boolean ALLOW_HEAD = false;
boolean ALLOW_POST = false;
boolean ALLOW_DELETE = false;
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)
if (allow==null) allow=METHOD_GET;
if (ALLOW_HEAD)
if (allow==null) allow=METHOD_HEAD;
else
allow += ", " + METHOD_HEAD;
if (ALLOW_POST)
if (allow==null) allow=METHOD_POST;
else
allow += ", " + METHOD_POST;
if (ALLOW_PUT)
if (allow==null) allow=METHOD_PUT;
else
allow += ", " + METHOD_PUT;
if (ALLOW_DELETE)
if (allow==null) allow=METHOD_DELETE;
else
allow += ", " + METHOD_DELETE;
if (ALLOW_TRACE)
if (allow==null) allow=METHOD_TRACE;
else allow += ", " + METHOD_TRACE;
if (allow==null) allow=METHOD_OPTIONS;
else allow += ", " + METHOD_OPTIONS;
//返回投信息
resp.setHeader("Allow", allow);
}