目录
一.什么是servlet?
Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。
- 创建Servlet
- 在类上使用@WebServlet注解,配置该Servlet的访问路径
- 继承HttpServlet
- 重写doPost()或doGet()方法
- 启动Tomcat,浏览器输入URL访问该Servlet
示例代码:
@WebServlet("/ServletTest")
public class ServletTest implements Servlet {
private static final long serialVersionUID = 1L;
@Override
public void init(ServletConfig arg0) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {
System.out.println("hello servlet");
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public ServletConfig getServletConfig() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getServletInfo() {
// TODO Auto-generated method stub
return null;
}
}

控制台:

Tomcat和servlet的关系:
Tomcat 是Web应用服务器,是Apache开源软件组织下的一个软件项目。Tomcat作为Web服务器,可以通过Http协议与客户端(浏览器)进行数据交互。同时也可以来发布Web应用。tomcat与Web应用是由不同的开发商开发的,那么这两种不同的软件系统是如何进行交互的呢。答案就是通过Servlet(最主要的接口)。Servlet是由Oracle公司指定的一个接口,用来规范Web服务器与Web应用之间的交互。Oracle制定了一系列交互相关接口以及对交互时的一些细节做了规定。这些接口以及规定统称为Servlet规范。Servlet规范把能够发布和运行Java Web应用的Web服务器称为Servlet容器(如Tomcat)。
Servlet 的优点:
-
方便:Servlet提供了大量的实用工具例程,如处理很难完成的HTML表单数据、读取和设置HTTP头,以及处理Cookie和跟踪会话等。
-
跨平台:Servlet用Java类编写,可以在不同操作系统平台和不同应用服务器平台下运行。
-
灵活性和可扩展性:采用Servlet开发的Web应用程序,由于Java类的继承性及构造函数等特点,使得应用灵活,可随意扩展。
-
Servlet具有功能强大、能够在各个程序之间共享数据、安全性强等特点。
二.servlet的体系结构

servlet接口:
- Servlet接口规定了必须由Servlet类实现并且由Servlet引擎识别和管理的方法集。
- Servlet接口的基本目标是提供与Servlet生命周期相关的方法,如:init()、service()和destroy()等。
ServletConfig接口:
- 一个 Web 应用中可以存在多个 ServletConfig 对象,一个 Servlet 只能对应一个 ServletConfig 对象,即 Servlet 的初始化参数仅对当前 Servlet 有效。
GenericServlet抽象类:
- GenericServlet是一个通用的协议无关的Servlet,它实现了Servlet和ServletConfig接口。
- GenericServlet继承自Servlet,应该重写service()方法。
httpServlet类:
- HttpServlet指能够处理HTTP请求的Servlet,它在原有Servlet接口上添加了对HTTP协议的处理,它比Servlet接口的功能更为强大。
三.servlet的主要任务
Servlet 执行以下主要任务:
- 读取客户端(浏览器)发送的显式的数据。这包括网页上的 HTML 表单,或者也可以是来自 applet 或自定义的 HTTP 客户端程序的表单。
- 读取客户端(浏览器)发送的隐式的 HTTP 请求数据。这包括 cookies、媒体类型和浏览器能理解的压缩格式等等。
- 处理数据并生成结果。这个过程可能需要访问数据库,执行 RMI 或 CORBA 调用,调用 Web 服务,或者直接计算得出对应的响应。
- 发送显式的数据(即文档)到客户端(浏览器)。该文档的格式可以是多种多样的,包括文本文件(HTML 或 XML)、二进制文件(GIF 图像)、Excel 等。
- 发送隐式的 HTTP 响应到客户端(浏览器)。这包括告诉浏览器或其他客户端被返回的文档类型(例如 HTML),设置 cookies 和缓存参数,以及其他类似的任务。
四.servlet的生命周期
生命周期就是Servlet什么时候创建,调用了何种方法,最后在什么时候被销毁。我们之前学过的对象都是自己手动创建的,最后由JVM来销毁,而Servlet的整个生命周期,都是由tomcat服务器来控制的。
servlet的生命周期具体步骤:
(1)Servlet对象创建:接收到浏览器请求后,才创建对象
(2)Servlet初始化创建对象之后,会调用init()方法作用:是在Servlet对象创建后,执行一些初始化操作
(3)Servlet处理客户端请求:接收到请求之后调用service()方法在每次接到请求后都会执行
(4)Servlet面临对象的销毁当前Web应用卸载之前调用destroy()方法在应用卸载之前,可能需要释放一些资源,关闭某些连接。
具体流程:
(1)init()方法
init()方法只会被调用一次,是在第一次访问(or 创建) Servlet 的时候被调用,在后续的每次请求时都不会再调用 init() 方法了, init()方法用于 Servlet 的初始化,可以简单地创建或加载一些数据,这些数据将被用于 Servlet 的整个生命周期。
实现init()方法(实现servlet接口就会实现servlet的抽象方法):
ServletConfig:代表的是配置信息。Servlet容器通过这个参数向Servlet传递配置信息。Servlet使用ServletConfig对象从Web应用程序的配置信息中获取以名-值对形式提供的初始化参数。另外,在Servlet中,还可以通过ServletConfig对象获取描述Servlet运行环境的ServletContext对象,使用该对象,Servlet可以和它的Servlet容器进行通信。
(2)service()方法
service()方法是Servlet的核心,是在servlet生命周期中的服务期,默认在HttpServlet类中实现,根据HTTP请求方法(GET、POST等),将请求分发到doGet、doPost等方法实现。
Servlet接口中定义了一个service()方法,而我们一般是使用HttpServlet,HttpServlet中对它进行了实现,将ServletRequest和ServletResponse转变成为HttpServletRequest和HttpServletResponse在service中实现。
一般来说service方法是不需要重写的,因为在HttpServlet中已经有了很好的实现,它会根据请求的方法名(GET,POST),调用doGet,doPos以及其他的doXXX方法,也就是说service是用来转向的,所以我们一般写一个servlet,只需要重写doGet或者doPost就可以了。
- Servlet中的service方法用于应答浏览器请求,每次请求都会调用该方法。
- ServletRequest:封装了请求信息,可以从中获取到任何的请求信息。
- ServletResponse:封装了响应信息,用于响应用户请求。
- 这两个接口都是服务器给予实现的,并在服务器调用service方法时传入。
(3)destory()方法
Java Servlet中destroy()的含义是,在容器决定销毁该Servlet之前,将立即执行内容。但是,如果您自己调用destroy()方法,则只执行内容,然后继续相应的过程。对于这个问题,destroy()被执行,然后servlet初始化完成。
首先调用destroy()方法,然后从容器中删除Servlet,然后最终进行垃圾回收。 destroy()方法通常包含用于释放将不会被垃圾收集的任何资源(如JDBC连接)的代码。
- 在servlet容器调用destroy方法之前,它必须允许servlet的service方法中当前正在运行的所有线程完成执行,或超过服务器定义的时间限制。
- 在Servlet实例上调用destroy方法后,容器可能不会将其他请求路由到该Servlet实例。
- 如果容器需要再次启用Servlet,则必须使用Servlet类的新实例来启用。
五.servlet的线程安全问题
Servlet是线程不安全的:这是单列模式导致的。
单例模式就是:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
当不同的请求都调用这一个servlet,单例模式只new ()一次,只在堆里申请了一个对象空间,即只实例化了一次。所以,不管来了多少请求,都是这一个实例在处理。如果多个请求在同一时刻出现,就会并发执行。
Servlet体系是建立在java多线程的基础之上的,它的生命周期是由Tomcat来维护的。当客户端第一次请求Servlet的时候,tomcat会根据web.xml配置文件实例化servlet,当又有一个客户端访问该servlet的时候,不会再实例化该servlet,也就是多个线程在使用这个实例。
多线程和单线程Servlet具体区别:多线程下每个线程对局部变量都会有自己的一份copy,这样对局部变量的修改只会影响到自己的copy而不会对别的线程产生影响,线程安全的。但是对于实例变量来说,由于servlet在Tomcat中是以单例模式存在的,所有的线程共享实例变量。多个线程对共享资源的访问就造成了线程不安全问题。
编写servlet线程安全的建议:
(1)用方法的局部变量保存请求中的专有数据。对方法中定义的局部变量,进入方法的每个线程都有自己的一份方法变量拷贝。任何线程都不会修改其他线程的局部变量。如果要在不同的请求之间共享数据,应该使用会话来共享这类数据。
(2)只用 Servlet的成员变量来存放那些不会改变的数据。有些数据在 Servlet 生命周期中不发生任何变化,通常是在初始时确定的,这些数据可以使用成员变量保存,如数据库连接名称、其他资源的路径等。
(3)对可能被请求修改的成员变量同步。有时数据成员变量或者环境属性可能被请求修改。当访问这些数据时应该对它们同步,以避免多个线程同时修改这些数据。
(4)如果 Servlet 访问外部资源,那么需要同步访问这些资源。例如,假设 Servlet 要从文件中读写数据。当一个线程读写一个文件时,其他线程也可能正在读写这个文件。文件访问本身不是线程安全的,所以必须编写同步访问这些资源的代码。在编写线程安全的 Servlet 时,下面两种方法是不应该使用的:
(5)在 Servlet API 中提供了一个 SingleThreadModel 接口,实现这个接口的 Servlet 在被多个客户请求时一个时刻只有一个线程运行。这个接口已被标记不推荐使用。
(6)对 doGet() 或doPost() 方法同步。如果必须在 Servlet 中使用同步代码,应尽量在最小的代码块范围上进行同步。同步代码越小,Servlet 执行得才越好。
六.总结
- service()是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。
- Servlet是线程不安全的,在Servlet类中可能会定义共享的类变量,这样在并发的多线程访问的情况下,不同的线程对成员变量的修改会引发错误。
- service()是用来转向的,所以我们一般写一个servlet,只需要重写doGet或者doPost就可以了。如果重写了service方法,那么servlet容器就会把请求交给这个方法来处理,倘若你重写的service方法没有调用doXXX,即使你在Servlet中又重写了其他doGet doPost等也是不回被调用的因为Servlet的service被自动调用。