抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

摘要:本文学习了Servlet的基本概念,以及如何使用Servlet技术开发Web应用。

环境

Windows 10 企业版 LTSC 21H2
Java 1.8
Tomcat 8.5.50

1 简介

Servlet(Server Applet,服务端应用程序)是运行在Web服务器或应用服务器上的Java程序,作为Web客户端(比如浏览器)请求与服务器上的数据库或应用程序之间的中间层。

Servlet程序是JavaWeb三大组件之一,其他两个是Listener监听器和Filter过滤器。

2 生命周期

2.1 创建

当Servlet首次被请求时,会调用init()方法创建Servlet实例。该方法只执行一次,用于加载资源和建立数据库连接等一次性设置。

在服务器启动时创建Servlet实例的两种方式:

  • web.xml配置文件中配置<load-on-startup>标签。
  • @WebServlet注解中配置loadOnStartup属性,只有Servlet版本在3.0以上才支持。

2.2 服务

当Servlet被请求时,会调用service()方法处理请求。该方法会被多次调用,用于处理用户请求。

根据请求类型不同,Servlet会自动调用相应的处理方法。

2.3 销毁

当Servlet不再被请求时,会调用destroy()方法销毁Servlet实例。该方法只执行一次,用于释放资源和关闭数据库连接等一次性清理。

3 技术体系

3.1 实现方式

3.1.1 Servlet接口

Servlet接口是最底层的接口,所有Servlet都必须实现该接口。

实现javax.servlet.Servlet接口是最基础的方式。

需要实现全部抽象方法:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class DemoServlet implements Servlet {
@Override
public void init(ServletConfig config) {
System.out.println("init ...");
}
@Override
public void service(ServletRequest req, ServletResponse res) {
System.out.println("service ...");
}
@Override
public void destroy() {
System.out.println("destroy ");
}
@Override
public ServletConfig getServletConfig() { return null; }
@Override
public String getServletInfo() { return null; }
}

3.1.2 GenericServlet类

GenericServlet类实现了Servlet类,并且实现大部分业务无关的方法,GenericServlet类的子类只需要实现service()方法。

继承GenericServlet类是最通用的方式,适用于非HTTP协议的场景。

简化接口实现,只需要实现service()方法:

java
1
2
3
4
5
6
public class DemoServlet extends GenericServlet {
@Override
public void service(ServletRequest req, ServletResponse res) {
System.out.println("service ...");
}
}

3.1.3 HttpServlet类

HttpServlet类继承了GenericServlet类,并且进一步实现了service()方法,根据请求方法调用HTTP专属的请求处理方法,子类只需要重写处理方法。

继承HttpServlet类是最常用的方式,适用于HTTP协议的场景。

只需要重写与请求方法对应的处理方法,最常用的是doGet()方法和doPost()方法:

java
1
2
3
4
5
6
7
8
9
10
public class DemoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) {
System.out.println("doGet ...");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse res) {
System.out.println("doPost ...");
}
}

3.2 核心接口

3.2.1 ServletConfig接口

ServletConfig接口用于获取Servlet的配置信息,包括名称、初始化参数、上下文对象等。

在创建Servlet时创建,在销毁Servlet时销毁。

常用方法:

java
1
2
3
4
5
6
7
8
// 获取名称
String getServletName();
// 获取上下文对象
ServletContext getServletContext();
// 获取初始化参数
String getInitParameter(String name);
// 获取所有初始化参数名
Enumeration<String> getInitParameterNames();

3.2.2 ServletContext接口

ServletContext接口代表整个应用,在所有客户端之间共享数据。

在应用启动时创建,在应用关闭时销毁。

常用方法:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 获取应用上下文名称
String getServletContextName();
// 获取容器名称和版本号
String getServerInfo();
// 获取应用的相对路径
String getContextPath();
// 获取指定路径的绝对路径,从盘符开始
String getRealPath(String path);
// 获取指定路径的文件夹名和文件,从应用程序根目录开始
Set<String> getResourcePaths(String path);
// 将指定资源转化为流,从应用程序根目录开始
InputStream getResourceAsStream(String path);
// 获取请求转发器,从应用程序根目录开始
RequestDispatcher getRequestDispatcher(String path);
// 根据属性名获取属性值
Object getAttribute(String name);
// 获取请求域中所有属性名的枚举集合
Enumeration<String> getAttributeNames();
// 设置属性名和属性值
void setAttribute(String name, Object object);
// 根据属性名移除属性
void removeAttribute(String name);

3.2.3 ServletRequest接口

ServletRequest接口用于获取请求信息,包括请求方法、请求路径、请求参数、请求头等。

每次请求时创建,请求结束时销毁。

