Servlet 是什么?
Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。
使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
Java Servlet 通常情况下与使用 CGI(Common Gateway Interface,公共网关接口)实现的程序可以达到异曲同工的效果。但是相比于 CGI,Servlet 有以下几点优势:
- 性能明显更好。
- Servlet 在 Web 服务器的地址空间内执行。这样它就没有必要再创建一个单独的进程来处理每个客户端请求。
- Servlet 是独立于平台的,因为它们是用 Java 编写的。
- 服务器上的 Java 安全管理器执行了一系列限制,以保护服务器计算机上的资源。因此,Servlet 是可信的。
- Java 类库的全部功能对 Servlet 来说都是可用的。它可以通过 sockets 和 RMI 机制与 applets、数据库或其他软件进行交互。
Servlet 架构
下图显示了 Servlet 在 Web 应用程序中的位置。
Servlet 任务
Servlet 执行以下主要任务:
- 读取客户端(浏览器)发送的显式的数据。这包括网页上的 HTML 表单,或者也可以是来自 applet 或自定义的 HTTP 客户端程序的表单。
- 读取客户端(浏览器)发送的隐式的 HTTP 请求数据。这包括 cookies、媒体类型和浏览器能理解的压缩格式等等。
- 处理数据并生成结果。这个过程可能需要访问数据库,执行 RMI 或 CORBA 调用,调用 Web 服务,或者直接计算得出对应的响应。
- 发送显式的数据(即文档)到客户端(浏览器)。该文档的格式可以是多种多样的,包括文本文件(HTML 或 XML)、二进制文件(GIF 图像)、Excel 等。
- 发送隐式的 HTTP 响应到客户端(浏览器)。这包括告诉浏览器或其他客户端被返回的文档类型(例如 HTML),设置 cookies 和缓存参数,以及其他类似的任务。
servlet容器
Servlet容器(servlet引擎)是Web服务器或应用程序服务器的一部分,用于在发送的请求和响应之上提供网络服务,解码基于 MIME的请求,格式化基于MIME的响应。Servlet容器在servlet生命周期内管理的servlet对象。根据Servlet容器工作模式的不同,可以将Servlet容器分为以下三类:
(1) 独立的Servlet容器:
基于java技术的Web服务器:例如tomcat
(2) 进程内的Servlet容器:
Servlet容器由Web服务器插件和Java容器两部分的实现组成。Web服务器插件在某个Web服务器内部地址空间中打开一个 JVM(Java虚拟机),使得Java容器可以在此JVM中加载并运行Servlet。如有客户端调用Servlet的请求到来,插件取得对此请求的控 制并将它传递(使用JNI技术)给Java容器,然后由Java容器将此请求交由Servlet进行处理。进程内的Servlet容器对于单进程、多线程 的服务器非常适合,提供了较高的运行速度,但伸缩性有所不足。例如运行在Apache Web服务器内的Tomcat
(3) 进程外的Servlet容器:
Servlet容器运行于Web服务器之外的地址空间,它也是由Web服务器插件和Java容器两部分的实现组成的。Web服务器插件和Java容 器(在外部JVM中运行)使用IPC机制(通常是TCP/IP)进行通信。当一个调用Servlet的请求到达时,插件取得对此请求的控制并将其传递(使 用IPC机制)给Java容器。进程外Servlet容器对客户请求的响应速度不如进程内的Servlet容器,但进程外容器具有更好的伸缩性和稳定性。
Servlet 生命周期
Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:
- Servlet 通过调用 init () 方法进行初始化。
- Servlet 调用 service() 方法来处理客户端的请求。
- Servlet 通过调用 destroy() 方法终止(结束)。
- 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
现在让我们详细讨论生命周期的方法。
init() 方法
init 方法被设计成只调用一次。它在第一次创建 Servlet 时被调用,在后续每次用户请求时不再调用。因此,它是用于一次性初始化,就像 Applet 的 init 方法一样。
Servlet 创建于用户第一次调用对应于该 Servlet 的 URL 时,但是您也可以指定 Servlet 在服务器第一次启动时被加载。
当用户调用一个 Servlet 时,就会创建一个 Servlet 实例,每一个用户请求都会产生一个新的线程,适当的时候移交给 doGet 或 doPost 方法。init() 方法简单地创建或加载一些数据,这些数据将被用于 Servlet 的整个生命周期。
init 方法的定义如下:
public void init() throws ServletException { // 初始化代码... }
service() 方法
service() 方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service() 方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。
每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service() 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet、doPost、doPut,doDelete 等方法。
下面是该方法的特征:
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException{ }
service() 方法由容器调用,service 方法在适当的时候调用 doGet、doPost、doPut、doDelete 等方法。所以,您不用对 service() 方法做任何动作,您只需要根据来自客户端的请求类型来重写 doGet() 或 doPost() 即可。
doGet() 和 doPost() 方法是每次服务请求中最常用的方法。下面是这两种方法的特征。
doGet() 方法
GET 请求来自于一个 URL 的正常请求,或者来自于一个未指定 METHOD 的 HTML 表单,它由 doGet() 方法处理。
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Servlet 代码 }
doPost() 方法
POST 请求来自于一个特别指定了 METHOD 为 POST 的 HTML 表单,它由 doPost() 方法处理。
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Servlet 代码 }
destroy() 方法
destroy() 方法只会被调用一次,在 Servlet 生命周期结束时被调用。destroy() 方法可以让您的 Servlet 关闭数据库连接、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。
在调用 destroy() 方法之后,servlet 对象被标记为垃圾回收。destroy 方法定义如下所示:
public void destroy() { // 终止化代码... }
架构图
下图显示了一个典型的 Servlet 生命周期方案。
- 第一个到达服务器的 HTTP 请求被委派到 Servlet 容器。
- Servlet 容器在调用 service() 方法之前加载 Servlet。
- 然后 Servlet 容器处理由多个线程产生的多个请求,每个线程执行一个单一的 Servlet 实例的 service() 方法。
创建servlet的三种方式
- 实现servlet接口 implements Servlet
- 继承GenericServlet 类 extends GenericServlet 并实现service()方法
- 继承HttpServlet 方法 extends HttpServlet
servlet接口;
package javax.servlet; import java.io.IOException; public interface Servlet { void init(ServletConfig var1) throws ServletException;//用于servlet初始化 ServletConfig getServletConfig();//返回ServletConfig实例,其中包含Servlet初始化和启动参数 void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;//用于服务处理 String getServletInfo();//返回servlet的作者、版本、版权等信息 void destroy();//销毁servlet }
GenericServlet类:
是一个抽象类、实现Servlet接口、ServletConfig接口、Serializable接口,实现了Service()并声明成抽象方法,子类必须实现。
package javax.servlet; import java.io.IOException; import java.io.Serializable; import java.util.Enumeration; public abstract class GenericServlet implements Servlet, ServletConfig, Serializable { private static final long serialVersionUID = 1L; private transient ServletConfig config; public GenericServlet() { } public void destroy() { } public String getInitParameter(String name) { return this.getServletConfig().getInitParameter(name); } public Enumeration<String> getInitParameterNames() { return this.getServletConfig().getInitParameterNames(); } public ServletConfig getServletConfig() { return this.config; } public ServletContext getServletContext() { return this.getServletConfig().getServletContext(); } public String getServletInfo() { return ""; } public void init(ServletConfig config) throws ServletException { this.config = config; this.init(); } public void init() throws ServletException { } public void log(String msg) { this.getServletContext().log(this.getServletName() + ": " + msg); } public void log(String message, Throwable t) { this.getServletContext().log(this.getServletName() + ": " + message, t); } public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException; public String getServletName() { return this.config.getServletName(); } }
HttpServlet类:
是一个抽象类,继承了抽象类GenericServlet,使用了HttpServletRequest 和 HttpServletResponse进行封装
abstract class HttpServlet extends GenericServlet{ //HttpServlet中的service() protected void service(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse){ //该方法通过httpServletRequest.getMethod()判断请求类型调用doGet() doPost() } // HttpServletRequest接口:提供基于HTTP协议的请求视图。该接口中定义的方法用于获取HTTP请求头信息。Cookie信息等。 // HttpServletResponse接口:提供基于HTTP协议的响应视图。该接口中定义的方法用于设置HTTP请求头信息。Cookie信息等 //必须实现父类的service()方法 public void service(ServletRequest servletRequest,ServletResponse servletResponse){ HttpServletRequest request; HttpServletResponse response; try{ request=(HttpServletRequest)servletRequest; response=(HttpServletResponse)servletResponse; }catch(ClassCastException){ throw new ServletException("non-http request or response"); } //调用service()方法 this.service(request,response); } }
Servlet 部署
通过web.xml来部署:Servlet01
<web-app>
<servlet>
<servlet-name>Servlet01</servlet-name>
#完全限定名=包名+类名
<servlet-class>cn.xy.servlet.Servlet01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Servlet01</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
通过注解方式来部署Servlet01
@WebServlet(name = "Servlet01",urlPatterns = {"/hello"})
或者
@WebServlet("/hello")
servlet 浏览器访问路径配置有个小问题:
1、java 类里的注解 —— @WebServlet("/HelloServlet") 对应浏览器路径:
http://localhost:8080/TomcatTest/HelloServlet
2、配置文件(web.xml)里对应的浏览器访问路径:
http://localhost:8080/TomcatTest/TomcatTest/HelloServlet
这两种配一个就好了,不然路径重名的话反而会让tomcat启动不了。
例如这样就启动不了:
修改 web.xml :
<url-pattern>/HelloServlet</url-pattern>
修改后,web.xml 和 java 类的注解,对应路径都是:
http://localhost:8080/TomcatTest/HelloServlet
导致
命名的 servlet[HelloServlet]和 [com.runoob.test.HelloServlet] 都被映射到 URL 模式 [/ HelloServlet] 这是不允许的。
解决方法
将注解去掉或者保留注解进入web.xml将映射删除既可以。
Servlet 映射的类型和顺序:
1、 精确映射 /test/index.jsp 请求路径必须是/test/index.jsp
2、 路径映射 /test/* 请求路径可能是/test/index.html或者/test/index.jsp
3、 扩展映射 *.jsp 请求路径后缀带.jsp都行
4、 默认映射 如果无法匹配的Servlet,则将请求转发给默认的Servlet;如果没有默认的Servlet,则返回404
初始化参数:
<web-app> <context-param> <!--全局初始化参数--> <param-name>name</param-name> <param-value>ayao</param-value> </context-param> </web-app> 通过ServletContext的getInitParamter()来获取全局初始化参数 <web-app> <servlet> <servlet-name>Servlet01</servlet-name> <servlet-class>cn.xy.servlet.Servlet01</servlet-class> <init-param> <!--局部servlet初始化参数--> <param-name>name</param-name> <param-value>ayao</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>Servlet01</servlet-name> <url-pattern>/hello</url-pattern> </servlet-mapping> </web-app> 通过ServletConfig接口中的getInitParamter()在init()方法中获取初始化参数