Servlet 简介

狭义上的 Servlet 是一种接口,如果开发者想要开发一个动态的 Web 资源,只需要:

  1. 编写一个实现了 Servlet 接口的类。

  2. 把开发好的 Java 类部署到服务器中。

广义上来讲,我们就把实现了 Servlet 接口的程序也称之为一个 Servlet。

Servlet 生命周期

当服务器接收到了某个 Servlet 程序的请求的时候,会执行以下过程:

  1. 服务器会先判断是否拥有当前 Servlet 实例,如果有,跳到第四步。

  2. 调用 Servlet 的构造方法实例化 Servlet 获取对象。

  3. 调用 Servlet 实例的 init() 方法来初始化此对象。

  4. 创建此 HTTP 请求的 ServletRequest 和 ServletResponse 实例,并传入调用的 service(ServletRequest,ServletResponse) 方法。

  5. 当 Web 程序停止/重启,会调用对应实例的 destroy() 方法

创建 Servlet

我们在创建一个 Servlet 类的时候,一般会直接继承 HttpServlet ,里面提供了很多强大的功能,它已经写好了 service() 方法,并对请求分流到 doGet() 和 doPost() 方法。所以我们只需要重写这两个方法来分别处理 Get 和 Post 请求即可。

创建 Servlet 之后,需要进行 Servlet-URL 进行映射,来确定在访问哪些 URL 的时候,让对应的 Servlet 去处理。这里有两种方法,一种是在 IDEA 创建 Servlet 的 Java 文件的时候,勾选 Create Java EE 6+ Annotated class 选项使用注解来注册 Servlet ,然后生成的 Servlet 代码的类前面会有注解: @WebServlet(name = "XXXX", value = "XXXX") name 表示 Servlet 服务名称,value 表示对应映射的 URL。

或者不勾选哪个选项,然后自己在 web.xml 配置文件中添加 servlet 信息:

1
2
3
4
5
6
7
8
<servlet> <!-- 这里注册 servlet -->
<servlet-name>MyServletName</servlet-name> <!-- 这里填写自己设置的 Servlet 名称 -->
<servlet-class>space.xorex.WebModule.MyServlet</servlet-class> <!-- 这里填写 Servlet 类的完整类名 -->
</servlet>
<servlet-mapping> <!-- 这里映射 servlet -->
<servlet-name>NewServlet</servlet-name> <!-- 填写 servlet 名称,要和上面注册的相同 -->
<url-pattern>/New</url-pattern> <!-- 这里填写映射的 URL -->
</servlet-mapping>

这里创建映射的时候,可以使用通配符 * ,比如 /*/XXXX/*.txt 等等。而对于一个路径有多个 Servlet 对应,那么路径映射返回越大的 Servlet 优先级越低。


如果有路径映射为 / 的 Servlet,那么这个 Servlet 就会成为默认 Servlet,所有找不到路径映射的访问都会指向这个 Servlet 去处理。

Servlet 线程安全

需要注意,一个 Servlet 程序在运行中只会创建一个实例,所以面对并发访问的时候,Tomcat 会创建新的线程去运行 service() 方法,但所有线程运行的 service() 都是属于同一个 Servlet 对象的。所以一旦涉及到非 service() 方法本地的访问与调用,就有可能牵扯到线程安全问题。

所以一定要小心 service() doGet() doPost() 方法所有的外部调用,避免出现线程安全问题,

ServletConfig

在 web.xml 文件的 servlet 标签下面可以设置此 Servlet 程序的初始化配置参数 <init-param>

1
2
3
4
5
6
7
8
9
10
<servlet>
<init-param>
<param-name>Tempest</param-name>
<param-value>Xorex</param-value>
</init-param>
<init-param>
<param-name>Yukinoshita</param-name>
<param-value>Yukino</param-value>
</init-param>
</servlet>

然后可以调用 父类 GenericServlet 的方法 getServletConfig() 获取一个 ServletConfig 实例,此实例的 getInitParameterNames() 和 getInitParameter() 可以获取 Servlet 配置里面的初始化参数。

1
2
3
4
5
6
7
8
public void showInitPara() {
ServletConfig config=getServletConfig();
Enumeration<String> paras = config.getInitParameterNames();
while(paras.hasMoreElements()) {
String name=paras.nextElement();
System.out.println(name + " : " + config.getInitParameter(name));
}
}

ServletContext

概念

ServletContext 对象是所有的 Web 应用都会有的一个单列实例,这个单列实例可以被这个 Web 应用的所有 Servlet 访问并修改内容,我们可以通过这个单列实例完成很多事情。

获取 Web 应用的初始化参数

和 ServletConfig 可以访问到 Servlet 的配置信息 <init-param> 一样,对于 Web 应用级别的 ServletContext 对象可以访问 Web 应用的配置信息 <context-param>,这个标签在 web.xml 中和 <servlet> 同级别。我们可以设置一些这样的 Context 属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<web-app>
<context-param>
<param-name>Tempest</param-name>
<param-value>Xorex</param-value>
</context-param>
<context-param>
<param-name>Yukinoshita</param-name>
<param-value>Yukino</param-value>
</context-param>

<servlet>
....
</servlet>
</web-app>

获取 ServletContext 除了调用父类的 getServletContext() 以外,还可以调用 ServletConfig 实例的 getServletContext() 方法(因为它内部封装了 ServletContext 的实例,可以调用出来)。然后剩下的步骤和 ServletConfig 读取初始化参数一样了。

1
2
3
4
5
6
7
8
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext context = getServletConfig().getServletContext();
Enumeration<String> iter = context.getInitParameterNames();
while(iter.hasMoreElements()) {
String name=iter.nextElement();
System.out.println(name+" : "+context.getInitParameter(name));
}
}

Servlet 数据共享

因为 ServletContext 是所有 Servlet 都能能访问的单列实例,所以就可以利用这个实例才存储一些数据,从而使不同的 Servlet 之间可以共享这些数据。

共享的方法就是将数据存储到实例的属性值中,使用方法 setAttribute(Name,Value) 和 getAttribute(Name) 来存取数据,实现不同 Servlet 之间的交流。

获取工程的绝对路径

使用 ServletContext 的 getReadPath(String) 来返回路径字符串,其中参数填写相对路径即可,比如 "./" ,然后返回项目在电脑中的绝对路径。

Servlet 请求转发

请求转发使用的是一个请求调度器 RequestDispatcher 里面的 forward(resquest,response) 可以将信息转发。而获取请求调度器的方法有几种:

  1. 通过 ServletContext 的 getRequestDispatcher(String) 获取

  2. 通过请求 HttpServletRequest 的 getRequestDispatcher(String) 获取

其中参数为目标 Servlet 的映射地址。

获取 RequestDispatcher 之后,使用 forward() 转发请求即可:

1
2
3
4
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
RequestDispatcher dispatcher=request.getRequestDispatcher("/hello-servlet"); // 把 /New 的请求转发给 /hello-servlet
dispatcher.forward(request, response);
}