JavaWeb

学习主要参考资料教程:动力节点最新JavaWeb视频教程_哔哩哔哩_bilibili

Servlet(Server Applet)
JSP
AJAX
MyBatis
Spring
SpringMVC
SpringBoot
SpringCloud

系统架构

系统架构主要分为C/S,B/S架构,两者优缺点如下

C/S架构

  • 优点
    1. 速度快(软件中的数据大部分都是集成到客户端软件当中的,很少量的数据从服务器端传送过来,所以C/S结构的系统速度快)
    2. 体验好,启动速度快,界面美观,可做出的界面特效较为丰富
    3. 服务器压力小(因为大量的数据都是集成在客户端软件当中,服务器只需要传送很少的数据量)
    4. 安全,大量数据直接存储本地计算机,保证一定的安全性,不受远程服务器的影响
  • 缺点
    1. 升级维护困难,有些软件安装所需环境较为苛刻,安装难度大

B/S架构

  • 优点
    1. 使用方便,只需安装浏览器软件,输入域名即可访问
    2. 升级维护简单,大量工作位于服务器端,客户端无需进行操作
  • 缺点
    1. 速度慢(数据基本存储于服务器端,每次请求都需要耗费时间去进行,所需网络数据量较大)
    2. 体验没有C端好,界面相较于C端较为简单
    3. 数据全部存储于服务器端,在安全上有一定风险

Web应用

JAVA

参考资料:[JAVA SE_百度百科 (baidu.com)](https://baike.baidu.com/item/JAVA SE/4662159?fr=aladdin)

JavaSE

Java SE 是Java平台标准版的简称(Java Platform, Standard Edition) (also known as Java 2 Platform) ,用于开发和部署桌面、服务器以及嵌入设备和实时环境中的Java应用程序。Java SE包括用于开发Java Web服务的类库,同时,Java SEJava EE和Java ME提供了基础。Java SEJava Platform, Standard Edition,Java标准版)就是基于JDKJRE,包含支持 Java Web 服务开发的类,并为 Java 企业级开发提供基础

JavaEE

Java EE是一种利用Java2平台来简化企业解决方案的开发、部署和管理相关的复杂问题的体系结构。可以用于开发web系统

关于JavaEE的版本

  • JavaEE目前最高版本是 JavaEE8

  • JavaEE被Oracle捐献了,Oracle将JavaEE规范捐献给Apache了。Apache把JavaEE换名了,以后不叫JavaEE了,叫做 jakarta EE。

  • JavaEE8版本升级之后的”JavaEE 9”,不再是”JavaEE9”这个名字了,叫做JakartaEE9

  • JavaEE8的时候对应的Servlet类名是:javax.servlet.Servlet

  • JakartaEE9的时候对应的Servlet类名是:jakarta.servlet.Servlet

  • 如果你之前的项目还是在使用javax.servlet.Servlet,那么你的项目无法直接部署到Tomcat10+版本上。你只能部署到Tomcat9-版本上。在Tomcat9以及Tomcat9之前的版本中还是能够识别javax.servlet这个包。

  • tomcat提供了迁移工具进行高版本升级:github.com

    image-20220724115209781

JavaME

Java MEJava微版的简称(Java Platform,Micro Edition),是一个技术和规范的集合,它为移动设备(包括消费类产品、嵌入式设备、高级移动设备等)提供了基于Java环境的开发与应用平台。Java ME分为两类配置,一类是面向小型移动设备的CLDCConnected Limited Device Profile),一类是面向功能更强大的移动设备如智能手机和机顶盒,称为CDCConnected Device Profile CDC)。

B/S通信原理

参考资料:分类: 计算机网络 | Dong (gitee.io)

