Java Servlet源码走读一:Servlet接口
servlet:Server Applet 服务端应用程序,是java web开发的基础
首先需要理解web容器的概念:
- web容器:web容器是一种服务程序,在服务器一个端口就有一个提供相应服务的程序,而这个程序就是处理从客户端发出的请求,比如tomcat、webSphere、weblogic
Servlet源码结构
Servlet的源码主要包括2个package:
1.在javax.servlet包中定义了所有的Servlet类都必须实现或扩展的的通用接口和类。
2.在javax.servlet.http包中定义了采用HTTP通信协议的HttpServlet类。
Servlet接口
javax.servlet.Servlet
是核心接口,见Servlet
接口注释的第一句:定义了所有servlet都要实现的方法
Servlet接口中定义的方法:
- init:只会调用一次,在创建servlet时由web容器调用
- getServletConfig:返回
ServletConfig
对象,包括servlet的初始化和启动参数信息 - service:web容器调用service()来处理来自客户端的请求,
service()
会根据请求类型,在适当的时候调用doGet
、doPost
等方法 - getServletInfo:见注释说明:返回作者、版本、版权等servlet信息
- destroy:
只会调用一次,在 Servlet 生命周期结束时被调用。destroy()
可以让 Servlet 关闭数据库连接、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。在调用destroy()
方法之后,servlet 对象被标记为垃圾回收
Servlet的生命周期
Servlet 通过调用 init ()
方法进行初始化。
Servlet 调用 service()
方法来处理客户端的请求。
Servlet 通过调用 destroy()
方法终止(结束)。
最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
ServletContext/ServletRequest/ServletResponse
3个接口,接口的主要注释分别如下
/**
* Defines a set of methods that a servlet uses to communicate with its
* servlet container, for example, to get the MIME type of a file, dispatch
* requests, or write to a log file.
*/
public interface ServletContext {
}
/**
* Defines an object to provide client request information to a servlet. The
* servlet container creates a <code>ServletRequest</code> object and passes it
* as an argument to the servlet's <code>service</code> method.
*/
public interface ServletRequest {
}
/**
* Defines an object to assist a servlet in sending a response to the client.
* The servlet container creates a <code>ServletResponse</code> object and
* passes it as an argument to the servlet's <code>service</code> method.
*/
public interface ServletResponse {
}
从注释看:
1、ServletContext
定义了用来和servlet容器通信的方法(获取servlet容器的一些信息)
2、ServletRequest
由servlet容器创建,包含了客户端请求的信息
3、ServletResponse
由servlet容器创建,帮助servlet向客户端发送应答信息
HttpServlet
先看类图:
- GenericServlet:
GenericServlet中并没有实现destroy()
和service()
2个接口,而是留给了具体应用的servlet实现类去扩展
GenericServlet中对于init的实现:
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
具体的servlet实现类只需要重写init()
方法即可,而不需要重写init(ServletConfig)
以spring mvc为例,HttpServletBean
重写了GenericServlet
的init()
方法来实现自己独特的初始化逻辑
- HttpServlet
HttpServlet实现了service()接口
/**
* Receives standard HTTP requests from the public
* <code>service</code> method and dispatches
* them to the <code>do</code><i>XXX</i> methods defined in
* this class. This method is an HTTP-specific version of the
* {@link javax.servlet.Servlet#service} method. There's no
* need to override this method.
*
* @param req the {@link HttpServletRequest} object that
* contains the request the client made of
* the servlet
*
* @param resp the {@link HttpServletResponse} object that
* contains the response the servlet returns
* to the client
*
* @exception IOException if an input or output error occurs
* while the servlet is handling the
* HTTP request
*
* @exception ServletException if the HTTP request
* cannot be handled
*
* @see javax.servlet.Servlet#service
*/
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < lastModified) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
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 {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
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);
}
}
从源码可以看到,HttpServlet
在service()
方法中主要做的事情是根据request的method类型调用具体的doXXX()
方法进行处理,起到的是一个根据method类型分发的作用
对于更下面一层的实现类,需要重写HttpServlet
的doXXX()
方法来实现自己的特殊处理逻辑:
举例:spring mvc中FrameworkServlet
重写了HttpServlet
的doXXX()
方法来实现对Http请求的拦截
总结下:
1、Servlet
的重点在于定义了init(ServletConfig)
,destroy()
,service()
这3个接口
2、GenericServlet
重点在于对init(ServletConfig)
接口的重写和提供了init()
接口以供扩展
3、HttpServlet
重点在于service()
方法中的分发逻辑,以及定义了doGet
/doPost
等一系列接口以供扩展
具体的HttpServlet
实现类通过重写上面的一系列接口实现自己的独特逻辑
遗留问题:
web容器是在什么时候,如何去调用servlet定义的生命周期方法init(),service(),destroy()?