Servlet工作原理的二三事
前天有一个Q友说,让我讲讲Servlet的工作原理,一直没有时间,今天突然有点时间了,所以这就是写这篇博文的初意
1、首先呢,我们应该清楚,什么是Servlet,什么是Servlet容器
Servlet是server 和 Applet的总写
server服务端
Applet 应用程序
那么Servlet的意思就是:运行在服务端的java应用程序
这里需要注意的就是不是所有运行在服务端的java应用程序都是servlet
只有当这个java应用程序继承或实现了Servlet的API或接口才是,如我们在开发中常常用的HttpServlet接口
Servlet容器又是什么呢?
顾名思义,一个杯子是一个容器,一个箱子是一个容器,杯子里面可以盛水,可以放饮料,箱子里可以放衣服,也可以放玩具
我可以往箱子放玩具,也可以取出玩具,还可以扔掉,还可以换掉
所以Servlet容器就是一个管理Servlet应用程序的东西(程序)
2、然后我们现在就可以开始写一个Servlet程序了
我们都知道,一个Servlet类只需要继承HttpServlet抽象类
而事实上他也是继承了GenericServlet抽象类
而这个抽象类又实现了Servlet接口,上面我们说了,不是所有的java应用程序都是Servlet,只有这个java应用程序实现了Servler接口才是
GenericServlet他是和协议无关的servlet,是一种通用版本。
我们在开发的时候是实现HttpServlet抽象类,这个抽象类是对GenericServlet抽象类进一步包装,和Http协议有关的
就是说您可以根据您自己的需要重写写一个抽象类继承GenericServlet,不一定要用HttpServlet
请看以下代码,是一个简单的Servlet类
public class servletA extends HttpServlet{ public void doGet(HttpServletRequest request,HttpServletResponse response) throws IOException{ doPost(request,response); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws IOException{ HttpSession session=request.getSession(true); //如果客户端已经关联了session 那么我们将得到这个session,如果没有,就创建一个并且关联 String sessionid=session.getId(); //获得这个session的id session.setAttribute("name", "accp"); //设置绑定session session.setAttribute("sessionid", sessionid); session.setMaxInactiveInterval(1*20); response.sendRedirect("servletB"); } public void init(ServletConfig config)throws ServletException{ System.out.println(config.getServletName()); System.out.println(config.getServletContext().getContextPath()); } }
您无需关心方法体内的一些东西,您只需要知道,如果请求方法是get,就会执行doGet方法,doGet方法转交给doPost方法来执行
那么他是怎么知道get还四post请求呢
这里我们就要说说底层Servlet接口最主要的三个方法,一共五个
2、1:init()
这个方法是servlet装载到serlvet容器中时的一个初始方法,在这个容器的生命周期只运行一次,只要你启动了服务,不管你运行了多少次Servlet程序,他都只运行一次,这个方法需要一个参数javax.servlet.ServletConfig ,在上面的代码中,我重写了init方法,兵器写入这个参数,并且打印出Servlet的名字和项目的名字。
大家可以测试下,不管您运行多少次这个Servlet,这两句代码始终是运行了一次。
2、2:service()
这个是应用程序的服务方法,比如逻辑运算等,在这个方法里就定义了各种请求,如post,get,put等,他是运行在容器中
2、3:destroy()
这个方法的作用就是销毁——服务器停止并且卸载servlet的时候由servlet容器调用一次
2、4:public String getServletInfo();
2、5:public ServletConfig getServletConfig();
好了,继续上面的,在GenericServlet抽象类中,对service接口方法中的四个都进行了默认的实现,只有一个service方法没有实现
请看源代码片段
public abstract class demo implements Servlet, ServletConfig, java.io.Serializable { public ServletConfig getServletConfig() { return config; } public String getServletInfo() { return ""; } public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); } public abstract void service(ServletRequest req, ServletResponse res)throws ServletException, IOException; public void destroy() { } }以上为代码片段
刚刚我们说了,GenericServlet他是和协议无关的servlet,是一种通用版本。
我们自己可以开发一种与协议有关的servlet
当然,java已经为我们写好了一种与http协议有关的servlet,那就是HttpServlet,就是我们在开发中所继承的抽象类,上面有提到
这个类他继承了GenericServlet 抽象类,请看他的类定义
public abstract class HttpServlet extends GenericServlet implements java.io.Serializable在这个类里,他就定义了如doGet,doPost,doDelete,doOptions,doTrace等方法
而每一个方法都有两个参数,我们以doPost方法为例说明:
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); } }
他的参数是HttpServletRequest 和 HttpServletResponse,注意和service接口与重载方法对比,重载方法的参数就是我们这里的参数,而接口方法不是的,请看下面的代码
接下来是实现接口的service方法,和重载的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 { 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 / 1000 * 1000)) { // 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); } }
上面的重载的service方法和doGet等方法都是protected
我们可以看到,实现的接口方法service中,关于参数的处理,就是一个强制转换,以便给重载的service方法使用,就是说,这里我们可以有两种方式来处理了:
第一:按照源码来,我们只需要继承这个HttpServlet抽象类,重新实现doPost等方法
第二,可是直接重写service接口,注意是接口,而不是重载的接口方法
第三,因为主要是service方法嘛
所以回到我们的示例代码中,我们重写了doGet和doPost方法也就好理解了。
3、它是怎么找到我们的Servlet的
对,就是web.xml配置文件,在Servlet容器启动的时候,他就会加载web.xml配置文件
而web.xml配置文件里就定义了我们的Servet类的位置以及URL,如以下代码片段
<servlet> <servlet-name>servletB</servlet-name> <servlet-class>com.servlet.servletB</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>servletB</servlet-name> <url-pattern>/servletB</url-pattern> </servlet-mapping>其中<load-on-startup>1</load-on-startup>中间,如果是整数,表示优先加载级别,如果是 -1 ,表示只有当启动这个servlet的时候才加载,大家可以自行测试
INFO: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time. servletB 七月 15, 2016 5:03:11 下午 org.apache.catalina.startup.HostConfig deployDescriptor上面代码的servletB,就是在启动的时候输出的servlet的名字
所以当我们运行 servletB这个servlet的时候,Servlet容器就会加载他的配置文件,配置文件告诉我们,原来他的位置是在com.servlet.servletB这里
然后就会运行service这个接口方法,然后这个方法就会去调用他的默认的重载方法
然后这个重载方法就相当于是一个分发器,根据不同请求,去调用相应的方法,如doPost,或doGet
doPost和doGet一般都是由我们自己来实现
好了,本文主要是介绍Servlet的工作原理,大概就是这样吧,如有疑问,欢迎留言哈!
另外分享Servlet的源码下载,以帮助了解Servlet
爆款云服务器s6 2核4G 低至0.46/天,具体规则查看活动详情