浏览器从输入URL到页面加载的全过程 - SegmentFault 思否

  1. 第一步:用户输入网址(URL)
  2. 查找缓存:浏览器先查看浏览器缓存-系统缓存-路由缓存中是否有该地址页面,如果有则显示页面内容。如果没有则进行下一步。
  3. 第二步:域名解析器进行域名解析 -> DNS域名解析([计算机网络-应用层 | Dong (gitee.io)](https://ypigy.gitee.io/2021/04/18/应用层/#域名系统 DNS))
  4. 第三步:建立TCP连接:解析出IP地址后,根据IP地址和默认80端口,和服务器建立TCP连接(计算机网络-运输层 | Dong (gitee.io),)
  5. 第四步:发起HTTP请求:浏览器发起读取文件的HTTP请求,,该请求报文作为TCP三次握手的第三次数据发送给服务器(计算机网络-应用层 | Dong (gitee.io)
  6. 第五步:服务器响应请求并返回结果:服务器对浏览器请求做出响应,并把对应的html文件发送给浏览器
  7. 第六步:关闭TCP连接:通过四次挥手释放TCP连接(计算机网络-运输层 | Dong (gitee.io)
  8. 第七步:客户端(浏览器)解析HTML内容并渲染出来
  9. 第八步:JS引擎解析过程:调用JS引擎执行JS代码(JS的解释阶段,预处理阶段,执行阶段生成执行上下文,VO,作用域链、回收机制等等)

Web应用的参与角色

图源老杜

BS结构系统的通信原理2

遵循的规划与协议

  1. webapp的开发团队 和 WEB Server的开发团队 之间有一套规范: JavaEE规范之一Servlet规范。
    • Servlet规范的作用是什么?
      • WEB Server 和 webapp解耦合。
  2. Browser 和 WebServer之间有一套传输协议:HTTP协议。(超文本传输协议。)
  3. webapp开发团队 和 DB Server的开发团队之间有一套规范:JDBC规范。

BS结构系统的角色和协议(图源老杜)

Tomcat

简介

omcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。

Web服务器

  1. Node服务器:/koa/express
  2. Tomcat
  3. Nginx

Tomcat环境配置

配置环境变量

  1. JAVA_HOME,变量值为java jdk安装目录:

    image-20220724105405790

  2. PATH:%JAVA_HOME%\bin

  3. CATALINA_HOME,变量值为tomcat安装目录:

    image-20220724105600320

  4. PATH:%CATALINA_HOME%\lib

  5. PATH:%CATALINA_HOME%\bin

配置原理:通过tomcat bin 目录下的starup脚本文件可以得出,其所需要引用一个CATALINA_HOME环境变量

image-20220724110256242

JAVA_HOME同理:你可以在没有该环境变量的情况下编译运行java程序,但是tomcat会需要引用该环境变量去启动服务器 -> catalina.bat

image-20220724110226085

测试

启动服务器:在控制台输入:starup.bat

关闭服务器:在控制台输入:shutdown.bat

启动Tomcat中文乱码(控制台)问题

将%CATALINA_HOME%/conf/logging.properties文件中的内容修改如下:

java.util.logging.ConsoleHandler.encoding = GBK

简单的web程序

\apache-tomcat-10.0.22\webapps下随便新建一个文件夹,如oa

在oa下新建或放入一个html文件

image-20220724111828121

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>你好啊</h1>
<p>大家好呀</p>
<a href="/oa/abc">hhh</a>
<a href="/oa/emp">员工表</a>
</body>
</html>

浏览器访问localhost:8080/oa即可浏览您的第一个web程序,注意:这个时候的页面被称作静态页面,因为无后端数据库等交互

image-20220724111737544

tomcat目录

  • bin : 这个目录是Tomcat服务器的命令文件存放的目录,比如:启动Tomcat,关闭Tomcat等。
  • conf: 这个目录是Tomcat服务器的配置文件存放目录。(server.xml文件中可以配置端口号,默认Tomcat端口是8080)
  • lib :这个目录是Tomcat服务器的核心程序目录,因为Tomcat服务器是Java语言编写的,这里的jar包里面都是class文件。
  • logs: Tomcat服务器的日志目录,Tomcat服务器启动等信息都会在这个目录下生成日志文件。
  • temp:Tomcat服务器的临时目录。存储临时文件。
  • webapps:这个目录当中就是用来存放大量的webapp(web application:web应用)
  • work:这个目录是用来存放JSP文件翻译之后的java文件以及编译之后的class文件。

Servlet

参考资料:菜鸟教程

什么是Servlet

Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。

使用 Servlet,您可以收集来自网页表单的用户输入,呈现来自数据库或者其他源的记录,还可以动态创建网页。

Java Servlet 通常情况下与使用 CGI(Common Gateway Interface,公共网关接口)实现的程序可以达到异曲同工的效果。但是相比于 CGI,Servlet 有以下几点优势:

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

servlet架构

image-20220724100222191

Servlet规范包括

  • 规范了哪些接口
  • 规范了哪些类
  • 规范了一个web应用中应该有哪些配置文件
  • 规范了一个web应用中配置文件的名字
  • 规范了一个web应用中配置文件存放的路径
  • 规范了一个web应用中配置文件的内容
  • 规范了一个合法有效的web应用它的目录结构应该是怎样的。
  • …..

简单的Servlet程序

文件路径:src -> HelloWorld

webapp目录结构

1
2
3
4
5
6
7
8
9
10
webapproot
|------WEB-INF
|------classes(存放字节码)
|------lib(第三方jar包)
|------web.xml(注册Servlet)
|------html
|------css
|------javascript
|------image
....
  • WEB-INF:Servlet规范中规定的,必须全部大写,必须一模一样,该文件夹下的资源是受保护的,无法访问

  • classes:这个目录存放Java程序编译之后的class文件

  • lib:这个目录不是必须的。但如果一个webapp需要第三方的jar包的话,这个jar包要放到这个lib目录下

  • web.xml:在这个配置文件中描述了请求路径和Servlet类之间的对照关系

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <?xml version="1.0" encoding="UTF-8"?>

    <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>

    按以上所示,新建目录结构

image-20220724113558864

Servlet编写

在任意位置创建一个java程序

在service写代码,每次请求该类对应的url路径是,则会触发并执行该方法

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
import jakarta.servlet.*;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.ResponseCache;

public class HelloWorld implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {

}

@Override
public ServletConfig getServletConfig() {
return null;
}

@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("Hello Servlet");
PrintWriter out = servletResponse.getWriter();
// todo 响应类型应该在输出内容前设置
servletResponse.setContentType("text/html");
out.print("Hello, Servlet");
out.print("<p>Hello, Servlet</p>");
}

@Override
public String getServletInfo() {
return null;
}

@Override
public void destroy() {

}
}

编译

配置环境变量CLASSPATH:CLASSPATH=.;toncat安装目录\apache-tomcat-10.0.12\lib\servlet-api.jar

执行编译

1
javac -d . java文件

将编译后的class文件拷贝至WEB-INF\classes目录下,注意:如果是在包下创建的java程序,需要连同文件夹一起复制过去

在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
<?xml version="1.0" encoding="UTF-8"?>

<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都对应一个servlet-mapping -->
<servlet>
<servlet-name>abc</servlet-name>
<!--这个位置必须是带有包名的全限定类名-->
<servlet-class>HelloWorld</servlet-class>
</servlet>
<!--servlet映射信息-->
<servlet-mapping>
<!--这里的内容要和上面一致-->
<servlet-name>abc</servlet-name>
<!--这里需要一个路径-->
<!--这个路径唯一的要求是必须以 / 开始-->
<url-pattern>/abc</url-pattern>
</servlet-mapping>
</web-app>

重写启动Tomcat服务器

重新启动并在浏览器输入url即可在控制台看到输出内容

image-20220724114808095

Tomcat解析过程

  1. 用户输入URL
  2. 然后Tomcat服务器接收到请求,截取路径
  3. Tomcat服务器找到项目
  4. Tomcat服务器在web.xml文件中查找url 对应的Servlet
  5. Tomcat服务器通过反射机制,创建对象。
  6. Tomcat服务器调用service方法。

简单的响应html

1
2
3
4
5
6
7
8
9
10
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
//todo 获取PrintWriter,用于响应输出
PrintWriter out = servletResponse.getWriter();
// todo 响应类型应该在输出内容前设置
servletResponse.setContentType("text/html");
//输出内容
out.print("Hello, Servlet");
out.print("<p>Hello, Servlet</p>");
}

JDBC与Servlet

文件路径:src -> HelloWorld02 -> JDBC01

进行数据库和Servlet的连接

  • 实现接口Servlet
  • servletResponse设置返回值类型:”text/html”
  • 获取返回值对象
  • 数据库查询,将结果输出至浏览器
  • lib文件夹下引入mysql jar包

servlet中调用JDBC报错:java.lang.ClassNotFoundException: com.mysql.jdbc.Driver

Servlet对象的生命周期

Servlet对象的创建,对象上方法的调用,对象最终的销毁,Javaweb程序员是无权干预的。

Servlet对象的生命周期是由Tomcat服务器(WEB Server)全权负责的。

Tomcat服务器通常我们又称为:WEB容器(WEB Container)

WEB容器来管理Servlet对象的生命周期

我们new的Servlet对象是不受WEB容器管理的

  • WEB容器创建的Servlet对象,这些Servlet对象都会被放到一个集合当中(HashMap),只有放到这个HashMap集合中的Servlet才能够被WEB容器管理,自己new的Servlet对象不会被WEB容器管理。(自己new的Servlet对象不在容器当中)

  • web容器底层应该有一个HashMap这样的集合,在这个集合当中存储了Servlet对象和请求路径之间的关系

    WEB容器中的Map集合(图源老杜)

    默认情况下,服务器在启动的时候Servlet对象并不会被实例化

    • 这个设计是合理的。用户没有发送请求之前,如果提前创建出来所有的Servlet对象,必然是耗费内存的,并且创建出来的Servlet如果一直没有用户访问,显然这个Servlet对象会浪费空间,所以没必要先创建

    服务器启动时创建Servlrt对象

    • 在servlet标签中添加子标签,在该子标签中填写整数,越小的整数优先级越高

      1
      2
      3
      4
      5
      6
      7
      8
      9
      <servlet>
      <servlet-name>abc</servlet-name>
      <servlet-class>HelloWorld</servlet-class>
      <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
      <servlet-name>abc</servlet-name>
      <url-pattern>/abc</url-pattern>
      </servlet-mapping>

      生命周期

定义如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class LifeCycle implements Servlet {
public LifeCycle() {
System.out.println("构造函数执行");
}

@Override
public void init(ServletConfig servletConfig) throws ServletException {
System.out.println("init方法执行");
}

@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service方法执行");
}

@Override
public void destroy() {
System.out.println("destroy方法执行");
}
}

