f10@t's blog

JavaEE基础 - Servlet

字数统计: 2.4k阅读时长: 11 min
2020/05/06

Java Servlet主要功能是可以交互式地浏览和修改数据,生成动态Web内容。

Servlet及其工作模式

Servlet 为创建基于 web 的应用程序提供了基于组件、独立于平台的方法,可以不受 CGI 程序的性能限制。Servlet 有权限访问所有的 Java API,包括访问企业级数据库的 JDBC API。

使用Servlet的优点如下:

  • 性能更好。
  • Servlet 在 Web 服务器的地址空间内执行。这样它就没有必要再创建一个单独的进程来处理每个客户端请求。
  • Servlet 是独立于平台的,因为它们是用 Java 编写的。
  • 服务器上的 Java 安全管理器执行了一系列限制,以保护服务器计算机上的资源。因此,Servlet 是可信的。
  • Java 类库的全部功能对 Servlet 来说都是可用的。它可以通过 sockets 和 RMI 机制与 applets、数据库或其他软件进行交互。

Servlet的定位大概是这样的位置:

客户端发出请求到HTTP服务器,请求内容由Servlet进行处理,由Servlet来作为中间层去调用如数据库等这类资源,最终处理结果作为响应返回给客户端浏览器。

Servlet生命周期

一个Servlet程序的生命周期有三步:

image-20200506143628648

即init() -> service() -> destroy()。其中service()方法是执行任务的主体,每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service() 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet、doPost、doPut,doDelete 等方法,这些方法都定义在HttpServlet这个抽象类中,该类的UML图如下:

简单实例

建立项目

首先先建立一个最基本的项目:

image-20200506144121381

选中Web APPlication项目即可。也可以使用Maven建立,我这里下好了tomcat所以就用javaEE模板了。之后取项目名称,结束。

Tomcat的配置如下:

image-20200506144330013

主要配置端口等项目,然后到Deployment中设置好war包以及访问路径:

image-20200506144450646

更多建立细节请移步https://www.cnblogs.com/javabg/p/7976977.html进行参考。

Servlet编写

下面主要重点关注Servlet的使用、过滤器的作用及原理、数据库的使用。其他如Cookie操作、Session操作等请移步https://www.runoob.com/servlet学习

首先我们写一个简单的类来进行了解。

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
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

@WebServlet("/Hello")
public class HelloWorld extends HttpServlet {

private String message;

/**
* 初始化函数
*/
public void init() throws ServletException {
message = "Hello World";
}

/**
* Get方法的处理函数
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 设置响应内容类型
response.setContentType("text/html");

// 业务逻辑
PrintWriter out = response.getWriter();
out.println("<h1>" + message + "</h1>");
}

/**
* 销毁函数
*/
public void destroy() {
System.out.println("Destroy");
}
}

​ 在你写的类的开头使用@WebService注解就可以将他作为一个servlet了,或者你可以将这个类写到web.xml中来体现:

1
2
3
4
5
6
7
8
9
10
11
<web-app>      
<servlet>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>HelloWorld</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>/HelloWorld</url-pattern>
</servlet-mapping>
</web-app>

使用过滤器

Servlet的过滤器机制以前看到过这样的设计,Struts2中就有过滤器这样的组件,都是由这个发展过去的。过滤器的作用主要是动态地拦截请求和响应,以变化或使用在请求或响应中地信息,可以实现以下目的:

  • 在客户端的请求访问后端资源之前,拦截这些请求。
  • 在服务器的响应发送到客户端之前,处理这些响应。

常见的由以下作用类型的过滤器:

  • 身份验证过滤器(Authentication Filters)。
  • 数据压缩过滤器(Data compression Filters)。
  • 加密过滤器(Encryption Filters)。
  • 触发资源访问事件过滤器。
  • 图像转换过滤器(Image Conversion Filters)。
  • 日志记录和审核过滤器(Logging and Auditing Filters)。
  • MIME-TYPE 链过滤器(MIME-TYPE Chain Filters)。
  • 标记化过滤器(Tokenizing Filters)。
  • XSL/T 过滤器(XSL/T Filters),转换 XML 内容。

过滤器类的编写主要是实现了javax.servlet.Filter接口,该接口的定义如下:

1
2
3
4
5
6
7
8
9
10
11
package javax.servlet;

import java.io.IOException;

public interface Filter {
default void init(FilterConfig filterConfig) throws ServletException {}

void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;

default void destroy() {}
}

其中init为初始化函数,doFilter函数为业务逻辑函数,destory函数为销毁函数。下面实现一个例子,在用户访问一个页面前对其用户名进行检查,这里使用Filter类:

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
51
52
53
54
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.io.PrintWriter;

