Servlet是什么?
Servlet是运行于服务器端,使用Java Servlet API 以及相关类和方法实现的Java程序。
为什么说Servlet独立于平台?
Java Servlet API 定义了一个 Servlet和Java使能服务器(Servlet 容器)之间的一个标准接口,这使得Servlet具有跨平台的特性。
为什么说Servlet与协议无关?
Servlet不对具体的协议实现,可以接受自定义协议,常用的WEB项目HttpServlet 是对HTTP协议的实现,我们可以像HttpServlet一样扩展GenericServlet 来实现FtpServlet,TelnetServlet等等。
为什么说Servlet相对高效?
Servlet在第一次被请求加载时调用init方法初始化一个Servlet,当后续的客户请求 servlet 服务时,Web 服务都将启动一个新的线程,而不是启动一个进程,这些线程由Servlet引擎服务器来管理,与传统的 CGI 为每个客户启动一个进程相比较,效率要高的多。
Servlet的生命周期
Servlet 的生命周期始于将它装入 Web 服务器的内存时,并在终止或重新装入 Servlet 时结束。Servlet的生命周期大致分三个阶段:
1. 初始化。
装入 Servlet 后,服务器创建一个 Servlet 实例并且调用 Servlet 的 init() 方法。在初始化阶段,Servlet 初始化参数(ServletConfig)传递给 Servlet 配置对象。 初始化的三个时机:
1.1 如果已配置自动装入选项,则在启动服务器时自动装入。
1.2 在服务器启动后,客户机首次向 Servlet 发出请求时。
1.3 重新装入Servlet时。
2. 请求处理
对于到达服务器的客户机请求,服务器创建特定于请求的一个“请求”对象和一个“响应”对象。服务器调用 Servlet 的 service() 方法,该方法用于传递“请求”和“响应”对象。service() 方法从“请求”对象获得请求信息、处理该请求并用“响应”对象的方法以将响应传回客户机。service() 方法可以调用其它方法来处理请求,例如 doGet()、doPost() 或其它的方法。
例如对于Http请求:
//HttpServlet中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);//看下方service()方法代码 }
HttpServlet中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 / 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); } }
3. 终止Servlet
当服务器不再需要 Servlet, 或重新装入 Servlet 的新实例时,服务器会调用 Servlet 的 destroy() 方法。
Java Servlet API简介
Java Servlet 开发工具(JSDK)提供了多个软件包,在编写 Servlet 时需要用到这些软件包。其中包括两个用于所有 Servlet 的基本软件包:javax.servlet 和 javax.servlet.http。javax.servlet包中的类都是抽象的,比较高层的,与协议无关的。下面主要介绍javax.servlet.http提供的HTTP Servlet应用编程接口。
HTTP Servlet 使用一个 HTML 表格来发送和接收数据。要创建一个 HTTP Servlet,需扩展 HttpServlet 类, 该类是用专门的方法来处理 HTML 表格的 GenericServlet 的一个子类。 HttpServlet 类包含 init()、destroy()、service() 等方法。其中 init() 和 destroy() 方法是继承的。
相关类图:
下面重点说几个常用且重要的方法:
1. init()方法
在 Servlet 的生命期中,仅执行一次 init() 方法。它是在服务器装入 Servlet 时执行的。 可以配置服务器,以在启动服务器或客户机首次访问 Servlet 时装入 Servlet。 无论有多少客户机访问 Servlet,都不会重复执行 init() 。
2. service()方法
service() 方法是 Servlet 的核心。每当一个客户请求一个HttpServlet 对象,该对象的service() 方法就要被调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。
3. destroy() 方法
destroy() 方法仅执行一次,即在服务器停止且卸装Servlet 时执行该方法。典型的,将 Servlet 作为服务器进程的一部分来关闭。缺省的 destroy() 方法通常是符合要求的,但也可以覆盖它,典型的是管理服务器端资源。
Servlet线程安全性问题
Servlet是非线程安全的。Servlet的线程安全问题主要是由于实例变量使用不当而引起。
Servlet体系结构是建立在Java多线程机制之上的,它的生命周期是由Web容器负责的。当客户端第一次请求某个Servlet时,Servlet容器将会根据web.xml配置文件实例化这个Servlet类。当有新的客户端请求该Servlet时,一般不会再实例化该Servlet类,也就是有多个线程在使用这个实例。Servlet容器会自动使用线程池等技术来支持系统的运行。
这样,当两个或多个线程同时访问同一个Servlet时,可能会发生多个线程同时访问同一资源的情况,数据可能会变得不一致。
线程安全问题主要是由实例变量造成的,因此:
1. 在Servlet中应避免使用实例变量。
2. 如果应用程序设计无法避免使用实例变量,那么使用同步来保护要使用的实例变量,但为保证系统的最佳性能,应该同步可用性最小的代码路径。