默认情况下服务器启动的时候AServlet对象并没有被实例化

当浏览器第一次输入指定网址,控制台输出

1
2
3
构造函数执行
init方法执行
service方法执行

接下来每次输入,控制台只会输出一句

1
service方法执行

关闭服务器,控制台输出

1
destroy方法执行

由上得出

  • 用户在发送第一次请求的时候Servlet对象被实例化
  • AServlet对象被创建出来之后,Tomcat服务器马上调用了AServlet对象的init方法。
  • 用户发送第一次请求的时候,init方法执行之后,Tomcat服务器马上调用Servlet对象的service方法。
  • 之后每次访问,Servlet对象并没有新建,还是使用之前创建好的Servlet对象,直接调用该Servlet对象的service方法
  • Servlet的destroy方法只被Tomcat服务器调用一次。

总结

  1. Servlet对象是单例的( Servlet类并不符合单例模式。我们称之为假单例。之所以单例是因为Servlet对象的创建 程序员管不着,这个对象的创建只能由Tomcat来说了算,Tomcat只创建了一个,所以导致了单例,但是属于假单例。真单例模式,构造方法是私有化的。)

  2. 无参数构造方法、init方法只在第一次用户发送请求的时候执行。也就是说无参数构造方法只执行一次。init方法也只被Tomcat服务器调用一次

  3. 用户每发送一次请求:service方法必然会被Tomcat服务器调用一次

  4. 服务器在销毁AServlet对象内存之前,Tomcat服务器会自动调用AServlet对象的destroy方法

  5. Servlet类中方法的调用次数

    • 构造方法只执行一次。
    • init方法只执行一次。
    • service方法:用户发送一次请求则执行一次,发送N次请求则执行N次。
    • destroy方法只执行一次。

