JavaWeb Servlet浅析
1、Servlet简介
Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。使用 Servlet,可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。
Servlet是sun公司提供的一门用于开发动态web资源的技术。
Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:
1、编写一个Java类,实现servlet接口。
2、把开发好的Java类部署到web服务器中。
按照一种约定俗成的称呼习惯,通常我们也把实现了servlet接口的java程序,称之为Servlet。
2、Servlet调用机制
Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:
①Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。
②装载并创建该Servlet的一个实例对象。
③调用Servlet实例对象的init()方法。
④创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
⑤WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
3、Servlet 生命周期
Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:
- Servlet 初始化后调用 init () 方法。init方法被设计成只调用一次。它在第一次创建Servlet时被调用,在后续每次用户请求时不再调用。因此,它是用于一次性初始化,就像Applet的init方法一样。init() 方法简单地创建或加载一些数据,这些数据将被用于 Servlet 的整个生命周期。
- Servlet 调用 service() 方法来处理客户端的请求。service() 方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service() 方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service() 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet、doPost、doPut,doDelete 等方法。service() 方法由容器调用,service 方法在适当的时候调用 doGet、doPost、doPut、doDelete 等方法。所以,自定义servlet时,不用对 service() 方法做任何动作,只需要根据来自客户端的请求类型来重写 doGet() 或 doPost() 即可。
- Servlet 销毁前调用 destroy() 方法。destroy() 方法只会被调用一次,在 Servlet 生命周期结束时被调用。destroy() 方法常用来关闭数据库连接、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动。在调用 destroy() 方法之后,servlet 对象被标记为垃圾回收。
- 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
4、servlet初始化时机
Servlet 创建于用户第一次调用对应于该 Servlet 的 URL 时,但是也可以指定 Servlet 在服务器第一次启动时被加载。
4.1、默认情况下第一次请求servlet对象时初始化;
4.2、可以通过load-on-startup 节点( load-on-startup属性值只能为整数 ) 来指定Servlet启动顺序:
- 当节点值为负数 (相当于不写时) , 表示当用户第一次请求时, 创建此Servlet ;
- 当节点值大于0或等于0时, 在服务器启动时, 创建此Servlet 。创建的顺序是: 值越小越早创建,值为0时最早创建 ;值相同时, 自上而下顺序创建
5、Servlet 任务
Servlet 执行以下主要任务:
- 读取客户端(浏览器)发送的显式的数据。这包括网页上的 HTML 表单,或者也可以是来自 applet 或自定义的 HTTP 客户端程序的表单。
- 读取客户端(浏览器)发送的隐式的 HTTP 请求数据。这包括 cookies、媒体类型和浏览器能理解的压缩格式等等。
- 处理数据并生成结果。这个过程可能需要访问数据库,执行 RMI 或 CORBA 调用,调用 Web 服务,或者直接计算得出对应的响应。
- 发送显式的数据(即文档)到客户端(浏览器)。该文档的格式可以是多种多样的,包括文本文件(HTML 或 XML)、二进制文件(GIF 图像)、Excel 等。
- 发送隐式的 HTTP 响应到客户端(浏览器)。这包括告诉浏览器或其他客户端被返回的文档类型(例如 HTML),设置 cookies 和缓存参数,以及其他类似的任务。
6、Servlet实例
@WebServlet(name = "MyServlet", urlPatterns = {"/MyServlet", "/myServlet"})
public class MyServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println("----------doPost-----------");
doGet(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println("----------doGet-----------");
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.print(" This is ");
out.print(this.getClass());
out.println(", using the GET method");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
}

7、Servlet注册方式
7.1、web.xml方式(传统方式)
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>org.silence.servlet.MyServlet</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>zhangsan</param-value>
</init-param>
<display-name>MyServlet</display-name>
</servlet>
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/servlet</url-pattern>
</servlet-mapping>
7.2、@WebServlet注解方式(servlet3.0开始)
注意:Servlet3.0的项目服务器需要Tomcat7.0开始的版本才支持!!
@WebServlet(name = "ParamServlet",
urlPatterns = "/param",
initParams = {@WebInitParam(name = "name", value = "zhangsan"), @WebInitParam(name = "abc", value = "abc")})
public class ParamServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = getServletContext();
ServletConfig config = getServletConfig();
String name = config.getInitParameter("name");
response.getWriter().println(name);
Enumeration<String> parameterNames = config.getInitParameterNames();
while (parameterNames.hasMoreElements()) {
String element = parameterNames.nextElement();
String parameter = config.getInitParameter(element);
response.getWriter().println(element + " = " + parameter);
}
}
}
8、Servlet线程安全
在默认的情况下Servlet容器(web服务器)对声明的Servlet,只创建一个Servlet实例,当多个客户端并发访问同一个Servlet时,Servlet容器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。

例如下面的代码:
@WebServlet(name = "SynServlet", urlPatterns = "/syn")
public class SynServlet extends HttpServlet {
int i = 1;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// synchronized (this) {
i++;
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
response.getWriter().write("i: " + i);
// }
}
}
解决办法之一是给共享资源加锁。
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
synchronized (this) {
i++;
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
response.getWriter().write("i: " + i);
}
}

8.1、Servlet线程安全的解决方式
8.1.1、实现javax.servlet.SingleThreadModel接口(不推介)
javax.servlet.SingleThreadModel API及其翻译
Ensures that servlets handle only one request at a time. This interface has no methods.
确保servlet每次只处理一项请求。接口不含方法。
If a servlet implements this interface, you are guaranteed that no two threads will execute concurrently in the servlet's service method. The servlet container can make this guarantee by synchronizing access to a single instance of the servlet, or by maintaining a pool of servlet instances and dispatching each new request to a free servlet.
*如果servlet实现了该接口,会确保不会有两个线程同时执行servlet的service方法。 servlet容器通过同步化访问servlet的单实例来保证,也可以通过维持servlet的实例池,对于新的请求会分配给一个空闲的servlet。*
Note that SingleThreadModel does not solve all thread safety issues. For example, session attributes and static variables can still be accessed by multiple requests on multiple threads at the same time, even when SingleThreadModel servlets are used. It is recommended that a developer take other means to resolve those issues instead of implementing this interface, such as avoiding the usage of an instance variable or synchronizing the block of the code accessing those resources. This interface is deprecated in Servlet API version 2.4.
*注意:SingleThreadModel不会解决所有的线程安全隐患。*例如,会话属性和静态变量仍然可以被多线程的多请求同时访问,即便使用了SingleThreadModel servlet。
8.1.2、同步共享资源(推介)
使用synchronized或提供的锁来同步共享资源。

浙公网安备 33010602011771号