上面的servlet只是接口,相当于描述了servlet的标准,也就是说与协议无关。而HttpServlet的实现是根据Http协议来完成的。
打开源码看后发现大多数接口和抽象类,那么真正的实现都在哪呢?在servlet容器源代码里,即类似Tomcat这样的servlet容器。
所以我觉得可以这样理解Servlet的作用:它提供了操控的按钮,这些按钮一旦被安装到Servlet容器里面,我们就可以通过这些按钮操控servlet容器,从而管理我们自己的Servlet实例。
这一点很重要。所以阅读Servlet源码其实就是了解他的基本结构和API,我们真正要读的是Tomcat的源码!
Servlet的运行结构和API:
首先看下一次Http请求-响应的过程:
A,首先客户端通过浏览器向服务器发送Http请求;
B,Tomcat监听服务器的8080端口,当有Http请求发过来之后,解析出项目名称,然后到webapps目录下搜索到该项目文件夹。
C,Tomcat作为servlet的容器,实例化第一次请求调用的的servlet实例(以后再有相同的servlet的请求,使用第一次的实例)。
D,调用init()方法,初始化工作。
E,调用HttpServlet的service方法,中间会更加请求,调用doGet或doPost方法,执行核心逻辑代码。
F,执行完servlet后,返回响应,客户端浏览器根据响应呈现效果。
从图上可以看出,不同的用户请求并发使用同一个Servlet实例,这是一个比较重要的基础机制。
Container解析Http请求后,实例化Servlet,因为是Http请求,会执行HttpServlet的代码:
首先会执行HttpServlet中的service(ServletRequest req, ServletResponse res)方法:
- public void service(ServletRequest req, ServletResponse res)
- throws ServletException, IOException {
- HttpServletRequest request;
- HttpServletResponse response;
- try {
- request = (HttpServletRequest) req;//请求转换成Http请求
- response = (HttpServletResponse) res;//响应转换成Http响应
- } catch (ClassCastException e) {
- throw new ServletException("non-HTTP request or response");
- }
- service(request, response);//调用处理Http的service方法
- }
然后会调用service(HttpServletRequest req, HttpServletResponse resp)方法:
- protected void service(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- String method = req.getMethod();//取得req中的方法类型
- if (method.equals(METHOD_GET)) {//根据方法类型来决定执行后续的方法,我们只关注doGet和doPost
- long lastModified = getLastModified(req);
- if (lastModified == -1) {
- doGet(req, resp);
- } else {
- long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
- if (ifModifiedSince < (lastModified / 1000 * 1000)) {
- maybeSetLastModified(resp, lastModified);
- doGet(req, resp);//如此我们就能执行到自己写的doGet方法,从而执行自己的业务逻辑。
- } 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的方法类型,返回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);
- }
- }
我们自己实现的Servlet,我们只要继承HttpServlet,实现doGet就ok了:
- public class HelloWorld extends HttpServlet{
- private static final long serialVersionUID = 1L;
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse resp)
- throws ServletException, IOException {
- System.out.println("test");
- getServletContext().getRequestDispatcher("/jsp/test.html").forward(req, resp);
- }
- }
来关注下Servlet运行时需要注意的:
A,Servlet生命周期:
servlet类加载--->实例化--->服务--->销毁
B,其中牵涉到3个方法代表了Servlet的生命周期:
1、init方法:负责初始化Servlet对象。
2、service方法:负责响应客户的请求。
3、destroy方法:当Servlet对象退出生命周期时,负责释放占用的资源。
C,执行自己Servlet时需要用到的几个重要参数:
1,HttpSession:一次连结到客户端关闭,从客户端连接的层面来管理。
2,ServletConfig:从一个servlet被实例化后,对任何客户端在任何时候访问有效,但仅对本servlet 有效,一个servlet的ServletConfig对象不能被另一个servlet访问。 从单个servlet的层面来管理。
3,ServletContext:对任何servlet,任何人在任何时间都有效,这才是真正全局的对象。从全部的servlet的层面来管理
4,HttpServletRequest,HttpServletResponse:从单个servlet的单次响应侧面来管理