注意:当我们Servlet类中编写一个有参数的构造方法时,会发生500服务器错误,无法实例化Servlet对象,尽量不要自定义构造函数,否则可能会遗忘创建无参数构造函数

思考:Servlet的无参数构造方法是在对象第一次创建的时候执行,并且只执行一次。init方法也是在对象第一次创建的时候执行,并且只执行一次。那么这个无参数构造方法可以代替掉init方法吗

回答:Servlet规范中有要求,作为javaweb程序员,编写Servlet类的时候,不建议手动编写构造方法,因为编写构造方法,很容易让无参数构造方法消失,这个操作可能会导致Servlet对象无法实例化。所以init方法是有存在的必要的

  1. init一般用于初始化操作,如:初始化数据库连接池,初始化线程池….
  2. destroy方法一般用于资源的关闭

GenericServlet

适配器模式Adapter实现Servlet封装

HttpServlet

HttpServlet源码分析

  • HttpServlet类是专门为HTTP协议准备的。比GenericServlet更加适合HTTP协议下的开发。
  • HttpServlet在哪个包下?
    • jakarta.servlet.http.HttpServlet
  • http包下都有哪些类和接口呢?jakarta.servlet.http.;
    • jakarta.servlet.http.HttpServlet (HTTP协议专用的Servlet类,抽象类)
    • jakarta.servlet.http.HttpServletRequest (HTTP协议专用的请求对象)
    • jakarta.servlet.http.HttpServletResponse (HTTP协议专用的响应对象)
  • HttpServletRequest对象中封装了什么信息?
    • HttpServletRequest,简称request对象。
    • HttpServletRequest中封装了请求协议的全部内容。
    • Tomcat服务器(WEB服务器)将“请求协议”中的数据全部解析出来,然后将这些数据全部封装到request对象当中了。
    • 也就是说,我们只要面向HttpServletRequest,就可以获取请求协议中的数据。
  • HttpServletResponse对象是专门用来响应HTTP协议到浏览器的。
  • 回忆Servlet生命周期?
    • 用户第一次请求
      • Tomcat服务器通过反射机制,调用无参数构造方法。创建Servlet对象。(web.xml文件中配置的Servlet类对应的对象。)
      • Tomcat服务器调用Servlet对象的init方法完成初始化。
      • Tomcat服务器调用Servlet对象的service方法处理请求。
    • 用户第二次请求
      • Tomcat服务器调用Servlet对象的service方法处理请求。
    • 用户第三次请求
      • Tomcat服务器调用Servlet对象的service方法处理请求。
    • ….
      • Tomcat服务器调用Servlet对象的service方法处理请求。
    • 服务器关闭
      • Tomcat服务器调用Servlet对象的destroy方法,做销毁之前的准备工作。
      • Tomcat服务器销毁Servlet对象。
  • HttpServlet源码分析:
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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
public class HelloServlet extends HttpServlet {
// 用户第一次请求,创建HelloServlet对象的时候,会执行这个无参数构造方法。
public HelloServlet() {
}

//override 重写 doGet方法
//override 重写 doPost方法
}

