前置知识
- JavaSE
- MySQL
- JDBC
- WEB前端(会一些)
Servlet
关于系统架构
系统架构包括什么
- C/S 架构
- B/S 架构
C/S 架构
- Client/Server(客户端/服务器)
- C/S 架构的软件或系统有哪些
- 特点:需要安装特定的客户端软件
- C/S 架构的优点和缺点:
- 优点:
- 速度快(一些数据存在客户端本身中,通过网络只需要传输一些数据)
- 体验好
- 服务器压力小
- 安全
- 缺点:
- 升级维护差劲(升级维护麻烦,成本高,有些软件不是很容易安装)
- 优点:
B/S 架构
- B/S(Browser/Server,浏览器/服务器)
- 举例:https://www.baidu.com 等各种网站
- B/S 结构是一个特殊的 C/S 系统,这个 C 比较特殊是一个固定不变的浏览器软件
- B/S 结构的系统优点和缺点:
- 优点:
- 升级维护方便,成本比较低(只需要升级服务端即可)
- 不需要安装特定的客户端软件,用户操作方便,只需要打开浏览器,输入网址即可
- 缺点:
- 速度慢(是因为所有数据都在服务器,用户发送的每个请求都需要服务器的加入,所有B/S服务器的系统在网络中传送的数据量比较大)
- 体验差(界面不是很酷炫,只支持三种语言HTML,CSS,JavaScript)
- 不安全(所有数据都在服务器上,如果发生不可抗力因素,最终数据全部丢失)
- 优点:
C/S 和 B/S 结构的使用场景
- 娱乐性软件建议 C/S 结构
- 公司内部使用的一些业务软件建议 B/S 结构
开发 B/S 结构的系统
- 就是开发个 Web 网站
- 需要的技术:
- WEB前端:HTML,CSS,JavaScript
- WEB后端:Java或C或C++或Python或PHP等,Java做WEB开发称为JavaWEB开发,JavaWEB开发最核心的规范:Servlet【Server Applet服务端的Java小程序】
JavaEE 是什么
- Java包括三大块:
- JavaSE:Java标准版(一套类库,只不过是标准类库,是EE和ME的基础)
- JavaEE:Java企业版(也是一套类库,只不过专门为解决企业级项目的开发,可以开发WEB系统,比较火爆)
- JavaME:Java微型版(还是一套类库,帮助电子微型设备内核程序的开发,吸尘器内核,电冰箱内核等等)
- Java包括13种规范:
- Servlet就是JavaEE的规范之一
B/S 结构系统通讯原理
WEB系统的访问过程
- 打开浏览器
- 找到地址栏
- 输入合法地址
- 回车
- 在浏览器会展示相应的结果
关于域名
- https://www.baidu.com (网址)
- www.baidu.com (域名)
- 在浏览器地址输入域名,回车后,域名解析器会将域名解析出来一个具体的IP地址和端口号等
- 解析结果也许是:https://14.215.177.39:80/index.html
- IP地址:
- 计算机在网络中的一个身份证号,在同一个网络中,IP地址唯一
- 计算机A和B想要通信,就需要知道B的IP地址
- 端口号:
- 一个端口代表一个软件(一个端口代表一个应用,一个端口仅代表一个服务)
- 一个计算机有很多软件,每一个软件启动后都有一个对应的端口号
- 在同一个计算机上,端口号具有唯一性
WEB系统通讯原理
- 输入URL(同一资源定位符):https://www.baidu.com
- 域名解析器进行域名解析:https://14.215.177.39:80/index.html
- 浏览器在网络中搜索 14.215.177.39 这一台主机
- 定位 14.215.177.39 这台主机上的服务器软件,因为是 80 端口,可以轻松定位到 80 端口对应的服务器软件
- 80 端口对应的服务器软件得知浏览器想要的资源名是 index.html
- 服务器软件找到 index.html 文件,并将文件内容输出到浏览器上
- 浏览器接收来自服务器的代码(HTML,CSS,JS)
- 浏览器渲染,执行(HTML,CSS,JS)
关于WEB服务器软件
WEB服务器软件有哪些
- Tomcat(Apache)
- jetty
- JBOSS(应用服务器)
- WebLogic(应用服务器)
- WebSphere(应用服务器)
应用服务器和WEB服务器的关系
- 应用服务器实现了JavaEE的所有规范(13个规范)
- WEB服务器只实现了JavaEE中的Servlet+JSP两个核心规范
- 说明应用服务器是包含WEB服务器的
- JBOSS内嵌了一个Tomcat服务器
Tomcat下载
- apache官网 https://www.apache.org
- Tomcat官网 https://tomcat.apache.org/
- Tomcat开源免费轻量级
- 别名 catalina (catalina是一个美国岛屿,据说作者在这个风景秀丽的小岛开发的WEB服务器,运行速度快)
- Logo是个公猫,实现了JavaEE中的Servlet+JSP两个核心规范
- Tomcat是Java语言写的
- Tomcat想要运行,必须现有jre(Java的运行环境)
- JAVA_HOME = …\jdk-version
- PATH = %JAVA_HOME%\bin
Tomcat服务器的目录
- bin:Tomcat命令文件存放目录
- conf:Tomcat配置文件存放目录
- server.xml 可以配置端口号,默认 8080
- lib:Tomcat服务器核心程序目录,都是jar包
- logs:Tomcat服务器日志目录
- temp:Tomcat服务器临时目录,存储临时文件
- webapps:存放大量的webapp
- work:存放JSP文件翻译之后的Java文件以及编译后的class文件
启动Tomcat
- bin目录下有一个 startup.bat 文件,可以启动 Tomcat 服务器
- xxx.bat 文件是 Windows 操作系统专用,bat 文件是批处理文件,可以编写大量的 Windows 的 dos 命令,执行 bat 文件相当于批量执行 dos 命令
- xxx.sh 文件是 Linux 环境下能够执行的 shell 命令,批量执行 shell 命令
- Tomcat 提供了 bat 和 sh 文件
- 分析 startup.bat 文件:实际上最后执行 catalina.bat 文件
- catalina.bat 文件中有这样一行配置:
set MAINCLASS=org.apache.catalina.startup.Bootstrap
- Tomcat 服务器是 Java 语言写的,那么启动 Tomcat 服务器就是执行 main 方法
- 配置 Tomcat 服务器需要的环境变量
- JAVA_HOME = JDK 的根
- CATALINA_HOME = Tomcat 服务器的根
- PATH = %JAVA_HOME%\bin; %CATALINA_HOME%\bin
- 启动 Tomcat 和 关闭 Tomcat:
- 将原先的 startup.bat 更名为 tomcat-startup.bat
- 将原先的 shutdown.bat 更名为 tomcat-shutdown.bat
- 于是 dos 窗口输入 tomcat-startup 和 tomcat-shutdown 即可
- 注意关闭的时候用命令关
- 测试 Tomcat 服务器是否启动成功
- 打开浏览器输入 URL = http://localhost:8080/
实现一个最基本的web应用(这个web应用中无Java小程序)
- 找到 CATALINA_HOME\webapps 目录
- 因为所有的 webapp 都需要放到 webapps 目录下,不放 Tomcat 找不到应用
- 在 CATALINA_HOME\webapps 目录下新建一个子目录:oa
- oa 就是这个 webapp 的名字
- 在 oa 目录下新建资源,例如:index.html
- 编写 index.html
- 启动 Tomcat
- 启动:tomcat-startup(本人更改了 startup.bat 文件更名为 tomcat-startup.bat)
- 关闭:tomcat-shutdown(同理)
- 在浏览器地址栏上输入 http://localhost:8080/oa/index.html 就可以得到 index.html 资源
动态的web应用请求响应过程
在 B/S 结构的系统中有哪些角色
- 浏览器软件的开发团队(Edge,Chrome)
- WEB Server 的开发团队(Tomcat,Jetty,WebLogic,JBOSS,WebSphere)
- DB Server 的开发团队(Oracle,MySQL)
- Webapp 的开发团队(WEB应用是JavaWEB程序员开发的)
角色与角色之间需要遵守哪些规范,哪些协议
- Webapp 的开发团队 和 WEB Server 的开发团队之间有一套规范:JavaEE 中的 Servlet 规范,Servlet 的作用是将 WEB Server 和 Webapp 解耦合
- Browser 和 WebServer 之间有一套传输协议:HTTP 协议(超文本传输协议)
- Webapp 开发团队 和 DB Server 之间有一套规范:JDBC 规范
模拟 Servlet 本质
- 充当 SUN 公司的角色,制定 Servlet 规范
- javax.servlet.Servlet 接口
- 充当 Tomcat 服务器的开发者
- 实现 Tomcat 类
- 充当 Webapp 的开发者
- BankServlet implements Servlet
- UserListServlet implements Servlet
- UserLoginServlet implements Servlet
- 对于 javaweb 程序员来说,只需要做:
- 编写类实现 Servlet 接口
- 将编写的类配置到配置文件中,在配置文件中指定 请求路径 和 类名的关系
- 注意:
- 配置文件的文件名不能乱来,固定的(Tomcat 已经指定好了)
- 配置文件的文件路径不能乱来,固定的(Tomcat 已经指定好了)
- 文件名,文件路径都是 SUN 公司制定的 Servlet 规范中的明细
- 严格意义上来说 Servlet 规范并不是简单的一个接口:
- Servlet 规范中规定了:
- 一个合格的 webapp 该有的目录结构
- 一个合格的 webapp 该有的配置文件
- 一个合格的 webapp 配置文件的路径位置
- 一个合格的 webapp 中的 java 程序所在位置
- Tomcat 服务器要遵循 Servlet 规范,JavaWEB 程序元也要遵循这个 Servlet 规范,这样 Tomcat 服务器和 webapp 才能解耦合
- Servlet 规范中规定了:
- Servlet 规范是什么一个规范
- 遵循 Servlet 规范的 webapp ,这个 webapp 就可以放在不同的 WEB 服务器中运行
- Servlet 规范包括什么:
- 规范了哪些接口
- 规范了哪些类
- 规范了一个 web 应用应该有哪些配置文件
- 规范了一个 web 应用中配置文件的名字,存放的路径,内容
- 规范了一个 web 应用的目录结构应该是什么样子的
1 | /** |
1 | /** |
1 | /** |
1 | /** |
1 | UserListServlet = |
1 | import java.io.FileReader; |
开发一个带有 Servlet 的 webapp(重点)
开发步骤:
在 webapps 目录下新建一个目录,起名就是 webapp 的名字,例如 crm
- crm 就是 webapp 的根
在 webapp 根目录 下新建一个目录:WEB-INF
- 这个目录的名字和位置是 Servlet 规范中规定的
在 WEB-INF 下新建一个目录:classes
- 这个也是 Servlet 规定,并且这个目录下存放的就是 class 文件,java 程序编译后的字节码文件
在 WEB-INF 下新建一个目录:lib
- 这个 lib 目录不是必须的,但如果一个 webapp 需要第三方的 jar 包,就需要放入 lib 目录下
在 WEB-INF 下新建一个文件:web.xml
- 这个文件是必须的,在这个配置文件中,描述了文件路径和 Servlet 类之间的对照关系
- 这个文件最好从其他 webapp 中拷贝
- web.xml 基本结构
1
2
3
4
5
6
7
8
9
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"
metadata-complete="true">
</web-app>
编写 java 程序: java 程序必须实现 Servlet 接口
- Servlet 接口不在 JDK 中(不是 JavaSE 了)
- Servlet 接口(Servlet.class 文件)是 SUN 公司提供的
- Servlet 接口是 JavaEE 的规范中的一员
- Tomcat 服务器实现了 Servlet 规范,所以 Tomcat 服务器也需要使用 Servlet 接口,Tomcat 服务器的 CATALINE_HOME\lib 目录下有一个 servlet-api.jar,解压后有一个 Servlet.class 文件
- 编译后的 class 文件放入 classes 目录下
编写我们的 HelloServlet.java
- 为了让 import jakarta.servlet.*; 通过编译,需要配置环境变量 CLASSPATH = ,;D:\Tomcat\apache-tomcat-10.0.16\lib\servlet-api.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
32import jakarta.servlet.Servlet;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.ServletConfig;
import java.io.IOException;
public class HelloServlet implements Servlet{
public void init(ServletConfig config) throws ServletException{
}
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException{
System.out.println("My First Servlet, Hello Servlet!");
}
public void destroy(){
}
public String getServletInfo(){
return "";
}
public ServletConfig getServletConfig(){
return null;
}
}
将编译之后的 HelloServlet.class 文件拷贝到 classes 目录下
在 web.xml 文件中编写配置信息,将”请求路径”与”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
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0"
metadata-complete="true">
<!--任何一个 servlet 都对应一个 servlet-mapping -->
<!--servlet 描述信息-->
<servlet>
<servlet-name>sameName</servlet-name>
<!--这个位置是包名的全限定类型-->
<servlet-class>HelloServlet</servlet-class>
</servlet>
<!--servlet 映射信息-->
<servlet-mapping>
<servlet-name>sameName</servlet-name>
<!--这里需要一个路径-->
<url-pattern>/asd/dfg/qw/xcv/aaa</url-pattern>
</servlet-mapping>
</web-app>启动 Tomcat 服务器
打开浏览器,在浏览器地址栏输入 url = localhost:8080/项目名/配置文件设置的路径,url-pattern 就是 “配置文件设置的路径”
- 会发现在 Tomcat 控制台终端显示 “My First Servlet, Hello Servlet!”
如何将信息输出到浏览器上,需要使用 ServletResponse 接口:response
- 在 HelloServlet.java 代码中:
1
2
3
4
5
6
7
8
9
10
11
12public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException{
System.out.println("My First Servlet, Hello Servlet!");
// 如何将信息输出到浏览器上
// 需要使用ServletResponse接口:response
PrintWriter out = response.getWriter();
/* 这是个输出流,不需要刷新与关闭,因为 Tomcat 来维护
out.flush();
out.close();
*/
out.print("Hello Servlet, You are my first servlet!")
} - 输入 URL = http://localhost:8080/crm/asd/dfg/qw/xcv/aaa,会发现在在 Tomcat 控制台上输出 “My First Servlet, Hello Servlet!”,并且在浏览器页面上输出 “Hello Servlet, You are my first servlet!”
- 在 HelloServlet.java 代码中,让 html 代码得以体现,在获取流之前设置 response.setContentType(“text/html”) :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException{
// 向控制台打印输出
System.out.println("My First Servlet, Hello Servlet!");
// 设置响应的内容类型是 普通文本/html 代码
response.setContentType("text/html");
// 将信息输出到浏览器上
PrintWriter out = response.getWriter();
/*
out.flush();
out.close();
*/
out.print("Hello Servlet, You are my first servlet!");
out.print("<h1>hello servlet</h1>");
} - 输入 URL = http://localhost:8080/crm/asd/dfg/qw/xcv/aaa,会发现在在 Tomcat 控制台上输出 “My First Servlet, Hello Servlet!”,并且在浏览器页面上输出 “Hello Servlet, You are my first servlet!” 和 html 中 h1 标签的 “hello servlet”
- 在 HelloServlet.java 代码中:
浏览器上编写的路径太长可以使用超链接
- html 页面只能放到 WEB-INF 目录之外
1
2
3
4
5
6
7
8
9
<html>
<head>
<title>index page</title>
</head>
<body>
<a href="/crm/asd/dfg/qw/xcv/aaa">hello servlet</a>
</body>
</html>
Tomcat 负责调用 main 方法,javaweb 程序员只需要编写 Servlet 接口的实现类,然后注册到 web.xml 文件中即可
总结:一个合法的 webapp 目录结构是怎样的:
1
2
3
4
5
6
7
8
9
10webappRoot
|--- WEB-INF
|--- classes(存放字节码)
|--- lib(第三方 jar 包)
|--- web.xml(注册servlet)
|--- html
|--- css
|--- javascript
|--- image
...浏览器发送请求到最后服务器调用 servlet 的过程(以下过程描述很粗糙)
- 用户输入 URL = http://localhost:8080/crm/asd/dfg/qw/xcv/aaa
- Tomcat 服务器收到请求,截取路径:/crm/asd/dfg/qw/xcv/aaa
- Tomcat 服务器找到 crm 项目
- Tomcat 在 web.xml 中查找 /asd/dfg/qw/xcv/aaa 路径对应的 Servlet
- Tomcat 服务器通过反射机制,创建 com.sunlie.servlet.HelloServlet 的对象
- Tomcat 服务器调用 com.sunlie.servlet.HelloServlet 对象的 service 方法
关于 JavaEE 的版本
- 目前最高版本是 Java EE 8
- Java EE 被 Oracle 捐献了,Oracle 将 Java EE 规范捐献给 Apache 了
- Java EE 被 Apache 更名为 jakarta EE
- 于是 Java EE 8 升级之后的 Java EE 9 不再是 Java EE 9 这个名字,而是叫 Jakarta EE 9
- Java EE 8 的时候对应的 Servlet 类名是:javax.servlet.Servlet
- jakatra EE 9 对应的 Servlet 类名是:jakarta.servlet.Servlet
解决 Tomcat 服务器在 DOS 命令窗口乱码问题
更改 %CATALINA_HOME%\conf\logging.properties
1 | # java.util.logging.ConsoleHandler.encoding = UTF-8 |
Servlet 连接数据库
- 在 Servlet 直接编写 java 代码即可
- 注意 JDBC 的 jar 包需要放到 \webapps\crm\WEB-INF\lib 下
1 | package com.sunlie.servlet; |
在 Tomcat 控制台输出如下
1
2
3
4
5
6
7
8
9
10
11register success!!!
connection success!!!
precompile success!!!
execute SQL success!!!
101 zs1 101
102 zs2 101
201 zs3 102
202 zs4 102
203 zs5 102
204 zs6 102
process result set success在浏览器输出如下
1
2
3
4
5
6101 zs1 101
102 zs2 101
201 zs3 102
202 zs4 102
203 zs5 102
204 zs6 102
使用 IDEA 开发 Servlet
- 创建空工程,新建 Module,为 Java
- 右键 Module ,Add FrameWork Support,添加框架支持
- 勾选 Web Application,IDEA 会生成符合 Servlet 规范的目录结构
- 其中生成的 web 目录就代表着 webapp 的根
- 生成的 index.jsp 可以删除
- 编写 Servlet
- code
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
90
91
92package com.sunlie.javaweb.servlet;
import jakarta.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
public class StudentServlet implements Servlet {
public void init(ServletConfig servletConfig) throws ServletException {
}
public ServletConfig getServletConfig() {
return null;
}
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
// 设置响应的内容类型
servletResponse.setContentType("text/html");
PrintWriter out = servletResponse.getWriter();
// 连接数据库(JDBC)
Connection conn = null;
PreparedStatement ps = null;
ResultSet res = null;
try {
// register driver
Class.forName("com.mysql.cj.jdbc.Driver");
// get connection
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/sunlie",
"root",
"root"
);
// get precomplie database object
String sql = "select sno, sname, classno from t_student";
ps = conn.prepareStatement(sql);
// execute SQL statement
res = ps.executeQuery();
// process result set
while (res.next()){
int sno = res.getInt("sno");
String sname = res.getString("sname");
int classno = res.getInt("classno");
out.print(sno + " " + sname + " " + classno + "<br>");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// close resource
if (res != null){
try {
res.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (ps != null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public String getServletInfo() {
return null;
}
public void destroy() {
}
}
- code
- 发现没有 Servlet 接口,将 CATALINA_HOME\lib 下的 jsp-api.jar 与 servlet-api.jar 加载到 IDEA 下的 classpath 中,具体是 File->Project Structure->Modules->Dependencies->’+’->JARS
- 实现 jakarta.servlet.Servlet 接口中的 5 个方法
- 在 WEB-INF 新建子目录 lib,将 JDBC 的 jar 包放到该目录下
- 在 web.xml 文件中完成 StudentServlet 类的注册
- code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>StudentServlet</servlet-name>
<servlet-class>com.sunlie.javaweb.servlet.StudentServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>StudentServlet</servlet-name>
<url-pattern>/servlet/student</url-pattern>
</servlet-mapping>
</web-app>
- code
- 给 html 页面放在 WEB-INF 目录外,编写一个超链接,用户点击超链接后,Tomcat 执行后台 StudentServlet
- code
1
2
3
4
5
6
7
8
9
10
11
<html lang="en">
<head>
<meta charset="UTF-8">
<title>student page</title>
</head>
<body>
<!--这里的项目名使用 /xmm ,无法动态获取-->
<a href="/xmm/servlet/student">student list</a>
</body>
</html>
- code
- IDEA 工具关联 Tomcat 服务器,关联的过程中将 webapp 部署到 Tomcat 服务器中
- IDEA 右上角 ‘绿色锤子’ 右边 -> Edit Configuration -> ‘+’ -> Tomcat Server Local
- 在 ‘Server’ 中配置 ‘Name’, ‘Application’, ‘JRE’
- 在 ‘Deployment’ 部署 webapp ,’+’ -> ‘Artifact’,在 ‘Application context’ 更改项目名
- 启动 Tomcat 服务器,IDEA 右上角以 ‘Debug’ 模式启动
- 打开浏览器,在浏览器输入 http://localhost:8080/xmm/student.html ,点击链接
Servlet 对象生命周期
- Servlet 对象的声明周期是由 Tomcat 服务器(web server)全权负责的
- Tomcat 服务器通常也叫做 WEB 容器(Container)
- 自己 new 的 servlet 不受 WEB Container 管理
- WEB 服务器创建的 Servlet 对象都会被放到一个集合(HashMap)当中,只有放入这个 HashMap 集合中的 Servlet 才能被 WEB 管理
- WEB 容器底层应该有一个 HashMap 集合,在这个集合中存储了 Servlet 对象和请求路径之间的关系
- 服务器启动的时候,Servlet 对象有没有被创建
- 通过测试,添加构造方法,发现 Servlet 对象并没有被实例化
- 当用户访问该 Servlet 类对应的路径时,Servlet 对象被实例化
- 如何让服务器启动时,创建 Servlet 对象
- 在 web.xml 下添加 “load-on-startup” 标签,标签之间需要添加一个整数,这个整数越小代表先被实例化,优先级越高
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
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<servlet>
<servlet-name>AServlet</servlet-name>
<servlet-class>com.sunlie.javaweb.servlet.AServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>AServlet</servlet-name>
<url-pattern>/aservlet</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>BServlet</servlet-name>
<servlet-class>com.sunlie.javaweb.servlet.BServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>BServlet</servlet-name>
<url-pattern>/bservlet</url-pattern>
</servlet-mapping>
</web-app>
- 在用户发送第一次请求时,Servlet 对象被实例化
- 在控制台输出
1
2
3ASerlvet!!! (构造方法)
AServlet init execute!!! (init 方法)
Aservlet service execute!!! (service 方法)
- 在控制台输出
- 在用户发送第二次或更多请求后,Servlet 调用 service 方法
- 在控制台输出
1
Aservlet service execute!!!
- 在控制台输出
- 得出结论
- Servlet 对象时单实例的,但是 Servlet 类并不符合单例模式,我们称之为假单例,是因为 Tomcat 只创建了一个 Servlet 对象,所以导致的单例(真单例模式:构造方法是私有化的)
- 无参数构造方法和 init 方法只会调用一次,只要用户发送一次请求,service 就会被调用一次
- 关闭服务器的时候,服务器输出
- 在控制台输出
1
AServlet destroy execute!!!
- 在控制台输出
- Servlet 对象被销毁之前调用 destory()
适配器设计模式 Adapter
因为使用 Servlet 接口,一般都是重写 service() ,但是生成的 5 个方法使得代码比较臃肿,所以采用适配器设计模式来简化代码,简而言之就是使用抽象类
以下是举例:
定义一个接口
1
2
3
4
5
6
7
8
9
10
11
12package com.sunlie.javaweb.adapter;
public interface MyInterface {
void m1();
void m2();
void m3();
void m4();
void m5();
void m6();
void core();
}将 core() 方法设置为抽象方法
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
26package com.sunlie.javaweb.adapter;
// UserAdapter 主要使用 core();
public abstract class UserAdapter implements MyInterface{
public void m1() {
}
public void m2() {
}
public void m3() {
}
public void m4() {
}
public void m5() {
}
public void m6() {
}
public abstract void core();
}其他类继承该抽象类即可
1
2
3
4
5
6
7
8
9
10package com.sunlie.javaweb.adapter;
// 只需要重写 core() ;
public class UserService extends UserAdapter{
public void core() {
}
}同理,对于 Servlet 接口,编写抽象类 GenericServlet (通用的servlet类) 来设置 service() 方法为抽象的,其他编写的 Servlet 类继承 GenericServlet 抽象类并实现 service() 即可,代码简洁
改造 Servlet 适配器 GenericServlet 抽象类
- Servlet 类中的 init 方法中的参数有 ServletConfig servletConfig
- Tomcat 的伪代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17class Solution {
public static void main(String[] args) {
// ...
// Tomcat 服务器伪代码
Class<?> aClass = Class.forName("...");
Object o = aClass.newInstance();
Servlet servlet = (Servlet) o;
// 创建 ServletConfig 对象
ServletConfig servletConfig = new org.apache.catalina.core.StandardWrapperFacade();
// 调用init方法
// 传入参数
servlet.init(servletConfig);
}
} - init 的参数 servletConfig 为了在 service() 中能够使用,改造 GenericServlet 抽象类
- 改造后的 GenericServlet,既能让子类获取到 servletConfig 又能让子类重写 init()
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
40package com.sunlie.javaweb.servlet;
import jakarta.servlet.*;
import java.io.IOException;
public abstract class GenericServlet implements Servlet {
// 成员变量
private ServletConfig config;
public final void init(ServletConfig servletConfig) throws ServletException {
this.config = servletConfig;
this.init();
}
// 让子类重写这个 init()
public void init(){
}
public ServletConfig getServletConfig() {
return this.config;
}
public abstract void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException;
public String getServletInfo() {
return null;
}
public void destroy() {
}
} - 在 CServlet 类中获取到 servletConfig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package com.sunlie.javaweb.servlet;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import java.io.IOException;
public class CServlet extends GenericServlet{
public void init() {
System.out.println("CServlet init method execute!!!");
}
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
// 于是可以在子类中调用 servletConfig 对象
ServletConfig servletConfig = this.getServletConfig();
System.out.println("CServlet's servletConfig = " + servletConfig);
// CServlet's servletConfig = org.apache.catalina.core.StandardWrapperFacade@4e01ddd4
}
}
- 改造后的 GenericServlet,既能让子类获取到 servletConfig 又能让子类重写 init()
- 好消息… jakarta.servlet.GenericServlet 抽象类已经有了,不需要再自己写了
ServletConfig 接口
- ServletConfig 和 Servlet 一样是一个接口,包名 jakarta.servlet.ServletConfig
- 实现了 ServletConfig 接口的类:
- org.apache.catalina.core.StandardWrapperFacade
- 也就是 Tomcat 服务器实现了 ServletConfig 接口
- ServletConfig 对象是 Servlet 的配置信息对象
- ServletConfig 对象中包装的信息是:
1
2
3
4<servlet>
<servlet-name>configTest</servlet-name>
<servlet-class>com.sunlie.javaweb.servlet.ConfigTestServlet</servlet-class>
</servlet> - 也就是
<servlet></servlet>
标签中的信息
- ServletConfig 对象中包装的信息是:
- 两个方法:
- getInitParameterNames() 与 getInitParameter(s)
- 修改配置文件 web.xml 如下,加入了初始化参数
<init-param>
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
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<servlet>
<servlet-name>configTest</servlet-name>
<servlet-class>com.sunlie.javaweb.servlet.ConfigTestServlet</servlet-class>
<!--这儿可以配置 Servlet 初始化信息-->
<init-param>
<param-name>driver</param-name>
<param-value>com.mysql.cj.jdbc.Driver</param-value>
</init-param>
<init-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/sunlie</param-value>
</init-param>
<init-param>
<param-name>user</param-name>
<param-value>root</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>root</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>configTest</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
</web-app> - ConfigTestServlet.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
38package com.sunlie.javaweb.servlet;
import jakarta.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
public class ConfigTestServlet extends GenericServlet {
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
servletResponse.setContentType("text/html");
PrintWriter out = servletResponse.getWriter();
// 获取 ServletConfig 对象
ServletConfig config = this.getServletConfig();
out.print("ServletConfig = " + config + "<br>");
// ServletConfig = org.apache.catalina.core.StandardWrapperFacade@5bc5db7d
// 获取 <servlet></servlet>
String servletName = config.getServletName();
out.println("servletName = " + servletName + "<br>");
// servletName = configTest
// 获取 ServletConfig 对象中的 web.xml 中的初始化信息
Enumeration<String> initParameterNames = config.getInitParameterNames();
while (initParameterNames.hasMoreElements()) {
String s = initParameterNames.nextElement();
out.print(s + " = " + config.getInitParameter(s) + "<br>");
}
/*
password = root
driver = com.mysql.cj.jdbc.Driver
user = root
url = jdbc:mysql://localhost:3306/sunlie
*/
}
} - 于是可以得到配置文件中的信息
- 然后 jalarta.servlet.GenericServlet 这个抽象类已经继承了 Servlet, ServletConfig, Serializable 这三个接口,于是以上两个方法可以直接 this.getInitParameterNames() 和 this.getInitParameter(s) …
- GenericServlet 真是好…