这里仅仅对Servlet规范中必要重要的Servlet、Session、Rrequest、Response的接口及其子类进行讲解,其余诸如websocket、el、jsp、filter、listener的相关内容不做介绍。
Servlet是基于Java技术的,容器托管的,用于生成动态内容的web组件。Servlet接口是Java Servlet API的核心抽象。首先我们来看看它的源码:
public interface Servlet {
/**
* 被servlet容器在实例化servlet后调用该方法表示该servlet已经被装载,以服务的形式提供给客户
* @param config
* @throws ServletException
*/
public void init(ServletConfig config) throws ServletException;
/**
* 返回一个ServletConfig对象,该对象包含了
* Servlet初始化和启动参数
* @return
*/
public ServletConfig getServletConfig();
/**
* 用户request的响应函数
* @param req
* @param res
* @throws ServletException
* @throws IOException
*/
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
/**
* 返回servlet的作者,版本和版权等信息
* @return
*/
public String getServletInfo();
/**
* servlet容器要销毁该servlet时调用该函数
* @return
*/
public void destroy();
}
Servlet接口定义了一个Servlet所要拥有的行为集合。接下来我们再看看它的子类GenericServlet:
public abstract class GenericServlet implements Servlet, ServletConfig,
java.io.Serializable {
private static final long serialVersionUID = 1L;
private transient ServletConfig config;
/**
* 没有任何行为,所有初始化操作在init函数里
*/
public GenericServlet() {
// NOOP
}
@Override
public void destroy() {
// NOOP by default
}
/**
* 获取Servlet的初始化参数
*/
@Override
public String getInitParameter(String name) {
return getServletConfig().getInitParameter(name);
}
/**
* 获取Servlet的初始化参数名字
*/
@Override
public Enumeration<String> getInitParameterNames() {
return getServletConfig().getInitParameterNames();
}
@Override
public ServletConfig getServletConfig() {
return config;
}
/**
* 返回该Servlet的运行上下文对象
*/
@Override
public ServletContext getServletContext() {
return getServletConfig().getServletContext();
}
/**
* 默认信息是空字符串
*/
@Override
public String getServletInfo() {
return "";
}
@Override
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
// NOOP by default
}
/**
* 在servlet的log file中记录日志信息
*/
public void log(String msg) {
getServletContext().log(getServletName() + ": " + msg);
}
public void log(String message, Throwable t) {
getServletContext().log(getServletName() + ": " + message, t);
}
@Override
public abstract void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
@Override
public String getServletName() {
return config.getServletName();
}
}
GenerticServlet抽象类实际没做什么具体的工作,只是提供了一些便捷函数获取上下文、servlet参数以及进行日志记录。仍然没看见Request和Response的出现,这会在它的子类中提及吗?让我们来看看它的子类HttpServlet:
public abstract class HttpServlet extends GenericServlet {
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 final ResourceBundle lStrings =
ResourceBundle.getBundle(LSTRING_FILE);
public HttpServlet() {
// NOOP
}
/**
* 处理HTTP GET请求,需要用户实现具体逻辑,否则会返回HTTP ERROR信息
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
//获取协议
String protocol = req.getProtocol();
//获取http.method_get_not_supported的国际化字符串
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
//如果是HTTP/1.1,返回405禁止访问方法错误
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
//如果不是HTTP/1.1,返回400错误的请求错误
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
/**
* 获取最后被修改时间
* @param req
* @return
*/
protected long getLastModified(HttpServletRequest req) {
return -1;
}
/**
* 处理HTTP HEAD请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void doHead(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (DispatcherType.INCLUDE.equals(req.getDispatcherType())) {
//如果DispatcherType是INCLUDE则当成GET请求处理
doGet(req, resp);
} else {
//否则封装不带BODY信息的response返回给客户端
NoBodyResponse response = new NoBodyResponse(resp);
doGet(req, response);
response.setContentLength();
}
}
/**
* 处理HTTP POST请求,@see doGet
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
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(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
/**
* 处理HTTP PUT请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
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(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
/**
* 处理HTTP DELETE请求
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
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(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}
/**
* 获取除了javax.servlet.http.HttpServlet以外的任意类c的所有声明方法
* @param c
* @return
*/
private static Method[] getAllDeclaredMethods(Class<?> c) {
if (c.equals(javax.servlet.http.HttpServlet.class)) {
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;
}
/**
* 处理HTTP OPTIONS请求
*/
protected void doOptions(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, IOException {
Method[] methods = getAllDeclaredMethods(this.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=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_OPTIONS)
if (allow==null) allow=METHOD_OPTIONS;
else allow += ", " + METHOD_OPTIONS;
resp.setHeader("Allow", allow);
}
/**
* 处理HTTP TRACE请求
*/
protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
int responseLength;
String CRLF = "\r\n";
StringBuilder buffer = new StringBuilder("TRACE ").append(req.getRequestURI())
.append(" ").append(req.getProtocol());
Enumeration<String> reqHeaderEnum = req.getHeaderNames();
while( reqHeaderEnum.hasMoreElements() ) {
String headerName = reqHeaderEnum.nextElement();
buffer.append(CRLF).append(headerName).append(": ")
.append(req.getHeader(headerName));
}
buffer.append(CRLF);
responseLength = buffer.length();
resp.setContentType("message/http");
resp.setContentLength(responseLength);
ServletOutputStream out = resp.getOutputStream();
out.print(buffer.toString());
out.close();
return;
}
/**
* 处理HTTP REQUEST的实际调用函数,通过它再进一步的调用doGet、doPost方法
* @param req
* @param resp
* @throws ServletException
* @throws IOException
*/
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
//获取http request的method参数,其实就是html的form标签
//中method属性对应的字符串
String method = req.getMethod();
//如果是GET请求
if (method.equals(METHOD_GET)) {
//获取最后被修改时间
long lastModified = getLastModified(req);
if (lastModified == -1) {
/**如果servlet不支持http request header的if-modified-since属性
* 则继续处理
**/
doGet(req, resp);
} else {
//如果支持这个属性
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
/**
* 如果客户端的文件最后修改时间和服务器端的文件最后修改时间一致则返回304不需要修改状态
* 这样服务器就不返回html,浏览器读取本地缓存文件,否则重新获取服务器端的对应html文件
*/
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
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 {
//如果HTTP HEADER的method属性超出了get、head、post、put、delete、
//options、trace范围则返回501不支持该请求错误
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);
}
}
private void maybeSetLastModified(HttpServletResponse resp,
long lastModified) {
if (resp.containsHeader(HEADER_LASTMOD))
return;
if (lastModified >= 0)
resp.setDateHeader(HEADER_LASTMOD, lastModified);
}
/**
* 覆盖实现父类的service方法,如果开发者继承该类没必要实现该方法,只需要实现protected
* 的service方法
*/
@Override
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);
}
}
HttpServlet开始就有了doService的具体实现细节了,它使浏览器的本地缓存文件的功能真正的开始发挥作用,提高了HTTP访问效率。doService的具体实现细节需要由Service