public abstract class GenericServlet implements Servlet, ServletConfig,
java.io.Serializable {

// 用户第一次请求的时候,HelloServlet对象第一次被创建之后,这个init方法会执行。
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
// 用户第一次请求的时候,带有参数的init(ServletConfig config)执行之后,会执行这个没有参数的init()
public void init() throws ServletException {
// NOOP by default
}
}

// HttpServlet模板类。
public abstract class HttpServlet extends GenericServlet {
// 用户发送第一次请求的时候这个service会执行
// 用户发送第N次请求的时候,这个service方法还是会执行。
// 用户只要发送一次请求,这个service方法就会执行一次。
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {

HttpServletRequest request;
HttpServletResponse response;

try {
// 将ServletRequest和ServletResponse向下转型为带有Http的HttpServletRequest和HttpServletResponse
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException(lStrings.getString("http.non_http"));
}
// 调用重载的service方法。
service(request, response);
}

// 这个service方法的两个参数都是带有Http的。
// 这个service是一个模板方法。
// 在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成。
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 获取请求方式
// 这个请求方式最终可能是:""
// 注意:request.getMethod()方法获取的是请求方式,可能是七种之一:
// GET POST PUT DELETE HEAD OPTIONS TRACE
String method = req.getMethod();

// 如果请求方式是GET请求,则执行doGet方法。
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}

} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);

} else if (method.equals(METHOD_POST)) {
// 如果请求方式是POST请求,则执行doPost方法。
doPost(req, resp);

} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);

} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);

} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);

} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);

} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//