常用方法:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// 根据参数名获取单个参数值
String getParameter(String name);
// 根据参数名获取所有参数值
String[] getParameterValues(String name);
// 获取所有参数名的枚举集合
Enumeration<String> getParameterNames();
// 获取所有参数的键值对集合
Map<String, String[]> getParameterMap();
// 获取请求协议(比如HTTP/1.1、HTTPS/1.1)
String getProtocol();
// 获取请求方案(比如HTTP、HTTPS)
String getScheme();
// 获取服务器名称(比如localhost)
String getServerName();
// 获取服务器端口号(比如8080)
int getServerPort();
// 获取上下文对象
ServletContext getServletContext();
// 获取客户端IP地址
String getRemoteAddr();
// 获取客户端主机名
String getRemoteHost();
// 获取客户端请求的端口号
int getRemotePort();
// 获取服务器接收请求的IP地址
String getLocalAddr();
// 获取服务器接收请求的主机名
String getLocalName();
// 获取服务器接收请求的端口号
int getLocalPort();
// 根据属性名获取属性值
Object getAttribute(String name);
// 获取请求域中所有属性名的枚举集合
Enumeration<String> getAttributeNames();
// 设置属性名和属性值
void setAttribute(String name, Object value);
// 根据属性名移除属性
void removeAttribute(String name);
// 获取请求的字符编码
String getCharacterEncoding();
// 设置请求字符编码
void setCharacterEncoding(String charset) throws UnsupportedEncodingException;
// 获取请求的MIME类型
String getContentType();
// 获取请求输入流
ServletInputStream getInputStream() throws IOException;
// 获取字符输入流
BufferedReader getReader() throws IOException;
// 获取请求转发器
RequestDispatcher getRequestDispatcher(String path);

3.2.4 ServletResponse接口

ServletResponse接口用于设置响应信息,包括响应状态、响应头、响应体等。

每次请求时创建,请求结束时销毁。

常用方法:

java
1
2
3
4
5
6
7
8
9
10
11
12
// 获取响应的字符编码
String getCharacterEncoding();
// 设置响应字符编码
void setCharacterEncoding(String charset);
// 获取响应的MIME类型
String getContentType();
// 设置响应内容类型
void setContentType(String type);
// 获取字节输出流
ServletOutputStream getOutputStream() throws IOException;
// 获取字符输出流
PrintWriter getWriter() throws IOException;

3.2.5 HttpServletRequest接口

HttpServletRequest接口继承了ServletRequest接口,并进一步实现了HTTP专属的请求信息。

新增的常用方法:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 获取请求方法
String getMethod();
// 获取请求的Cookie数组
Cookie[] getCookies();
// 获取当前请求的HttpSession实例,没有则创建
HttpSession getSession();
// 获取应用上下文路径
String getContextPath();
// 获取应用上下文路径后面的映射路径,不包含查询字符串
String getServletPath();
// 获取映射路径后面的信息路径,不包含查询字符串
String getPathInfo();
// 获取请求的URI,不包含查询字符串
String getRequestURI();
// 获取请求的URL,包含协议、主机名、端口号,以及URI,不包含查询字符串
StringBuffer getRequestURL();
// 获取查询字符串
String getQueryString();
// 获取客户端认证方案
String getAuthType();
// 获取当前已认证用户的用户名
String getRemoteUser();
// 检查当前用户是否拥有指定角色
boolean isUserInRole(String role);
// 获取用户身份验证对象
Principal getUserPrincipal();
// 触发用户认证
void authenticate(HttpServletResponse response) throws IOException, ServletException;
// 触发用户登录
void login(String username, String password) throws ServletException;
// 触发用户注销
void logout() throws ServletException;
// 获取请求中指定名称的文件上传部件
Part getPart(String name) throws IOException, ServletException;
// 获取请求中所有文件上传部件
Collection<Part> getParts() throws IOException, ServletException;
// 根据请求头名获取值
String getHeader(String name);
// 获取所有请求头名的枚举集合
Enumeration<String> getHeaderNames();

3.2.6 HttpServletResponse接口

HttpServletResponse接口继承了ServletResponse接口,并进一步实现了HTTP专属的响应信息。

每次请求时创建,请求结束时销毁。

新增的常用方法:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 添加Cookie
void addCookie(Cookie cookie);
// 获取响应状态码
int getStatus();
// 设置响应状态码
void setStatus(int sc);
// 发送错误响应,包括状态码和原因短语
void sendError(int sc, String msg) throws IOException;
// 发送错误响应,使用默认原因短语
void sendError(int sc) throws IOException;
// 触发客户端重定向
void sendRedirect(String location) throws IOException;
// 设置响应头
void setHeader(String name, String value);
// 添加响应头
void addHeader(String name, String value);
// 根据响应头名获取值
String getHeader(String name);
// 根据响应头名获取所有值
Collection<String> getHeaders(String name);
// 获取所有响应头名的列表集合
Collection<String> getHeaderNames();

3.2.7 RequestDispatcher接口

RequestDispatcher接口用于将请求资源交由指定路径处理。

获取方式:

java
1
2
3
4
// 通过ServletContext获取,只能传入绝对路径
RequestDispatcher rd = context.getRequestDispatcher("/target");
// 通过ServletRequest获取,可以传入相对路径和绝对路径
RequestDispatcher rd = request.getRequestDispatcher("target");

常用方法:

java
1
2
3
4
// 请求转发,将当前请求转发指定转发器处理
void forward(ServletRequest, ServletResponse);
// 请求包含,在当前请求包含指定转发器处理的结果
void include(ServletRequest, ServletResponse);

4 配置文件

在Web应用根目录下创建WEB-INF目录,在目录中创建web.xml配置文件,用于声明Web应用的组件规则和全局配置,以及运行时行为。

4.1 全局参数

配置全局上下文参数,需要通过ServletContext对象获取:

web.xml
1
2
3
4
<context-param>
<param-name>contextParam</param-name>
<param-value>全局参数</param-value>
</context-param>

4.2 欢迎页面

配置欢迎页面,按照顺序查找:

web.xml
1
2
3
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

4.3 错误页面

配置错误页面:

web.xml
1
2
3
4
5
6
7
8
<error-page>
<error-code>404</error-code>
<location>/error/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error/500.jsp</location>
</error-page>

5 配置组件

5.1 基于文件

在配置文件中配置Servlet组件,支持设置全局参数和局部参数:

web.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<servlet>
<servlet-name>DemoServlet</servlet-name>
<servlet-class>com.example.servlet.DemoServlet</servlet-class>
<!-- 局部参数 -->
<init-param>
<param-name>initParam</param-name>
<param-value>局部参数</param-value>
</init-param>
<!-- 加载顺序 -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DemoServlet</servlet-name>
<url-pattern>/demo</url-pattern>
</servlet-mapping>

5.2 基于注解

使用@WebServlet注解代替在文件中配置Servlet组件,只能设置局部参数,不能设置全局参数:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@WebServlet(
name = "DemoServlet",
urlPatterns = "/demo",
initParams = {
@WebInitParam(name = "initParam", value = "局部参数")
},
loadOnStartup = 1
)
public class DemoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
// 获取全局参数
System.out.println("contextParam: " + getServletContext().getInitParameter("contextParam"));
// 获取局部参数
System.out.println("initParam: " + getServletConfig().getInitParameter("initParam"));
// 获取请求参数
System.out.println("username: " + req.getParameter("username"));
PrintWriter out = res.getWriter();
out.write(req.getParameter("username"));
out.flush();
out.close();
}
}

6 映射原理

当请求到达时,容器会按照以下顺序处理:

  1. 首先检查是否有精确的Servlet映射匹配,比如基于文件和基于注解配置的映射,如果有则交由对应的Servlet处理。
  2. 如果没有匹配的Servlet映射,则由默认Servlet处理,默认Servlet一般在容器的配置目录下,比如Tomcat的web.xml配置文件。

7 使用技巧

7.1 请求转发

服务器收到请求后,将请求转发给另一个Servlet处理,然后将响应返回给客户端。

特点:

  • 地址栏没有变化。
  • 属于同一次请求,请求数据可以共享。
  • 可以转发到WEB-INF目录下的资源,不能转发到其他应用中的资源。
  • 路径以/开头,表示相对于当前应用的路径,不需要拼接当前应用。

转发请求:

java
1
2
3
4
5
6
7
8
9
10
@WebServlet("/demo")
public class DemoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException {
// 获取请求参数
System.out.println("username: " + req.getParameter("username"));
// 转发请求
req.getRequestDispatcher("/forward").forward(req, res);
}
}

处理转发:

java
1
2
3
4
5
6
7
8
9
10
@WebServlet("/forward")
public class ForwardServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
// 获取请求参数
System.out.println("username: " + req.getParameter("username"));
// 处理转发
res.getWriter().println(req.getParameter("username"));
}
}

7.2 响应重定向

服务器收到请求后,将响应状态码设置为302,并将Location头设置为新的URL,客户端收到响应后会自动向新的URL发送请求。

特点:

  • 地址栏会发生变化。
  • 属于两次请求,请求数据不能共享。
  • 不能重定向到WEB-INF目录下的资源,可以重定向到其他应用中的资源。
  • 路径以/开头,表示相对于当前服务器的路径,需要拼接当前应用。

重定向响应:

java
1
2
3
4
5
6
7
8
9
10
@WebServlet("/demo")
public class DemoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
// 获取请求参数
System.out.println("username: " + req.getParameter("username"));
// 重定向响应
res.sendRedirect("redirect");
}
}

处理重定向:

java
1
2
3
4
5
6
7
8
9
10
@WebServlet("/redirect")
public class RedirectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
// 获取请求参数
System.out.println("username: " + req.getParameter("username"));
// 处理重定向
res.getWriter().println(req.getParameter("username"));
}
}

评论