/**
* 实现一个Filter过滤器类
*/
@WebFilter("/Hello")
public class MyFilter implements Filter {

private static String passMsg;

@Override
public void init(FilterConfig filterConfig) throws ServletException {
//获取初始化参数
passMsg = "<h1>Login Ok.</h1>";
}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletResponse.setContentType("text/html;charset=UTF-8");
servletRequest.setCharacterEncoding("UTF-8");
servletResponse.setCharacterEncoding("UTF-8");
PrintWriter out = servletResponse.getWriter();
if (servletRequest.getParameter("name").isEmpty()) {
out.println("<!DOCTYPE html> \n" +
"<html>\n" +
"<head><meta charset=\"utf-8\"><title>登录页面</title></head>\n" +
"<body>\n" +
"please input name!" +
"</body>\n" +
"</html>");
} else if (servletRequest.getParameter("name").equals("float")) {
out.println("<!DOCTYPE html> \n" +
"<html>\n" +
"<head><meta charset=\"utf-8\"><title>登录页面</title></head>\n" +
"<body>\n" +
passMsg +
"</body>\n" +
"</html>");
//将请求传回过滤连
filterChain.doFilter(servletRequest, servletResponse);
} else {
out.println("<!DOCTYPE html> \n" +
"<html>\n" +
"<head><meta charset=\"utf-8\"><title>登录页面</title></head>\n" +
"<body>\n" +
"wrong username!" +
"</body>\n" +
"</html>");
}
}
}

我使用了webFilter将这个类作为Hello的Servlet的过滤器,其中要求了传入的用户名不为空且为float,这样才会放过这个请求到Hello的Servlet中去。访问结果:

image-20200506165331898
image-20200506165339550
image-20200506165404787

这里HTTP头的操作可以遍历HttpServletRequest对象的headers来实现,使用getHeaderNamesgetHeader即可。

其中的filterChain.doFilter(servletRequest, servletResponse);意为继续调用下一个过滤器,因为我这里只有一个过滤器,所以没有太大用途。过滤器可以使用@WebFilter注解进行标注,/*意为全局过滤器,也可以在web.xml中进行定义,过滤器之间的顺序由xml文档中的顺序决定:

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
<filter>
<filter-name>LogFilter</filter-name>
<filter-class>com.runoob.test.LogFilter</filter-class>
<init-param>
<param-name>test-param</param-name>
<param-value>Initialization Paramter</param-value>
</init-param>
</filter>

<filter>
<filter-name>AuthenFilter</filter-name>
<filter-class>com.runoob.test.AuthenFilter</filter-class>
<init-param>
<param-name>test-param</param-name>
<param-value>Initialization Paramter</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>LogFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
<filter-name>AuthenFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

比如上述的这个配置中有两个过滤器,但是因为filter-mapping的顺序为:LogFilter -> AuthenFilter,所以会先进入LogFilter再进入AuthenFilter过滤器。

连接数据库

Java Database Connectivity,简称JDBC,是Java中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。下面使用mysql5.7进行配合使用JDBC,可以使用Maven拉取依赖,这里选择了引入jar文件。

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;

@WebServlet("/DatabaseAccess")
public class DatabaseAccess extends HttpServlet {

//JDBC驱动以及数据库URL
static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String DB_URL = "jdbc:mysql://localhost:3306/RUNOOB";

// 数据库的用户名与密码,需要根据自己的设置
static final String USER = "root";
static final String PASS = "990311puk";

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Connection conn = null;
Statement stmt = null;
//设置响应内容类型
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
String title = "Servlet Mysql";
String docType = "<!DOCTYPE HTML>";
out.println(docType +
"<html>\n" +
"<head><title>" + title + "</title></head>\n" +
"<body bgcolor=\"#f0f0f0\">\n" +
"<h1 align=\"center\">" + title + "</h1>\n");
try {
//注册JDBC驱动器
Class.forName("com.mysql.jdbc.Driver");

//打开一个连接
conn = DriverManager.getConnection(DB_URL, USER, PASS);

// 执行 SQL 查询
stmt = conn.createStatement();
String sql;
sql = "SELECT id, name, url FROM websites";
ResultSet rs = stmt.executeQuery(sql);

// 展开结果集数据库
while(rs.next()){
// 通过字段检索
int id = rs.getInt("id");
String name = rs.getString("name");
String url = rs.getString("url");

// 输出数据
out.println("ID: " + id);
out.println(", 站点名称: " + name);
out.println(", 站点 URL: " + url);
out.println("<br />");
}
out.println("</body></html>");

// 完成后关闭
rs.close();
stmt.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 最后是用于关闭资源的块
try{
if(stmt!=null)
stmt.close();
}catch(SQLException se2){
}
try{
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
}
}
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}

访问结果:

image-20200506162625948

参考

https://www.runoob.com/servlet

CATALOG
  1. 1. Servlet及其工作模式
  2. 2. Servlet生命周期
  3. 3. 简单实例
    1. 3.1. 建立项目
    2. 3.2. Servlet编写
    3. 3.3. 使用过滤器
    4. 3.4. 连接数据库
  4. 4. 参考