String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);

resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}


protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException{
// 报405错误
String msg = lStrings.getString("http.method_get_not_supported");
sendMethodNotAllowed(req, resp, msg);
}

protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
// 报405错误
String msg = lStrings.getString("http.method_post_not_supported");
sendMethodNotAllowed(req, resp, msg);
}

}

/*
通过以上源代码分析:
假设前端发送的请求是get请求,后端程序员重写的方法是doPost
假设前端发送的请求是post请求,后端程序员重写的方法是doGet
会发生什么呢?
发生405这样的一个错误。
405表示前端的错误,发送的请求方式不对。和服务器不一致。不是服务器需要的请求方式。

通过以上源代码可以知道:只要HttpServlet类中的doGet方法或doPost方法执行了,必然405.

怎么避免405的错误呢?
后端重写了doGet方法,前端一定要发get请求。
后端重写了doPost方法,前端一定要发post请求。
这样可以避免405错误。


有的人,你会看到为了避免405错误,在Servlet类当中,将doGet和doPost方法都进行了重写。
这样,确实可以避免405的发生,但是不建议,405错误还是有用的。该报错的时候就应该让他报错。
如果你要是同时重写了doGet和doPost,那还不如你直接重写service方法好了。这样代码还能
少写一点。
*/
  • 我们编写的HelloServlet直接继承HttpServlet,直接重写HttpServlet类中的service()方法行吗?

    • 可以,只不过你享受不到405错误。享受不到HTTP协议专属的东西。
  • Servlet类的开发步骤:

    • 第一步:编写一个Servlet类,直接继承HttpServlet
    • 第二步:重写doGet方法或者重写doPost方法,到底重写谁,javaweb程序员说了算。
    • 第三步:将Servlet类配置到web.xml文件当中。
    • 第四步:准备前端的页面(form表单),form表单中指定请求路径即可。

HttpServletRequest接口

  • HttpServletRequest是一个接口,全限定名称:jakarta.servlet.http.HttpServletRequest

  • HttpServletRequest接口是Servlet规范中的一员。

  • HttpServletRequest接口的父接口:ServletRequest

    • public interface HttpServletRequest extends ServletRequest {}
      <!--code13-->
      

HttpServletRequest接口常用的方法

1
2
3
4
5
Map<String,String[]> getParameterMap() //这个是获取Map
Enumeration<String> getParameterNames() //这个是获取Map集合中所有的key
String[] getParameterValues(String name) //根据key获取Map集合的value
String getParameter(String name) //获取value这个一维数组当中的第一个元素。这个方法最常用。
// 以上的4个方法,和获取用户提交的数据有关系。

前端永远提交的是字符串,后端获取的也永远是字符串

后端一般使用map集合获取前端传参,因为前端可能传递多个同类型的数据,如checkbox,所以后端统一用数组进行值存储

1
2
3
Map<String, String[]>
key存储String
value存储String[]

请求域

  • 请求域”对象要比“应用域”对象范围小很多。生命周期短很多。请求域只在一次请求内有效。

  • 一个请求对象request对应一个请求域对象。一次请求结束之后,这个请求域就销毁了。

  • 请求域对象三个方法:

    1
    2
    3
    void setAttribute(String name, Object obj); // 向域当中绑定数据。
    Object getAttribute(String name); // 从域当中根据name获取数据。
    void removeAttribute(String name); // 将域当中绑定的数据移除
  • 请求域和应用域的选用原则?

    • 尽量使用小的域对象,因为小的域对象占用的资源较少。

转发

两个Servlet怎么共享数据?

  • 将数据放到ServletContext应用域当中,当然是可以的,但是应用域范围太大,占用资源太多。不建议使用。
  • 可以将数据放到request域当中,然后AServlet转发到BServlet,保证AServlet和BServlet在同一次请求当中,这样就可以做到两个Servlet,或者多个Servlet共享同一份数据

转发的时候是一次请求,不管你转发了多少次。都是一次请求。

AServlet转发到BServlet,再转发到CServlet,再转发到DServlet,不管转发了多少次,都在同一个request当中。

这是因为调用forward方法的时候,会将当前的request和response对象传递给下一个Servlet。

1
2
3
4
5
6
7
// 第一步:获取请求转发器对象
RequestDispatcher dispatcher = request.getRequestDispatcher("/b");
// 第二步:调用转发器的forward方法完成跳转/转发
dispatcher.forward(request,response);

// 第一步和第二步代码可以联合在一起。
request.getRequestDispatcher("/b").forward(request,response);

重定向

1
2
3
4
5
// 注意:路径上要加一个项目名。为什么?
// 浏览器发送请求,请求路径上是需要添加项目名的。
// 以下这一行代码会将请求路径“/oa/dept/list”发送给浏览器
// 浏览器会自发的向服务器发送一次全新的请求:/oa/dept/list
response.sendRedirect("/oa/dept/list");

转发和重定向的本质区别?

  • 转发:是由WEB服务器来控制的。A资源跳转到B资源,这个跳转动作是Tomcat服务器内部完成的。
  • 重定向:是浏览器完成的。具体跳转到哪个资源,是浏览器说了算。
  • 如果在上一个Servlet当中向request域当中绑定了数据,希望从下一个Servlet当中把request域里面的数据取出来,使用转发机制。
  • 剩下所有的请求均使用重定向。(重定向使用较多。)

转发:

转发

重定向:

重定向

Servlet注解

-> Servlet10

使用模板方法设计模式优化oa项目

-> OA2

Session

-> Servlet11

-> Servlet12

JSP

JSP基础语法总结:

  • JSP中直接编写普通字符串
    • 翻译到service方法的out.write(“这里”)
  • <%%>
    • 翻译到service方法体内部,里面是一条一条的java语句。
  • <%! %>
    • 翻译到service方法之外。类体
  • <%= %>
    • 翻译到service方法体内部,翻译为:out.print();
  • <%@page contentType=”text/html;charset=UTF-8”%>
    • page指令,通过contentType属性用来设置响应的内容类型。

JSP文件的扩展名必须是xxx.jsp吗?

  • jsp文件的扩展名是可以配置的。不是固定的。

  • 在CATALINA_HOME/conf/web.xml,在这个文件当中配置jsp文件的扩展名。

  • <servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspx</url-pattern>
    </servlet-mapping>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    - xxx.jsp文件对于小猫咪来说,只是一个普通的文本文件,web容器会将xxx.jsp文件最终生成java程序,最终调用的是java对象相关的方法,真正执行的时候,和jsp文件就没有关系了。

    - JSP的指令

    - 指令的作用:指导JSP的翻译引擎如何工作(指导当前的JSP翻译引擎如何翻译JSP文件。)

    - 指令包括哪些呢?

    - include指令:包含指令,在JSP中完成静态包含,很少用
    - taglib指令:引入标签库的指令。这个到JJSTL标签库的时候再学习。现在先不管。
    - page指令:目前重点学习一个page指令。

    - 指令的使用语法是什么?

    - <%@指令名 属性名=属性值 属性名=属性值 属性名=属性值....%>

    - 关于page指令当中都有哪些常用的属性呢?

    <%@page session="true|false" %> true表示启用JSP的内置对象session,表示一定启动session对象。没有session对象会创建。 如果没有设置,默认值就是session="true" session="false" 表示不启动内置对象session。当前JSP页面中无法使用内置对象session。
    1

    <%@page contentType="text/json" %> contentType属性用来设置响应的内容类型 但同时也可以设置字符集。 <%@page contentType="text/json;charset=UTF-8" %>
    1

    <%@page pageEncoding="UTF-8" %> pageEncoding="UTF-8" 表示设置响应时采用的字符集。
    1

    <%@page import="java.util.List, java.util.Date, java.util.ArrayList" %> <%@page import="java.util.*" %> import语句,导包。
    1

    <%@page errorPage="/error.jsp" %> 当前页面出现异常之后,跳转到error.jsp页面。 errorPage属性用来指定出错之后的跳转位置。
    1

    <%@page isErrorPage="true" %> 表示启用JSP九大内置对象之一:exception 默认值是false。

EL表达式、JSTL标签库

49-JSP的page指令以及九大内置对象和EL表达式_哔哩哔哩_bilibili(p49 - 55 未看)

Filter过滤器

-> Servlet13

  • Filter是过滤器。
  • Filter可以在Servlet这个目标程序执行之前添加代码。也可以在目标Servlet执行之后添加代码。之前之后都可以添加过滤规则。
  • 一般情况下,都是在过滤器当中编写公共代码。

注意:

  • Servlet对象默认情况下,在服务器启动的时候是不会新建对象的。
  • Filter对象默认情况下,在服务器启动的时候会新建对象。
  • Servlet是单例的。Filter也是单例的。(单实例。)
  • Filter的优先级,天生的就比Servlet优先级高。

关于Filter的配置路径:

  • /a.do、/b.do、/dept/save。这些配置方式都是精确匹配。
  • /* 匹配所有路径。
  • *.do 后缀匹配。不要以 / 开始
  • /dept/* 前缀匹配。

在web.xml文件中进行配置的时候,Filter的执行顺序是什么?

  • 依靠filter-mapping标签的配置位置,越靠上优先级越高。

使用@WebFilter的时候,Filter的执行顺序是怎样的呢?

  • 执行顺序是:比较Filter这个类名。
  • 比如:FilterA和FilterB,则先执行FilterA。
  • 比如:Filter1和Filter2,则先执行Filter1.

Filter的生命周期?

  • 和Servlet对象生命周期一致。
  • 唯一的区别:Filter默认情况下,在服务器启动阶段就实例化。Servlet不会。

责任链设计模式。

过滤器最大的优点:

  • 在程序编译阶段不会确定调用顺序。因为Filter的调用顺序是配置到web.xml文件中的,只要修改web.xml配置文件中filter-mapping的顺序就可以调整Filter的执行顺序。显然Filter的执行顺序是在程序运行阶段动态组合的。那么这种设计模式被称为责任链设计模式。

责任链设计模式最大的核心思想:

  • 在程序运行阶段,动态的组合程序的调用顺序。

Listener监听器

-> Servlet14

-> OA3 实现在线人数统计

什么是监听器?

  • 监听器是Servlet规范中的一员。就像Filter一样。Filter也是Servlet规范中的一员。
  • 在Servlet中,所有的监听器接口都是以“Listener”结尾。

监听器有什么用?

  • 监听器实际上是Servlet规范留给我们javaweb程序员的特殊时机。
  • 特殊的时刻如果想执行这段代码,你需要想到使用对应的监听器。

Servlet规范的监听器类型

  • jakarta.servlet包下:
    • ServletContextListener
    • ServletContextAttributeListener
    • ServletRequestListener
    • ServletRequestAttributeListener
  • jakarta.servlet.http包下:
    • HttpSessionListener
    • HttpSessionAttributeListener
      • 该监听器需要使用@WebListener注解进行标注。
      • 该监听器监听的是什么?是session域中数据的变化。只要数据变化,则执行相应的方法。主要监测点在session域对象上。
    • HttpSessionBindingListener
      • 该监听器不需要使用@WebListener进行标注。
      • 假设User类实现了该监听器,那么User对象在被放入session的时候触发bind事件,User对象从session中删除的时候,触发unbind事件。
      • 假设Customer类没有实现该监听器,那么Customer对象放入session或者从session删除的时候,不会触发bind和unbind事件。
    • HttpSessionIdListener
      • session的id发生改变的时候,监听器中的唯一一个方法就会被调用。
    • HttpSessionActivationListener
      • 监听session对象的钝化和活化的。
      • 钝化:session对象从内存存储到硬盘文件。
      • 活化:从硬盘文件把session恢复到内存。