0%

一.Maven简介
1.1 Maven是什么
Maven 是一个项目管理工具,它包含了一个项目对象模型 (POM:Project Object Model),一组标准集合,一个项 目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周 期阶段(phase)中插件(plugin)目标(goal)的逻辑。

1.2 Maven能解决什么问题
一种工具,可以构建工程,管理 jar包,编译代码,还 能帮你自动运行单元测试,打包,生成报表,部署项目,生成 Web 站点。

1.3 Maven的两个核心功能
1.3.1 依赖管理
Maven的一个核心特性就是依赖管理。MVN依赖管理,就是一个管理jar包的过程

CRM项目
使用mvn工程实现 ➡项目中给定坐标,去仓库拉取jar包➡
索引的存在,使拉取jar包过程,可以认为是自己项目中有的 MVN仓库
jar的仓库
坐标:将来要找某个jar包,就是要确定这个jar包的坐标
jar包如何给出坐标:公司/组织名+项目名+版本号一起确定
maven工程中不直接将jar包导入到工程中,而是通过在pom.xml文件中添加所需jar包的坐标,避免了jar直接引入进来,在需要用到jar包的时候,只要查找pom.xml文件,再通过pom.xml文件中的坐标,到一个专门用于”存放jar包的仓库”(maven仓库)中根据坐标从而找到这些jar包,再把这些jar包拿去运行。

通过读取pom.xml 文件中的坐标,再到仓库中找到jar包,会不会很慢

通过pom.xml文件配置要引入的jar包的坐标,再读取坐标并到仓库中加载jar包,这样我们就可以直接使用jar包,为了解决这个过程中速度慢的问题,maven中也有索引的概念,通过建立索引,可以大大提高加载jar包的速度,使得我们认为jar包基本跟放在本地的工程文件中再读取出来的速度是一样的。

1.3.2 项目构建
什么是项目构建

项目从编译、测试、运行、打包、安装 ,部署整个过程都交给maven进行管理,这个过程称为构建。

一键构建 指的是整个构建过程,使用maven一个命令可以轻松完成整个工作。

Maven规范化构建流程

清理 ➡ 编译 ➡ 测试 ➡ 报告 ➡ 打包 ➡ 部署
二 Maven安装和使用
2.1 Maven下载和安装
2.1.1 Maven下载
Maven官网下载地址:http://maven.apache.org/download.cgi

目前使用@3.5版本

2.1.2 Maven安装
将apache-maven-3.5.2-bin.zip解压并添加至path

maven目录结构

目录 介绍
bin 存放了 maven 的命令
boot 存放了一些 maven 本身的引导程序,如类加载器等
conf 存放了 maven 的一些配置文件,如 setting.xml 文件
lib 存放了 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
apache-maven-3.5.2
├── LICENSE
├── NOTICE
├── README.txt
├── bin
│ ├── m2.conf
│ ├── mvn
│ ├── mvn.cmd
│ ├── mvnDebug
│ ├── mvnDebug.cmd
│ └── mvnyjp
├── boot
│ └── plexus-classworlds-2.5.2.jar
├── conf
│ ├── logging
│ ├── settings.xml
│ └── toolchains.xml
└── lib
├── aopalliance-1.0.jar
├── …省略
└── wagon-provider-api.license

7 directories, 91 files
2.1.3 Maven及JDK配置
Maven 3.3+ require JDK 1.7 or above to execute - they still allow you to build against 1.3 and other JDK versions by Using Toolchains

配置PATH

1
2
3
4
5
vi .zshrc

#MAVEN_HOME
export MAVEN_HOME=/Users/swzxsyh/Program/apache-maven-3.5.2
export PATH=$PATH:$MAVEN_HOME/bin
2.1.4 Maven软件版本测试
1
2
3
4
5
6
7
~ #mvn -v
Apache Maven 3.5.2 (138edd61fd100ec658bfa2d307c43b76940a5d7d; 2017-10-18T15:58:13+08:00)
Maven home: /Users/swzxsyh/Program/apache-maven-3.5.2
Java version: 1.8.0_221, vendor: Oracle Corporation
Java home: /Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/jre
Default locale: en_CN, platform encoding: UTF-8
OS name: “mac os x”, version: “10.15.4”, arch: “x86_64”, family: “mac”
2.3 Maven仓库
2.3.1 Maven仓库的分类
Maven仓库分为本地仓库和远程仓库二大类。而远程仓库又可分成中央仓库,私服,第三方仓库。

Maven仓库
本地仓库 远程仓库

中央仓库
私服
第三方公共库
依赖寻找流程

在本地仓库中,根据Maven坐标系寻找指定依赖,如果存在,直接返回。

如果Maven仓库中不存在,或者需要检查依赖的版本时,maven则会去远程仓库中寻找,下载到本地仓库中再使 用。

本地仓库

用来存储从远程仓库下载的插件和jar包,项目使用一些插件或jar包,优先从本地仓库查找。

中央仓库

在maven软件中内置一个远程仓库地址http://repo1.maven.org/maven2 ,它是中央仓库,服务于整个互联网,它是由Maven团队自己维护,里面存储了非常全的jar包,它包含了世界上大部分流行的开源项目构件。

第三方公共库

Maven 仓库默认中央仓库在国外且只有一个, 国内使用难免很慢,我们可以更换为第三方公共库,例如:阿里云镜 像。

私服

私服是一种特殊的远程仓库,其内容是来自于其他的远程仓库,一般架设在局域网内,提供给一个组织的人员使用。 当Maven需要下载依赖时,从私服请求,如果私服上不存在该依赖,则从其他远程仓库下载,同时缓存在私服上,提 供给其他人使用。如果项目中的一些内部模块,无法发布到外部远程仓库中,也可发布在私服上,提供给项目中的其 他人员使用。

2.3.2 Maven本地仓库的配置
1
2
3
4
5
6
7
8
9
10
11
12
13
cd $MAVEN_HOME/conf
vi settings.xml

找到



/path/to/local/repo改为本地仓库路径添加到注释后
2.3.4 Maven仓库国内镜像配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21




alimaven
aliyun maven
http://maven.aliyun.com/nexus/content/groups/public/
central


2.3.4 全局setting与用户setting
maven仓库地址、私服等配置信息需要在setting.xml文件中配置,分为全局配置和用户配置。

在maven安装目录下 的有 conf/setting.xml文件,此setting.xml文件用于maven的所有project项目,它作为maven的全局配置。 如需要 个性配置则需要在用户配置中设置,用户配置的setting.xml文件默认的位置在:${user.dir} /.m2/settings.xml目录 中,${user.dir} 指windows 中的用户目录。

maven会先找用户配置,如果找到则以用户配置文件为准,否则使用全 局配置文件。

2.3 Maven坐标和依赖
Maven的一个核心的作用就是管理项目的依赖,引入我们所需的各种jar包等。为了能自动化的解析任何一个Java构 件,Maven必须将这些Jar包或者其他资源进行唯一标识,这是管理项目的依赖的基础,也就是我们要说的坐标。包 括我们自己开发的项目,也是要通过坐标进行唯一标识的,这样才能才其它项目中进行依赖引用。

坐标的定义元素如下

元素 作用
groupId 定义当前Maven项目名称
artifactId 定义项目模块
version 定义当前项目的当前版本
例如

创建一个Maven的web项目,在pom.xml文件中生成坐标

1
2
3
4
5

com.test
maven_helloword
1.0-SNAPSHOT
war
要引入junit的测试jar,只需要在pom.xml配置文件中配置引入junit的坐标依赖即可

1
2
3
4
5
6
7
8
9
10
11
12
13

junit junit 4.12 test 2.4 Maven工程的认识 2.4.1 Maven工程的目录结构 作为一个maven工程,它的src目录和pom.xml是必备的。

1
2
3
4
├── maven_java.iml
├── pom.xml
├── src
└── target
进入src目录后,我们发现它里面的目录结构如下

1
2
3
4
5
6
7
8
9
10
src
├── main
│ ├── java #项目代码
│ ├── resources #配置文件
│ └── webapp #页面资源
└── test
├── java #测试代码
└──resources

6 directories, 0 files
文件 说明
src/main/java 存放项目的.java文件
src/main/resources 存放项目资源文件,如spring, mybatis配置文件
src/test/java 存放所有单元测试.java文件,如junit测试类
src/test/resources 测试资源文件
target 项目输出位置,编译后的class文件会输出到此目录
pom.xml maven项目核心配置文
注意:如果是普通的java项目,那么就没有webapp目录。
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
完整java项目
java工程名(项目名)
|– src目录
|– main目录(主干代码)
|– java目录(java代码)
|– resources目录(配置文件)
|– test目录(测试代码)
|– java目录(java代码)
|– resources目录(配置文件)
|– pom.xml(maven工程核心配置文件)
|– target目录(存放编译后的class文件…..)

  • web项目【重点】
    web工程名(项目名)
    |– src目录
    |– main目录(主干代码)
    |– java目录(java代码)
    |– resources目录(配置文件)
    |– webapp目录(页面资源)
    |– WEB-INF
    |– web.xml(web工程核心配置文件)
    |– index.jsp
    |– css、js、img..
    |– test目录(测试代码)
    |– java目录(java代码)
    |– resources目录(配置文件)
    |– pom.xml(maven工程核心配置文件)
    |– target目录(存放编译后的class文件…..)

2.4.2 Maven工程的运行
进入maven工程目录(当前目录有pom.xml文件),运行tomcat7:run命令

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
~ mvn tomcat7:run

[INFO] Scanning for projects…
[INFO]
[INFO] ————————————————————————
[INFO] Building maven_web 1.0-SNAPSHOT
[INFO] ————————————————————————
[INFO]
[INFO] >>> tomcat7-maven-plugin:2.2:run (default-cli) > process-classes @ maven_web >>>
[INFO]
[INFO] — maven-resources-plugin:2.6:resources (default-resources) @ maven_web —
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] — maven-compiler-plugin:3.1:compile (default-compile) @ maven_web —
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/target/classes
[INFO]
[INFO] <<< tomcat7-maven-plugin:2.2:run (default-cli) < process-classes @ maven_web <<<
[INFO]
[INFO]
[INFO] — tomcat7-maven-plugin:2.2:run (default-cli) @ maven_web —
[INFO] Running war on http://localhost:8080/
[INFO] Creating Tomcat server configuration at /Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/target/tomcat
[INFO] create webapp with contextPath:
May 15, 2020 10:29:37 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler [“http-bio-8080”]
May 15, 2020 10:29:37 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Tomcat
May 15, 2020 10:29:37 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.47
May 15, 2020 10:29:38 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler [“http-bio-8080”]
根据上边的提示信息,访问http://localhost:8080/ 即可

三.Maven生命周期和插件
3.1 Maven常用命令和插件
3.1.1 clean
clean是maven工程的清理命令,执行 clean会删除target目录及内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
~ mvn clean

[INFO] Scanning for projects…
[INFO]
[INFO] ————————————————————————
[INFO] Building maven_web 1.0-SNAPSHOT
[INFO] ————————————————————————
[INFO]
[INFO] — maven-clean-plugin:2.5:clean (default-clean) @ maven_web —
[INFO] Deleting /Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/target
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 0.228 s
[INFO] Finished at: 2020-05-15T23:52:52+08:00
[INFO] Final Memory: 9M/309M
[INFO] ————————————————————————
3.1.2 compile
compile是maven工程的编译命令,作用是将src/main/java下的文件编译为class文件输出到target目录下。

将src中main目录下java代码进行编译,将src中main目录下配置抽取,输出到target目录: classes目录

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
~ mvn compile

[INFO] Scanning for projects…
[INFO]
[INFO] ————————————————————————
[INFO] Building maven_web 1.0-SNAPSHOT
[INFO] ————————————————————————
[INFO]
[INFO] — maven-resources-plugin:2.6:resources (default-resources) @ maven_web —
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] — maven-compiler-plugin:3.1:compile (default-compile) @ maven_web —
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/target/classes
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 0.952 s
[INFO] Finished at: 2020-05-15T23:56:28+08:00
[INFO] Final Memory: 13M/209M
[INFO] ————————————————————————

~ ll ./target/classes/cn/itcast/web/servlet/

total 8
-rw-r–r– 1 swzxsyh staff 1.1K May 15 23:56 HelloServlet.class
3.1.3 test
test是maven工程的测试命令 mvn test,会执行src/test/java下的单元测试类,并编译为class文件。

mvn test=>target编译main代码=>编译test代码=>执行所有测试代码,类名xxxTest结尾,必须有@Test注解的方法

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
~ mvn test

[INFO] Scanning for projects…
[INFO]
[INFO] ————————————————————————
[INFO] Building maven_web 1.0-SNAPSHOT
[INFO] ————————————————————————
[INFO]

#清理target目录
[INFO] — maven-resources-plugin:2.6:resources (default-resources) @ maven_web —
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
#编译src/main主干
[INFO] — maven-compiler-plugin:3.1:compile (default-compile) @ maven_web —
[INFO] Nothing to compile - all classes are up to date
[INFO]
#编译src/test测试目录
[INFO] — maven-resources-plugin:2.6:testResources (default-testResources) @ maven_web —
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] — maven-compiler-plugin:3.1:testCompile (default-testCompile) @ maven_web —
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/target/test-classes
[INFO]

针对test目录下的测试代码,逐一测试

[INFO] — maven-surefire-plugin:2.12.4:test (default-test) @ maven_web —
[INFO] Surefire report directory: /Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/target/surefire-reports


T E S T S

Running cn.itcast.test.HelloTest
仅在测试期有效
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.065 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 1.509 s
[INFO] Finished at: 2020-05-16T00:00:02+08:00
[INFO] Final Memory: 15M/212M
[INFO] ————————————————————————
3.1.4 package
package是maven工程的打包命令,对于java工程执行package打成jar包,对于web工程打成war包。

mvn package=>编译main代码=>编译test代码=>执行测试=>将项目打成war包

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
~ mvn package

[INFO] Scanning for projects…
[INFO]
[INFO] ————————————————————————
[INFO] Building maven_web 1.0-SNAPSHOT
[INFO] ————————————————————————
[INFO]
[INFO] — maven-resources-plugin:2.6:resources (default-resources) @ maven_web —
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] — maven-compiler-plugin:3.1:compile (default-compile) @ maven_web —
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] — maven-resources-plugin:2.6:testResources (default-testResources) @ maven_web —
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] — maven-compiler-plugin:3.1:testCompile (default-testCompile) @ maven_web —
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] — maven-surefire-plugin:2.12.4:test (default-test) @ maven_web —
[INFO] Surefire report directory: /Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/target/surefire-reports


T E S T S

Running cn.itcast.test.HelloTest
仅在测试期有效
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.048 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] — maven-war-plugin:2.2:war (default-war) @ maven_web —
[INFO] Packaging webapp
[INFO] Assembling webapp [maven_web] in [/Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/target/maven_web-1.0-SNAPSHOT]
[INFO] Processing war project
[INFO] Copying webapp resources [/Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/src/main/webapp]
[INFO] Webapp assembled in [49 msecs]
[INFO] Building war: /Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/target/maven_web-1.0-SNAPSHOT.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 1.528 s
[INFO] Finished at: 2020-05-16T00:05:14+08:00
[INFO] Final Memory: 12M/294M
[INFO] ————————————————————————

~ find . -name ‘*war’
./target/maven_web-1.0-SNAPSHOT.war
#此包可以直接放在tomcat的webapp下执行
注意:为什么maven_hello是war包而不是jar包呢?

1
2
3
cat pom.xml| grep -ri ‘packaging’
(standard input): war
#导包方式:jar默认,手动指定war
3.1.5 install
install是maven工程的安装命令,执行install将maven打成jar包或war包发布到本地仓库。

mvn install=>编译main代码=>编译test代码=>执行测试=>将项目打成war包=>安装到本地库

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
~ mvn install

[INFO] Scanning for projects…
[INFO]
[INFO] ————————————————————————
[INFO] Building maven_web 1.0-SNAPSHOT
[INFO] ————————————————————————
[INFO]
[INFO] — maven-resources-plugin:2.6:resources (default-resources) @ maven_web —
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] — maven-compiler-plugin:3.1:compile (default-compile) @ maven_web —
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] — maven-resources-plugin:2.6:testResources (default-testResources) @ maven_web —
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] — maven-compiler-plugin:3.1:testCompile (default-testCompile) @ maven_web —
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] — maven-surefire-plugin:2.12.4:test (default-test) @ maven_web —
[INFO] Surefire report directory: /Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/target/surefire-reports


T E S T S

Running cn.itcast.test.HelloTest
仅在测试期有效
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.053 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] — maven-war-plugin:2.2:war (default-war) @ maven_web —
[INFO] Packaging webapp
[INFO] Assembling webapp [maven_web] in [/Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/target/maven_web-1.0-SNAPSHOT]
[INFO] Processing war project
[INFO] Copying webapp resources [/Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/src/main/webapp]
[INFO] Webapp assembled in [32 msecs]
[INFO] Building war: /Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/target/maven_web-1.0-SNAPSHOT.war
[INFO] WEB-INF/web.xml already added, skipping
[INFO]

将自己的代码,编译到本地仓库,之后可以上传到远程仓库,服务器自动下载部署

[INFO] — maven-install-plugin:2.4:install (default-install) @ maven_web —
[INFO] Installing /Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/target/maven_web-1.0-SNAPSHOT.war to /Users/swzxsyh/Program/repository/cn/itcast/maven_web/1.0-SNAPSHOT/maven_web-1.0-SNAPSHOT.war
[INFO] Installing /Users/swzxsyh/Downloads/Day54-maven基础&环境搭建/代码/maven_web/pom.xml to /Users/swzxsyh/Program/repository/cn/itcast/maven_web/1.0-SNAPSHOT/maven_web-1.0-SNAPSHOT.pom
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time: 1.522 s
[INFO] Finished at: 2020-05-16T00:08:09+08:00
[INFO] Final Memory: 12M/297M
[INFO] ————————————————————————
从运行结果中,可以看出: 当后面的命令执行时,前面的操作过程也都会自动执行

3.1.6 deploy
maven工程部署命令,将jar或war包部署(上传)到私服中。

3.2 Maven生命周期
maven对项目构建过程分为三套相互独立的生命周期,请注意这里说的是“三套”,而且“相互独立”。

在同一个生命周期中的命令,执行后面的命令,前面的命令自动执行

三套名称 描述 命令
Clean Lifecycle 在进行真正的构建之前进行一些清理工作。 clean
Default Lifecycle 构建的核心部分,编译,测试,打包,部署等等。 compile,test,package,install,deploy
Site Lifecycle 生成项目报告,站点,发布站点。 site
3.2.1 clean生命周期 clean生命周期的目的是清理项目
阶段 描述
pre-clean 执行一些需要在clean之前完成的工作
clean 移除所有上一次构建生成的文件
post-clean 执行一些需要在clean之后立刻完成的工作
3.2.2 default生命周期
default生命周期定义了真正构件时所需要执行的所有步骤,它是生命周期中最核心的部分

生命周期阶段 描述
validate(校验) 校验项目是否正确并且所有必要的信息可以完成项目的构建过程。
initialize(初始化) 初始化构建状态,比如设置属性值。
generate-sources(生成源代码) 生成包含在编译阶段中的任何源代码。
process-sources(处理源代码) 处理源代码,比如说,过滤任意值。
generate-resources(生成资源文件) 生成将会包含在项目包中的资源文件。
process-resources (处理资源文件) 复制和处理资源到目标目录,为打包阶段最好准备。
compile(编译) 编译项目的源代码。
process-classes(处理类文件) 处理编译生成的文件,比如说对Java class文件做字节码改善优化。
generate-test-sources(生成测试源代 码) 生成包含在编译阶段中的任何测试源代码。
process-test-sources(处理测试源代 码) 处理测试源代码,比如说,过滤任意值。
generate-test-resources(生成测试资源 文件) 为测试创建资源文件。
process-test-resources(处理测试资源 文件) 复制和处理测试资源到目标目录。
test-compile(编译测试源码) 编译测试源代码到测试目标目录.
process-test-classes(处理测试类文件) 处理测试源码编译生成的文件。
test(测试) 使用合适的单元测试框架运行测试(Juint是其中之一)。
prepare-package(准备打包) 在实际打包之前,执行任何的必要的操作为打包做准备。
package(打包) 将编译后的代码打包成可分发格式的文件,比如JAR、WAR或者 EAR文件。
pre-integration-test(集成测试前) 在执行集成测试前进行必要的动作。比如说,搭建需要的环境。
integration-test(集成测试) 处理和部署项目到可以运行集成测试环境中。
post-integration-test(集成测试后) 在执行集成测试完成后进行必要的动作。比如说,清理集成测试 环境。
verify (验证) 运行任意的检查来验证项目包有效且达到质量标准。
install(安装) 安装项目包到本地仓库,这样项目包可以用作其他本地项目的依赖。
deploy(部署) 将最终的项目包复制到远程仓库中与其他开发者和项目共享。
3.2.3 site生命周期
site生命周期的目的是建立和发布项目站点,Maven能够基于POM所包含的信息,自动生成一个友好的站点,方便团 队交流和发布项目信息。

阶段 描述
pre-site 执行一些需要在生成站点文档之前完成的工作
site 生成项目的站点文档
post-site 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备
site-deploy 将生成的站点文档部署到特定的服务器上
3.3 Maven概念模型
Maven 包含了一个项目对象模型 (POM:Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插 件(plugin)目标(goal)的逻辑。

项目对象模型 (Project Object Model)

一个maven工程都有一个pom.xml文件,通过pom.xml文件定义项目的坐标、项目依赖、项目信息、插件目标等。

依赖管理系统(Dependency Management System)

通过maven的依赖管理对项目所依赖的jar 包进行统一管理。比如:项目依赖junit4.12,通过在pom.xml中定义 junit4.12的依赖即使用junit4.12,如下所示是junit4.12的依赖定义:

1
2
3
4
5
6
7
8

junit junit 4.12
    <scope>test</scope>
</dependency>
一个项目生命周期(Project Lifecycle)

使用maven完成项目的构建,项目构建包括:清理、编译、测试、部署等过程,maven将这些过程规范为一个生命

周期

清理 ➡ 编译 ➡ 测试 ➡ 报告 ➡ 打包 ➡ 部署
一组标准集合

maven将整个项目管理过程定义一组标准。

比如:通过maven构建工程有标准的目录结构,有标准的生命周期阶 段、依赖管理有标准的坐标定义等。

插件(plugin)目标(goal)

maven 管理项目生命周期过程都是基于插件完成的。

四.IDEA创建Maven工程
4.1 IDEA配置本地Maven
进入configure–>settings–>build–>build tools–>Maven中,设置maven工具和本地仓库

进入configure–>settings–>build–>build tools–>Maven–>Runner中,设置VM Options:-DarchetypeCatalog=internal -Dfile.encoding=GB2312

4.2 IDEA创建工程
Java工程

选择Maven,下一步,设置GroupID域名倒写,ArtifactID项目名,Version版本号

手动创建test测试配置文件目录

test目录右击创建resources目录,然后Mark Directory AS选Resources Root

指定maven环境的jdk版本和字符集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

org.apache.maven.plugins maven-compiler-plugin 3.1 1.8 1.8 UTF-8 maven工程命令操作

打开右侧maven栏,双击命令即可

如何导入依赖

1
2
3
4
5
6
7
8
9
10
11
12

mysql mysql-connector-java 5.1.47 runtime 安装一个插件JBLJavaToWeb

点击项目,选择插件功能JBLJavaToWeb即可将Java项目转换为Web项目

4.3 发布web工程
idea使用外置tomcat运行

Add Configurations–>ADD=>Tomcat(Local),设置名称Tomcat,其他跟之前项目一样

idea使用maven内置tomcat插件

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




org.apache.maven.plugins
maven-compiler-plugin
3.1

1.8
1.8
UTF-8



org.apache.tomcat.maven
tomcat7-maven-plugin
2.2

8080
/
utf-8




4.4 依赖管理
我们不考虑依赖范围引起的问题

依赖范围 对于编译classpath有效 对于测试classpath有效 对于运行时classpath有效 例子
compile(默认) Y Y Y mybatis
test - Y - junit
provided Y Y - servlet-api
runtime - Y Y JDBC驱动
system Y Y - 本地,maven仓库之外的库
依赖范围 描述
compile 默认依赖范围,作用域在编译、测试、运行时都有效。
test 作用域在测试时有效。编译和运行时不需要,比如:Junit。
provided 作用域在编译、测试时有效。运行时不需要,比如: servlet api 被 tomcat 容器提供。
runtime 作用域在测试、运行时有效。编译时不需要,比如:jdbc的驱动包。
system system范围依赖与provided类似,jar包在本地磁盘而不是maven仓库
在maven_web工程中测试scope:

默认引入 的jar包

compile 【默认范围 可以不写】(编译、测试、运行 都有效 )

servlet-api 、jsp-api

provided (编译、测试 有效, 运行时无效 防止和tomcat下jar冲突)

jdbc驱动jar包

runtime (测试、运行 有效 )

junit

test (测试有效)

依赖范围由强到弱的顺序是:compile>provided>runtime>test

总结

介绍

项目管理工具

  • 依赖管理

​ - 通过maven管理jar包的整个过程

  • 一键构建

​ - 通过一个maven的一个命令就可以完成工程整个构建的过程

安装和使用

下载和安装

  • 下载 3.5.2 版本

  • 安装解压到非中文路径

  • 配置MAVEN_HOME环境变量

​ - 依赖java环境

仓库分类

本地仓库

远程仓库

  • 1.中央仓库

  • 2.第三方远程仓库

​ - 阿里云镜像

  • 3.私服

配置指定本地仓库

  • 帅哥提供5G

​ - 不建议覆盖

配置阿里云镜像

  • 提高下载速度

命令和插件

1)clean

  • 清理target目录

2)compile

  • 编译 src/main/java

3)test

  • 测试 src/test/java

4)package

  • 将工程进行打包

​ - jar

​ - war

5)install

  • 将打包后的工程安装到本地仓库

6)deploy

  • 将本地仓库的jar包或war包上传到私服

生命周期

清理生命周期

  • clean

默认生命周期

  • compile

​ - test

​ - package

​ - install

​ - deploy

站点生命周期

  • site

IDEA配置maven工具

全局settings配置

  • 指定maven工具路径

  • 指定maven工具配置文件路径

  • 指定maven工具本地仓库的路径

指定本地创建maven时,使用本地骨架

  • -DarchetypeCatalog=internal -Dfile.encoding=GB2312

IDEA创建maven工程

1)创建java工程

2)创建web工程

  • 下载JBLJavaToWeb插件

IDEA发布web工程

1)使用本地tomcat

2)使用tomcat插件

  • pom.xml

依赖范围

1)我们编写的web工程需要手动导入servlet-api 坐标

2)在打成 war包的时候将servlet-api 设置到 /WEB-INF/lib目录下

3)如果与tomcat软件内置的版本不一致,可能会启动报错

4)需要指定jar包的作用范围(provided)

有哪些依赖范围呢?

  • compile

​ - 默认,在编译、测试、运行期有效

  • test

​ - 仅在测试期有效

  • provided

​ - 在编译、测试期有效

  • runtime

​ - 在测试、运行期有效

  • system

​ - 从本地中导入jar包

一.JDBC基础
1.1 概述
Java 数据库连接(Java DataBase Connectivity)

作用:通过Java语言操作数据库

本质:是官方(sun公司)定义的一套操作所有关系型数据库的规则(接口)。各个数据库厂商去实现这套接口,提 供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,运行时的代码其实是驱动jar包中的实现类。

1.2 快速入门
需求

通过java代码向数据库user表插入一条记录

准备数据库和表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CREATE DATABASE Day48;
USE Day48;
CREATE TABLE USER
(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
password VARCHAR(50)
);
INSERT INTO USER (username, password)
VALUES (‘admin’, ‘123’),
(‘tom’, ‘123’),
(‘jack’, ‘123’);

SELECT * FROM USER;
创建java工程,导入MySQL驱动jar包

mysql-connector-java-5.1.45-bin.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
public class JDBCDEMO {
public static void main(String[] args) throws Exception {
//1.注册驱动
// DriverManager.registerDriver(new Driver());
Class.forName(“com.mysql.jdbc.Driver”);
//2.建立连接
Connection connection = DriverManager.getConnection(“jdbc:mysql://localhost:3306/Day48”, “root”, “root”);
//3.编写sql
String sql = “insert into user values(4,’lucy’,666)”;
//4.获取sql执行对象
Statement statement = connection.createStatement();
//5.执行sql并返回结果
int i = statement.executeUpdate(sql);
//6.处理结果
if (i > 0) {
System.out.println(“Success”);
} else {
System.out.println(“Failed”);
}
//7.释放资源
statement.close();
connection.close();
}
}
1.3 API介绍
sun公司提供的:java.sql包下

DriverManager:驱动管理对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1.注册驱动
1)static void registerDriver(Driver driver)
我们通过翻看MySQL Driver实现类的源码发现内部的静态代码已经提供了注册驱动功能
static {
try{
DriverManager.registerDriver(new Driver());
}catch(SQLException var1){
throw new RuntimeException(“Can’t register driver!”);
}
}
2)反射
Class.forName(“com.mysql.jdbc.Driver”);
3)SPI 服务提供接口 【Service Provider Interface】
2.建立连接
static Connection getConnection(String url,String user,String password)
参数说明:
url:连接指定数据库地址【固定格式】
格式:jdbc:mysql://ip地址+端口/数据库名
实例:
jdbc:mysql://localhost:3306/day23
jdbc:mysql:///day23
user:用户名
password:密码
Connection:数据库连接对象

1
2
3
4
5
6
7
8
9
10
11
12
13

  1. 获取sql执行对象【小货车】
    Statement createStatement()
    PreparedStatement prepareStatement(String sql)
  2. 事务管理
    1)关闭自动提交(开启事务)
    void setAutoCommit(boolean autoCommit)
    参数:
    true:自动提交【默认值】
    false:手动提交
    2)提交事务
    void commit()
    3)回滚事务
    void rollback()

Statement:执行sql的对象

1
2
3
4
5
6
7
8
9
10

  1. 执行所有类型sql语句【了解】
    boolean execute(String sql)
  2. 仅执行DML类型sql语句
    int executeUpdate(String sql)
    参数:dml类型sql(insert、update、delete)
    返回值:影响行数
  3. 仅执行DQL类型sql语句
    ResultSet executeQuery(String sql)
    参数:dql类型sql(select)
    返回值:结果集
    ResultSet:结果集对象,封装查询结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

  1. 指针下移
    boolean next()
    返回值:
    true:表示此行有数据
    false:表示此行没有数据
    while(resultSet.next){
    //获取一行数据
    }

  2. 获取数据
    根据制定列编号和数据类型获取
    T getXxx(int 列编号)
    根据指定列名和数据类型获取
    T getXxx(String 列名)

    补充:获取所有类型
    Object getObject(String 列名)
    String getString(String 列名)

1.4 CRUD操作
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
// 1.注册驱动
// 2.建立连接
// 3.编写sql
// 4.获取sql执行对象 // 5.执行sql并返回结果 // 6.处理结果
// 7.释放资源

public class CRUDDEMO {
//新增
@Test
public void testInsert() throws Exception {
Class.forName(“com.mysql.jdbc.Driver”);
Connection connection = DriverManager.getConnection(“jdbc:mysql://localhost:3306/Day48”, “root”, “root”);
String sql = “insert into user values(NULL,’testInsert’,666)”;
Statement statement = connection.createStatement();
int i = statement.executeUpdate(sql);
if (i > 0) {
System.out.println(“Successful”);
} else {
System.out.println(“Failed”);
}
statement.close();
connection.close();
}

//修改
@Test
public void testUpdate() throws Exception {
    Class.forName("com.mysql.jdbc.Driver");
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/Day48", "root", "root");
    String sql = "UPDATE user SET username='testUpdate' WHERE id = 4";
    Statement statement = connection.createStatement();
    int i = statement.executeUpdate(sql);
    if (i > 0) {
        System.out.println("Successful");
    } else {
        System.out.println("Failed");
    }
    statement.close();
    connection.close();
}

//删除
@Test
public void testAlter() throws Exception {
    Class.forName("com.mysql.jdbc.Driver");
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/Day48", "root", "root");
    String sql = "delete from user WHERE id = 2";
    Statement statement = connection.createStatement();
    int i = statement.executeUpdate(sql);
    if (i > 0) {
        System.out.println("Successful");
    } else {
        System.out.println("Failed");
    }
    statement.close();
    connection.close();
}

//查询
@Test
public void testFindAll() throws Exception {
    Class.forName("com.mysql.jdbc.Driver");
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/Day48", "root", "root");
    String sql = "SELECT * FROM user";
    Statement statement = connection.createStatement();
    ResultSet resultSet = statement.executeQuery(sql);

    while (resultSet.next()) {
        int id = resultSet.getInt("id");
        String username = resultSet.getString("username");
        String password = resultSet.getString("password");
        System.out.println("\tID:" + id + "\tUSERNAME:" + username + "\tPASSWORD:" + password);
    }
    resultSet.close();
    statement.close();
    connection.close();
}

}
1.5 工具类
通过上面案例需求我们会发现每次去执行SQL语句都需要注册驱动,获取连接,得到Statement,以及释放资源。发

现很多重复的劳动,我们可以将重复的代码定义到一个工具类中。

目的:简化书写,一劳永逸

步骤分析

1
2
3
4
5
6
7
8
9
10
11
public class JdbcUtils{
// 1.注册驱动【保证一次】
static{ }
// 2.提供获取连接的静态方法
public static Connection getConnection(){
return null;
}
// 3.提供释放资源的方法
public void close(){
}
}
1.5.1 版本一
该版本具有耦合性
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
public class JdbcUtils1 {
// 1.注册驱动【保证一次】
static {
try {
Class.forName(“com.mysql.jdbc.Driver”);
} catch (ClassNotFoundException e) {
throw new RuntimeException(“Load JDBC Driver Failed”);
}
}

// 2.提供获取连接的静态方法
public static Connection getConnection() throws SQLException {
    return DriverManager.getConnection("jdbc:mysql://localhost:3306/Day48", "root", "root");
}

// 3.提供释放资源的方法
public void close(ResultSet resultSet, Statement statement, Connection connection) {
    if (resultSet != null) {
        try {
            resultSet.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (statement != null) {
        try {
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (connection != null) {
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

// 重载关闭方法
public static void close(Statement statement, Connection connection) {
    close(statement, connection);
}

}

1.5.2 版本二
解耦合版本

1
2
3
4
5
#K-V
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/Day48
jdbc.user=root
jdbc.password=root
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
public class JdbcUitls {

// 声明变量
private static String driver = null;
private static String url = null;
private static String user = null;
private static String password = null;

// 加载jdbc.properties配置文件,初始化变量
static {
    ResourceBundle jdbc = ResourceBundle.getBundle("jdbc");
    driver = jdbc.getString("jdbc.driver");
    url = jdbc.getString("jdbc.url");
    user = jdbc.getString("jdbc.user");
    password = jdbc.getString("jdbc.password");
}

// 1.注册驱动【保证一次】
static {
    try {
        Class.forName(driver);
    } catch (ClassNotFoundException e) {
        throw new RuntimeException("Load Driver Failed");
    }
}

// 2.提供获取连接的静态方法
public static Connection getConnection() throws Exception {
    return DriverManager.getConnection(url, user, password);
}

// 3.提供释放资源的方法
public static void close(ResultSet resultSet, Statement statement, Connection connection) {
    if (resultSet != null) {
        try {
            resultSet.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (statement != null) {
        try {
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (connection != null) {
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

// 重载关闭方法
public static void close(Statement statement, Connection connection) {
    close(null, statement, connection);
}

}

1.6 事务操作
事务

如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败

MySQL事务操作

事务
开启事务 begin | start transaction;
提交事务 commit;
回顾事务 rollback;
Java操作(使用Connection对象)

事务
关闭自动提交(开启事务) void setAutoCommit(false);
提交事务 void commit();
回顾事务 void rollback();
需求:

通过Java代码实现转账案例

导入账户表

1
2
3
4
5
6
7
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(10),
money DOUBLE
);
INSERT INTO account(name,money) VALUES (‘UserA’,1000),(‘UserB’,1000);

编写转账代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class TXDemo{
@Test
public void testTX(){
try{
// 1.获取连接【JdbcUtils工具类】
// 2.开启事务
// 3.UserA扣钱
// 机器故障
// 4.UserB加钱
// 5.提交事务
}catch(Exception e){
// 6.回滚事务
}finally{
// 7.释放资源
}
}
}
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
public class TXDemo {
@Test
public void testTX() {
Connection connection = null;
Statement statement = null;
try {
// 1.获取连接【JdbcUtils工具类】
connection = JdbcUitls.getConnection();
// 2.开启事务
connection.setAutoCommit(false);
tatement = connection.createStatement();
// 3.UserA扣钱
// 机器故障
// 4.UserB加钱
// 5.提交事务
connection.commit();
} catch (Exception e) {
try {
// 6.回滚事务
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
} finally {
// 7.释放资源
JdbcUitls.close(statement, connection);
}
}
}
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
public class TXDemo {
@Test
public void testTX() {
Connection connection = null;
Statement statement = null;
try {
// 1.获取连接【JdbcUtils工具类】
connection = JdbcUitls.getConnection();
// 2.开启事务
connection.setAutoCommit(false);
statement = connection.createStatement();

        // 3.UserA扣钱
        String ASql = "update account set money = money-100 where id=2;";
        int AResult = statement.executeUpdate(ASql);
        if (AResult > 0) {
            System.out.println("User A Payment Successful");
        }
        // 机器故障
        int a = 1 / 0;
        String BSql = "update account set money = money +100 where id =1;";
        // 4.UserB收到钱
        int BResult = statement.executeUpdate(BSql);
        if (BResult > 0) {
            System.out.println("User B Get the Payment");
        }
        // 5.提交事务
        connection.commit();
    } catch (Exception e) {
        e.printStackTrace();
        try {
            // 6.回滚事务
            connection.rollback();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    } finally {
        // 7.释放资源
        JdbcUitls.close(statement, connection);
    }
}

}
二.案例:用户登陆
2.1 需求分析
浏览器登录

服务器LoginServlet
接收用户请求
用户名,密码

操作JDBC
根据用户名和密码查询数据库 ➡
⬅ Server

判断用户是否登录成功
⬇正确 ➡错误 转发登录页面提示
重定向至list.jsp
2.2 代码实现
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
package com.test.web;

import com.test.util.JdbcUitls;

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.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

@WebServlet(“/LoginServlet”)
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //统一编码
    req.setCharacterEncoding("utf-8");
    resp.setContentType("text/html;charset=utf-8");
    //接收请求
    String username = req.getParameter("username");
    String password = req.getParameter("password");

    try {
        // 2.操作JDBC
        // 2.1 获取连接
        Connection connection = JdbcUitls.getConnection();

        // 2.2 编写sql
        // String sql = "select * from user where username ='admin' and password='123'";
        String sql = "SELECT * FROM user WHERE username='" + username + "'AND password ='" + password + "'";
        System.out.println(sql);

        // 2.3 获取sql执行对象
        Statement statement = connection.createStatement();

        // 2.4 执行sql并返回结果
        ResultSet resultSet = statement.executeQuery(sql);

        // 3.判断是否登录成功
        if (resultSet.next()) {// 成功
            String loginUsername = resultSet.getString("username");
            req.getSession().setAttribute("loginUsername", loginUsername);
            resp.sendRedirect(req.getContextPath() + "/list.jsp");
        } else {// 失败
            req.setAttribute("error", "Username/Password Wrong");
            req.getRequestDispatcher("/login.jsp").forward(req, resp);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

}

}

总结

jdbc

概述

  • 通过java语言操作数据库

本质

  • 面向接口编程思想

  • sun公司通过操作关系型数据库的一套规范(接口),所有的数据库厂商都需要实现这套接口,对于开发者只需要学习这套接口的API就可以操作所有类型的关系型数据库,真正的执行者是实现类(jar包驱动)

快速入门

  • 1.注册驱动

  • 2.建立连接

  • 3.编写sql

  • 4.获取sql执行对象

  • 5.执行sql并返回结果

  • 6.处理结果

  • 7.释放资源

API详解

  • DriverManager

​ - 1.注册驱动

​ - Class.forName()

​ - 2.建立连接

  • Connection

​ - 1.获取sql执行对象

​ - Statement

​ - PreparedStatement

​ - 2.事务安全

  • Statement

​ - 1.仅执行DML类型sql语句

​ - int executeUpdate(String sql)

​ - 2.仅执行DQL类型sql语句

​ - ResultSet executeQuery(String sql)

  • ResultSet

​ - 1.指针下移

​ - boolean next()

​ - 2.获取数据

​ - T getXxx(String 列名)

crud练习

  • 新增

  • 修改

  • 删除

  • 查询所有

JdbcUtils

  • 版本一

  • 版本二

事务安全

  • 模拟转账

一.Linstener
1.1 概述
中的监听器(观察者模式)
Java程序中,也要需要被监视的对象,一旦被监视对象发生变化,采取相应对象

监听器三大对象
HttpServletRequest,HttpSession,ServletContext

场景
历史访问次数,统计在线人数,系统启动时初始化配置信息

1.2 快速入门
使用ServletContextListener箭头项目启动和销毁做一些事,如在项目启动时加载配置文件
步骤:

创建ServletContextListener普通类
监听ServletContext创建、销毁
配置web.xml/注解
补充:监听HttpServletRequestListener/HttpSessionListener

web.xml

1
2
3
<listener>
<listener-class>com.test.Demo01.MyListener</listener-class>
</listener>
1
2
3
4
5
6
7
8
9
10
11
12
13

public class MyListener implements ServletContextListener {
//监听ServletContext创建
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext创建");
}
//监听ServletContext销毁
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext销毁");
}
}

注解

1
2
3
4
5
6
7
8
9
10
11
12
13
@WebListener
public class MyListener implements ServletContextListener {
//监听ServletContext创建
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext创建");
}
//监听ServletContext销毁
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext销毁");
}
}

1.3 模拟Spring框架
ServletContext可以在项目启动时读取配置文件加载

1
2
3
4
5
<!--    全局配置参数-->
<context-param>
<param-name>configLocation</param-name>
<param-value>words.properties</param-value>
</context-param>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

@WebListener
public class MyListener implements ServletContextListener {
//监听ServletContext创建
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext创建");
//可以加载公司定义配置文件的名称
//servletContextEvent 上下文事件对象,获取ServletContext
ServletContext servletContext = servletContextEvent.getServletContext();
//通过加载定义公司定义的配置文件名称
String configLoaction = servletContext.getInitParameter("configLocation");
System.out.println("" + configLoaction);
}

//监听ServletContext销毁
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
System.out.println("ServletContext销毁");
}
}

1.4 案例统计在线人数
需求:
有用户使用网站: 在线人数+1,用户退出,在线人数-1

步骤分析
使用ServletContext域对象存储在线总人数
使用ServletContextListener在项目启动时,初始化总人数为0
使用HttpSessionListener监听器,用户访问,人数+1,用户退出,人数-1
使用LogoutServlet控制器对当前会话对Session手动销毁

代码实现

InitNumberListerner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@WebListener
public class InitNumberListerner implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
//获取上下文域对象
ServletContext servletContext = servletContextEvent.getServletContext();
//初始化在线人数
servletContext.setAttribute("number",0);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {

}
}

NumberChangeListener

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
@WebListener
public class NumberChangeListener implements HttpSessionListener {
//会话建立,在线人数+1
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
//获取session
HttpSession session = httpSessionEvent.getSession();
//获取上下文session域对象
ServletContext servletContext = session.getServletContext();
//取出在线人数
Integer number = (Integer) servletContext.getAttribute("number");
//+1
servletContext.setAttribute("number", number + 1);
}
//会话销毁,在线人数-1
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
//获取session
HttpSession session = httpSessionEvent.getSession();
//获取上下文session域对象
ServletContext servletContext = session.getServletContext();
//取出在线人数
Integer number = (Integer) servletContext.getAttribute("number");
//-1
servletContext.setAttribute("number", number - 1);
}
}

index.jsp

1
2
3
4
5
6
7
8
<body>

<h3>Learn Listener</h3>
<h5>Online Member:${applicationScope.number}</h5>

<a href="${pageContext.request.contextPath}/LogoutServlet">Logout</a>
$END$
</body>

LogoutServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
@WebServlet("/LogoutServlet")
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//销毁Session
req.getSession().invalidate();
resp.getWriter().write("Logout");
}
}

二.综合案例
2.1 环境搭建

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
public class User {
private String id;
private String name;
private String sex;
private Integer age;
private String address;
private String qq;
private String email;
}

@WebFilter("/*")
public class EncodeFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
if (request.getMethod().equalsIgnoreCase("post")) {
request.setCharacterEncoding("UTF-8");
}
filterChain.doFilter(request, response);
}

@Override
public void destroy() {

}
}


public class DataUtils {
private static String realpath = "/Users/swzxsyh/Program/userdata.txt";

//从文件中读取所有学员信息
public static List<User> readAll() {
//保存所有学生对象信息
List<User> list = new ArrayList<>();
try {
//得到文件真实路径
//创建字符输入流
Reader isr = new InputStreamReader(new FileInputStream(realpath), "UTF-8");
//创建字符缓冲流
BufferedReader br = new BufferedReader(isr); //装饰模式
//一次读一行
String row = null;
while ((row = br.readLine()) != null) {//row = "1,张三,男,20"
String[] arr = row.split(",");
User user = new User();
user.setId(arr[0]);
user.setName(arr[1]);
user.setSex(arr[2]);
user.setAge(Integer.parseInt(arr[3]));
user.setAddress(arr[4]);
user.setQq(arr[5]);
user.setEmail(arr[6]);
//将User对象添加到集合
list.add(user);
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}

return list;
}

//向文件中写入所有用户信息--覆盖写
public static void writeAll(List<User> list) {
try {
//创建字符输出流
Writer osw = new OutputStreamWriter(new FileOutputStream(realpath), "UTF-8");
//创建字符缓冲流
BufferedWriter out = new BufferedWriter(osw);
//循环向文件中写入文本
for (User user : list) {
out.write(user.getId() + "," + user.getName() + "," + user.getSex() + "," + user.getAge() + "," + user.getAddress() + "," + user.getQq() + "," + user.getEmail());
out.newLine();//创建新的一行
}
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
List<User> list = readAll();

}
}

2.2 用户查询功能

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
@WebServlet("/FindAllServlet")
public class FindAllServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
UserService userService = new UserService();
List<User> list = userService.findAll();
req.setAttribute("list", list);
req.getRequestDispatcher("/list.jsp").forward(req, resp);
}
}

public class UserService {
UserDao userDao = new UserDao();
public List<User> findAll() {

return userDao.findAll();
}


public class UserDao {
public List<User> findAll() {
List<User> list = DataUtils.readAll();
return list;
}

2.3 添加用户功能

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
public class AddServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
Map<String, String[]> parameterMap = req.getParameterMap();
User user = new User();
BeanUtils.populate(user,parameterMap);
UserService userService = new UserService();
userService.add(user);
resp.sendRedirect(req.getContextPath()+"/FindAllServlet");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}

public void add(User user) {
userDao.add(user);
}

public void add(User user) {
List<User> list = DataUtils.readAll();
list.add(user);
DataUtils.writeAll(list);
}

2.4 删除用户功能

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

@WebServlet("/DeleteServlet")
public class DeleteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String id = req.getParameter("id");
UserService userService = new UserService();
userService.delete(id);
resp.sendRedirect(req.getContextPath()+"/FindAllServlet");
}}

public void delete(String id){
userDao.delete(id);
}

public void delete(String id) {
List<User> list = DataUtils.readAll();
for (User user : list) {
if (user.getId().equalsIgnoreCase(id)) {
list.remove(user);
break;
}
}
DataUtils.writeAll(list);
}

2.5 修改用户功能
2.5.1 用户回显

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
@WebServlet("/FindByIdServlet")
public class FindByIdServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String id = req.getParameter("id");
UserService userService = new UserService();
User user = userService.fidById(id);
req.setAttribute("user", user);
req.getRequestDispatcher("/update.jsp").forward(req, resp);
}}
public User fidById(String id) {
return userDao.findById(id);
}

public User findById(String id) {
User returnUser = null;
List<User> list = DataUtils.readAll();
for (User user : list) {
if (user.getId().equalsIgnoreCase(id)) {
returnUser = user;
}
}
return returnUser;
}

2.5.2 修改用户

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
@WebServlet("/UpdateServlet")
public class UpdateServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
Map<String, String[]> parameterMap = req.getParameterMap();
User newUser = new User();
BeanUtils.populate(newUser, parameterMap);
UserService userService = new UserService();
userService.update(newUser);
resp.sendRedirect(req.getContextPath() + "/FindAllServlet");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}

}
}

public void update(User newUser) {
userDao.update(newUser);
}


public void update(User newUser) {
List<User> list = DataUtils.readAll();
for (User user : list) {
if (user.getId().equalsIgnoreCase(newUser.getId())) {
try {
BeanUtils.copyProperties(user, newUser);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
DataUtils.writeAll(list);
}

总结
listener
概述
监听web三大域对象:Request、Session、ServletContext(创建和销毁)
作用
历史访问次数
统计在线人数
系统启动时初始化配置信息
快速入门
定义一个类,实现ServletContextListener接口
重写接口中的方法
配置

web.xml

别人写好的监听器,只能通过配置文件进行配置
注解

案例:统计在线人数
1)初始化在线人数
2)创建会话时人数+1,关闭会话时人数-1
3)servlet实现用户退出

一.概述
Web中的过滤器:当用户访问服务器资源时,过滤器将请求拦截,完成一些通用操作

应用场景:登陆验证,统一编码处理,敏感字符过滤

二.快速入门
2.1 XML配置
编写Java类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class QuickFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//此方法拦截用户的请求
//servletRequest请求对象
//servletResponse响应对象
//FilterChain过滤器链
System.out.println("QuickFilter Filter");
//放行
filterChain.doFilter(servletRequest, servletResponse);

}

@Override
public void destroy() {

}}

配置XML

1
2
3
4
5
6
7
8
9
<!--    快速入门-->
<filter>
<filter-name>QuickFilter</filter-name>
<filter-class>com.test.Demo01.QuickFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>QuickFilter</filter-name>
<url-pattern>/Demo01.jsp</url-pattern>
</filter-mapping>

2.2 注解配置
注意:使用注解,需要把web.xml标签注释

编写java类,实现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
//@WebFilter(filterName = "QuickFilter",urlPatterns = "/quick.jsp")
//@WebFilter(urlPatterns = "/quick.jsp")
//@WebFilter(value = "/quick.jsp")
@WebFilter("/quick.jsp")
public class QuickFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//此方法拦截用户的请求
//servletRequest请求对象
//servletResponse响应对象
//FilterChain过滤器链
System.out.println("QuickFilter Filter");
//放行
filterChain.doFilter(servletRequest, servletResponse);

}

@Override
public void destroy() {

}}

三.工作原理
发送请求
doFilter{
对请求拦截
是否放行
对响应增强
}
quick.jsp调用Service方法
doFilter{
对响应增强
}
返回响应
四.使用细节
4.1 生命周期
方法 代码
初始化方法 public void init(FilterConfig filterConfig)
拦截方法 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
销毁方法 public void destroy()
时机 说明
创建 服务器启动项目加载,创建filter对象,执行init方法
运行(过滤拦截) 用户访问被拦截目标资源时,执行doFilter方法
销毁 服务器关闭或项目卸载时,销毁filter对象,执行destroy方法(只执行一次)
注意 过滤器一定是优先于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
// 设置初始化参数一般不会在注解中使用
//@WebFilter(value = "/show.jsp", initParams = {@WebInitParam(name = "encode", value = "utf-8")})
//@WebFilter("/show.jsp")
public class LifeCycleFilter implements Filter {
private String encode;

//filterConfig它是filter对配置对象
//作用:获取filter对初始化参数
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("LifeCycleFilter Run");
filterConfig.getInitParameter("encode");
}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("LifeCycleFilter Filter Request,Run Method");
System.out.println("Unicode"+encode);
filterChain.doFilter(servletRequest, servletResponse);
}

@Override
public void destroy() {
System.out.println("LifeCycleFilter Destroy");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
<filter>
<filter-name>LifeCycleFilter</filter-name>
<filter-class>com.test.Demo02.LifeCycleFilter</filter-class>
<init-param>
<param-name>encode</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>LifeCycleFilter</filter-name>
<url-pattern>/show.jsp</url-pattern>
</filter-mapping>

4.2 拦截路径
在开发时,可以指定过滤器对拦截路径来定义拦截目标资源的范围
匹配方式 说明
精准匹配 用户访问指定目标资源(/show.jsp)时,过滤器进行拦截
目录匹配 用户访问指定目录下(/user/)所有资源时,过滤器进行拦截
后缀匹配 用户访问指定后缀名(
.jsp)的资源时,过滤器进行拦截
匹配所有 用户访问该网站所有资源(/*)时,过滤器进行拦截

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//@WebFilter("/show.jsp") //精准匹配
//@WebFilter("/User/*") //目录匹配
//@WebFilter("*.html") //后缀匹配
@WebFilter("/*")
public class UrlPatternFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {

}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("UrlPatternFilter拦截了请求..."); // 放行
filterChain.doFilter(servletRequest, servletResponse);
}

@Override
public void destroy() {

}
}

4.3 拦截方式
开发时,可以指定过滤器拦截方式来处理不同的应用场景(如浏览器发送的,或内部转发的)
拦截方式 说明
request(默认拦截方式) 浏览器直接发送请求时,过滤器拦截
forward 资源A转发到资源B时,过滤器拦截
资源A:ForwardServlet
资源B:show.jsp
可以同时配置两者
XML版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ModeFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter");
filterChain.doFilter(servletRequest, servletResponse);
}

@Override
public void destroy() {

}
}
1
2
3
4
5
6
7
8
9
10
<filter>
<filter-name>ModeFilter</filter-name>
<filter-class>com.test.Demo04.ModeFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ModeFilter</filter-name>
<url-pattern>/show.jsp</url-pattern>
<dispatcher>FORWARD</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>

注解版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@WebFilter(value = "/show.jsp",dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.FORWARD})
public class ModeFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter");
filterChain.doFilter(servletRequest, servletResponse);
}

@Override
public void destroy() {

}
}

4.4 过滤器链
在一次请求中,若请求匹配到多个filter,通过请求将相当于把这些filter串起来,形成过滤器链

需求
用户访问目标资源 show.jsp时,经过 FilterA FilterB

过滤器链执行顺序 (先进后出)

1.用户发送请求
2.FilterA拦截,放行
3.FilterB拦截,放行
4.执行目标资源 show.jsp
5.FilterB增强响应
6.FilterA增强响应
7.封装响应消息格式,返回到浏览器

过滤器链中执行的先后问题

方式 顺序
配置文件 谁先声明,谁先执行

注解【不推荐】 根据过滤器类名进行排序,值小的先执行
FilterA FilterB 进行比较, FilterA先执行
4.5 注解和XML使用
如果是自己定义filter,无执行先后顺序,可以使用注解开发
如果是第三方jar提供的filter,要在web.xml进行配置
五.综合案例
5.1 用户评论留言
需求
用户访问某论坛网站,可以对文章比赛等内容进行留言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>BBS</title>
</head>

<body>

<h3>LPL View Board</h3>
<hr>
<form action="${pageContext.request.contextPath}/WordServlet" method="post">
<textarea name="content" id="" cols="30" rows="10"></textarea>
<input type="submit" value="text">


</form>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@WebServlet("/WordServlet")
public class WordServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//接收请求参数Content
String content = req.getParameter("content");
//结果响应到浏览器
resp.getWriter().write(content);
}
}

5.2 统一网站编码
需求
tomcat8.5版本中已经将get请求的中文乱码解决了,但是post请求还存在中文乱码 浏览器发出的任何请求,通过过滤器统一处理中文乱码

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 统一网站编码   -->
<filter>
<filter-name>EncodeFilter</filter-name>
<filter-class>com.test.Case01.EncodeFilter</filter-class>
<init-param>
<param-name>encode</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EncodeFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
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
public class EncodeFilter implements Filter {
private String encode;

@Override
public void init(FilterConfig filterConfig) throws ServletException {
encode = filterConfig.getInitParameter("encode");
}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//类型向下转换
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//判断用户是否为post请求,才设置编码
if (request.getMethod().equalsIgnoreCase("post")) {
request.setCharacterEncoding(encode);
}
response.setContentType("text/html;charset=" + encode);

//放行
filterChain.doFilter(servletRequest, servletResponse);
}

@Override
public void destroy() {

}
}

5.3 非法字符拦截

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
@WebFilter("/WordServlet")
public class WordsFilter implements Filter {
private List<String> wordList;

@Override
public void init(FilterConfig filterConfig) throws ServletException {
//加载配置文件,ResourceBundle专门读取src下的properties文件,不需要后缀名
ResourceBundle words = ResourceBundle.getBundle("words");
//读取keyword关键字内容
String keyword = words.getString("keyword");
//split切割,转为list集合
wordList = Arrays.asList(keyword.split(","));
System.out.println(wordList);
}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//向下转型
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//获取用户输入的值
String content = request.getParameter("content");
//拦截非法内容,提示
for (String word : wordList) {
if(content.contains(word)){
response.getWriter().write("Input Words has Problem");
return;
}
}
//放行
filterChain.doFilter(request, response);
}

@Override
public void destroy() {

}
}

5.4 非法字符过滤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MyRequest extends HttpServletRequestWrapper {
//非法词库
private List<String> wordList;

public MyRequest(HttpServletRequest request, List<String> wordList) {
super(request);
this.wordList = wordList;
}

//对谁增强就重写谁
@Override
public String getParameter(String name) {
//调用原有的功能,获取用户输入的值
String parameter = super.getParameter(name);
//对非法词库过滤
for (String word : wordList) {
if (parameter.contains(word)) {
//注意:替换完后进行覆盖
parameter = parameter.replaceAll(word, "**");
}
}
return parameter;
}
}
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
@WebFilter("/WordServlet")
public class WordsProFilter implements Filter {
private List<String> wordList;

@Override
public void init(FilterConfig filterConfig) throws ServletException {
//加载配置文件,ResourceBundle专门读取src下的properties文件,不需要后缀名
ResourceBundle words = ResourceBundle.getBundle("words");
//读取keyword关键字内容
String keyword = words.getString("keyword");
//split切割,转为list集合
wordList = Arrays.asList(keyword.split(","));
System.out.println(wordList);
}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//向下转型
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;

//对request对象进行包装(过滤)
MyRequest requestPro = new MyRequest(request, wordList);

//放行
filterChain.doFilter(requestPro, response);
System.out.println(wordList);
}

@Override
public void destroy() {

}
}

附录 Filter模版设置

1
2
3
4
5
6
7
8
9
#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME};#end #parse("File Header.java") @javax.servlet.annotation.WebFilter("/${Entity_Name}")
public class ${Class_Name} implements javax.servlet.Filter {
public void init(javax.servlet.FilterConfig config) throws javax.servlet.ServletException {
}
public void doFilter(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse, javax.servlet.FilterChain chain) throws javax.servlet.ServletException, java.io.IOException {
// 放行
chain.doFilter(servletRequest, servletResponse); }
public void destroy() {
} }

总结
概述
作用
拦截用户请求
应用场景
如:登录验证、统一编码处理、敏感字符过滤…
快速入门

  1. 定义一个类实现Filter接口
    javax.servlet 包
  2. 重写filter方法
    doFilter
  3. 配置
    web.xml
    注解
    工作原理
    用户发送请求
    执行Filter拦截请求

放行(执行放行后的资源)

执行Filter拦截响应

响应给浏览器结果
细节
生命周期
何时创建

在服务器启动时,创建fitler对象,执行init方法,只执行一次
何时销毁

服务器正常关闭时,销毁filter对象,执行destroy方法,只执行一次
创建优先级

ServletContext

Filter

Servlet
拦截路径
精准匹配

/show.jsp
目录匹配

/user/*
后缀匹配

*.html
拦截所有

/*
拦截方式
REQUEST

客户端直接访问资源时,执行Filter
FORWARD

服务器内部资源跳转时,执行Filter
过滤器链
拦截顺序

先进后出
执行先后

web.xml

谁先声明,谁先执行
注解

按照自定义过滤器类名的字符串比较规则,值小的先执行

Docker安装使用Oracle 11g

拉取镜像

1
2
3
4
5
6
7
8
9
10
11
# 拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g
# 可以改名
docker tag xxxxxxxxxx oracle:oracle_11g

# 启动镜像
docker run -d -p 1521:1521 --name oracle_11g oracle_11g:latest
# 查看容器是否运行
docker ps -a


配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 进入容器
docker exec -it oracle_11g bash

# 切换到root
su root
#密码:helowin

# 编辑profile文件
vi /etc/profile

# 最后行添加如下环境变量
export ORACLE_HOME=/home/oracle/app/oracle/product/11.2.0/dbhome_2
export ORACLE_SID=helowin
export PATH=$ORACLE_HOME/bin:$PATH

# 创建软链接
ln -s $ORACLE_HOME/bin/sqlplus /usr/bin

# 切换回oracle
su - oracle
阅读全文 »

一.JSP
1.1 概述
简单来说:在html标签中嵌套java代码
作用:简化书写,展示动态页面
1.2 快速入门
在jsp页面显示当前时间

1
2
3
4
5
6
7
8
9
<body>
<%
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
String currentTime = sdf.format(date);
%>
<h3><%out.write(currentTime);%></h3>

</body>

1.3 工作原理
JSP本质就是一个Servlet
index.jsp
转换⬇
index_jsp.java
servlet接口
编译⬇
index_jsp.class
执行 service方法
1.4 脚本和注释
1.4.1 脚本
JSP通过脚本方式来定义Java代码
格式 说明
<% 代码%> 脚本片段,生成在service方法中,每次请求的时候都会执行
<%! 代码%> 声明片段,在java代码中声明成员,放在jsp生成jsva文件中的成员位置
<%=代码%> 输出脚本片段,相当于out.print(“Code”);方法,输出到jsp页面
格式 说明
out.print(); 方法支持一切类型输出
out.write(); 仅支持字符类型输出,如果传递的是整型,将根据ASCII码表转换输出
1.4.2 注释
格式 说明
<!– 注释静态资源 –> html注释
<%– 注释所有 –%> JSP注释
// 单行注释
/* 多行注释 /
/*文档注释 */ Java注释(JSP脚本内使用)
1.5 指令
作用
用于配置JSP页面,导入资源文件

格式
<%@ 指令名称 属性名1=”属性值1” 属性名2=”属性值2” %>

三大指令

指令 作用
page 配置JSP页面
include 页面包含(静态)
taglib 导入资源文件
1.5.1 page指令
指令 作用
contentType 相当于response.setContentType();设置响应体的MIME类型和编码方式
language 指定语言,目前仅支持Java
import 导入jar包
errorpage 当页面报错后,自动跳转到指定错误提示页面
isErrorpage 默认false关闭,设置true

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
<%@ page import="java.util.Date" %>
<%@ page import="java.text.SimpleDateFormat" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="500.jsp"%>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
new Date();
new SimpleDateFormat();
Integer age=1/0;
%>
</body>
</html>

<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>Server Busy</h3>
<%
//打印错误提示
out.print(exception.getMessage());
%>
</body>

1.5.2 include指令(静态包含)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- top.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Top</title>
</head>
<body>
<div style="border: skyblue dashed 5px;height: 150px">Top</div>
</body>
</html>


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Demo03</title>
</head>
<body>
<%@include file="top.jsp"%>
<div style="border: 5px solid red;height:200px ">
Demo03
</div>
</body>
</html>

1.5.3 taglib指令
导入jar包
通过taglib指令引入

1
2
3
4
5
6
7
8
9
10
11
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Demo04</title>
</head>
<body>
<h3>Taglib 指令</h3>
<c:if test=""></c:if>
</body>
</html>

1.6 内置对象
作用
JSP 页面中不需要get获取也不需要手动创建,就可以直接使用的对象
变量名 真实类型 作用
pageContexxt PageContext 当前页面中共享数据(域对象)
request HttpServletRequest 一切请求中共享数据
session HttpSession 一次会话中共享数据(域对象)
application ServletContext 整个web应用共享数据(域对象)
————- ——————- ——————-
response HttpServletResponse 响应对象
page(this) Object 当前页面Servlet对象
out JSPWriter 输出对象
config ServletConfig Servlet配置对象
exception Throwable 异常对象(默认关闭)
常用:
pageContext 1)当前页面的域对象
2)获取其他八个内置对象
request 1)接收用户请求(参数)
2)一次请求中域对象
response 1)设置响应:
  字节流
  字符流
out 1)专门在jsp中处理字符流
  print()//可以输出一切类型
  wirte(); //只能输出字符类型
1.7 JSP动作标签
作用
简化JSP页面编码
常用
指令 底层 作用
Jsp:include request.getRequestDispatcher().include(req,resp) 页面包含(动态)
Jsp:forward request.getRequestDispatcher().forward(req,resp) 请求转发(页面跳转)
Jsp:param include forward的子标签
使用request.getParameter()获取参数 参数传递
1.7.1 动态包含
静态包含

demo03.jsp 合并转换为一个java文件➡ demo3.java 编译➡ demo3.class 执行➡ response封装响应输出
top.jsp
动态包含

demo06.jsp 转换➡ demo06.java 编译➡ demo06.class 合并执行➡ response封装响应输出
top.jsp 转换➡ top.java 编译➡ top.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>

<head>
<title>Demo06</title>
</head>

<body>
<jsp:include page="top.jsp"></jsp:include>

<div style="border: gray 1px solid;height: 400px">JSP的动态包含:主体</div>

</body>
</html>

在企业开发时,推荐使用静态包含提示访问性能;注意:不能出现重名的变量

1.7.2 请求转发

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
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>A</title>
</head>
<body>
<%
System.out.println("A Run");
request.setAttribute("username","request Set");
%>
<jsp:forward page="b.jsp">
<jsp:param name="name" value="jack"></jsp:param>
<jsp:param name="age" value="18"></jsp:param>
</jsp:forward>
</body>
</html>


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>B</title>
</head>
<body>
<%
System.out.println("B Run");
//request域传递
System.out.println(request.getAttribute("username"));
//param标签传递
System.out.println(request.getParameter("name"));
System.out.println(request.getParameter("age"));
%>
</body>
</html>

二.MVC模式
2.1 JSP发展介绍
早期只有servlet,只能使用response输出html标签,非常麻烦。
后来有了JSP,简化了servlet开发;如果过度使用JSP,在JSP页面中写了大量的java代码和html标签,造成难于 维护,难于分工协作的场景。
再后来为了弥补过度使用jsp的问题,我们使用servlet+jsp这套组合拳,利于分工协作。
简单来说:就是一套总结出来的设计经验,适合在各种软件开发领域

目的:高内聚,低耦合
2.2 MVC介绍
MVC设计模式: Model-View-Controller简写。
MVC是软件工程中的一种软件架构模式,它是一种分离业务逻辑与显示界面的设计方法。
指令
M model(模型)

JavaBean(1.处理业务逻辑,2.封装实体)
V view(视图

JSP(展示数据)
C controller(控制器)

Servlet(1.接收请求,2.调用模型,3.转发视图)
优点 降低耦合性,方便维护和拓展,利于分工协作
缺点 使得项目架构变得复杂,对开发人员要求高
客户端
静态资源
动态资源
请求➡ 服务器
Controller
Servlet
1.接收请求
2.调用模型
3.转发视图 ➡
⬅ Model
JavaBean
处理业务逻辑
封装实体 ➡
⬅ SQL

⬅返回 View
jsp、html
展示动静内容

一.EL
1.1 概述
表达式语言(Expression Language)
作用:
主要用于代替jsp中脚本的功能,简化对java代码对操作
语法:
${表达式}
1.2 使用
1.2.1 获取值
EL表达式只能从域对象(4个域)中获取数据
标准写法
${pageScope.键名} 从page域中获取指定键名对应的值
${requestScope.键名} 从request域中获取指定键名对应的值
${sessionScope.键名} 从session域中获取指定键名对应的值
${applicationScope.键名} 从servletContext域中获取指定键名对应的值
简化写法
${键名} 特点:默认从最小域开始找,找到后直接显示,不在继续寻找
注意:要求四个域键名唯一

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
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Demo01</title>
</head>
<body>
<h3>EL 表达式基本语法</h3>
<%
/*模拟Servlet向域中存值*/
pageContext.setAttribute("Username", "Jack");
request.setAttribute("Age", "50");
session.setAttribute("Gender", "Male");
application.setAttribute("Address", "NY");
%>
<h5>标准语法</h5>
${pageScope.Username}<br><%--不会提示空指针异常--%>
${requestScope.Age}<br>
${sessionScope.Gender}<br>
${applicationScope.Address}<br>
<h5>简化语法</h5>
${Username}<br>
${Age}<br>
${Gender}<br>
${Address}<br>
</body>
</html>

练习
获取字符串 ${键名}
获取对象(User) ${键名.属性名}
获取List(Array)集合 ${键名[索引]}
获取Map集合 ${键名.key}
${键名[“key”]}
——–
注意 El不会出现null和数组角标越界问题

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
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Demo02</title>
</head>
<body>
<h3>获取User对象</h3>
<%
User user = new User("Jack", 13, "Male");
request.setAttribute("user",user);
%>
${user}<br><%--执行该对象对toString方法--%>
${user.username} |${user.age} |${user.gender}<br>

<h3>获取List集合</h3>
<%
List<Object> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.add(user);
request.setAttribute("list",list);

%>>
${list}<br><%--执行该对象对toString方法--%>
${list[0]} | ${list[1]} | ${list[2]} | ${list[3].username} |${list[3].age}<br>
${list[3].gender}<%--EL表达式不会出现集合(数组)角标越界异常--%>

<h3>获取map集合</h3>
<%
Map<String, Object> map = new HashMap<>();
map.put("key1","A");
map.put("key2","B");
map.put("key3","C");
map.put("key.4",user);
request.setAttribute("map",map);
%>
${map}<br><%--执行该对象对toString方法--%>
${map.get("key1")} | ${map.key2} | ${map.key3}<br>
${map['key.4']}<br>
${map['key.4'].username} | ${map['key.4'].age} | ${map['key.4'].gender}
</body>
</html>

1.2.2 执行运算
运算符 语法
算数运算符 + - * /(div) %(mod)
比较运算符 > < >= <= ==(eq) !=(ne)
逻辑运算符 &&(and)
三元运算符 ${条件表达式?为真:为假}
——– ————————–
空运算符 ${not empty 对象}
功能:
可以判断字符串和对象是否为空
可以判断一个集合的长度是否为0

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
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Demo03</title>
</head>
<body>
<%
int a = 4;
int b = 3;
request.setAttribute("a", a);
request.setAttribute("b", b);
%>
<h5>算数运算符</h5>
${a / b} | ${a div b} <br>
${a % b} | ${a mod b} <br>

<h5>比较运算符</h5>
${a == b} | ${a eq b}<br>
${a != b} | ${a ne b}<br>

<h5>三元运算符</h5>
${a==b?"Y":"N"}<br>

<h5>非空判断</h5>
<%
User user = new User();
request.setAttribute("user", user);

List list = new ArrayList();
list.add("aa");
request.setAttribute("list", list);
%>
${not empty user}<%--if(user != null){}--%>
${not empty list}<%--if(list != null && list.size()>0)--%><br>

<h5>空值判断</h5>
${empty user}<%--if(user == null){}--%>
${empty list}<%--if(list == null && list.size()==0)--%><br>
</body>
</html>

1.2.3 隐式对象
EL表达式中有11个隐藏对象
掌握
pageContext
cookie
对象 作用
pageContext 就是jsp九大内置对象之一,可以通过它获得其他八个内置对象
cookie 可以获取浏览器指定cookie名称的值

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
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>

<head>
<title>Demo04</title>
</head>

<body>

<h3>EL隐式对象</h3>

${pageContext.request.contextPath}<%--动态获取:项目名称(虚拟路径)--%><br>
${cookie.JSESSIONID.value}<%--获取指定cookie名称的值--%>
</body>
</html>
1.2.4 补充
jsp默认支持el表达式
servlet2.3规范中,默认不支持el表达式
忽略el表达式 方法
忽略当前jsp页面中所有的el表达式 isELIgnored=”true” 属性
忽略单个el表达式 ${表达式}

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Demo04</title>
</head>
<body>
<h3>EL隐式对象</h3>
${pageContext.request.contextPath}<%--动态获取:项目名称(虚拟路径)--%><br>
${cookie.JSESSIONID.value}<br><%--获取指定cookie名称的值--%>
\${cookie.JSESSIONID.value}<%--获取指定cookie名称的值--%>
</body>
</html>

1.3 JavaBean
实际上是一个普通Java类
使用规范
所有字段(成员变量)为private
提供无参构造方法
提供getter,setter和is方法
实现Serializable接口
例如:如下User类有4个字段(成员变量),有全参和无参构造方法,有一个属性(username)

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
public class User implements Serializable {
private String Username;
private Integer Age;
private String Gender;

private boolean success;//是否操作成功

public boolean isSuccess() {
return success;
}

public void setSuccess(boolean success) {
this.success = success;
}

public User() {
}

public String getUsername() {
return Username;
}

public void setUsername(String username) {
Username = username;
}

public Integer getAge() {
return Age;
}

public void setAge(Integer age) {
Age = age;
}

public String getGender() {
return Gender;
}

public void setGender(String gender) {
Gender = gender;
}
}

二.JSTL
2.1 概述
Jsp 标准标签库(Jsp Standard Tag Library),是由Apache组织提供的开源的jsp标签库
作用:替换和简化jsp页面中java代码的编写
JSTL标准标签库有5个子库,但随着发展,目前常使用的是它的核心库(Core)
标签库 标签库的URI 前缀
Core http://java.sun.com/jsp/jstl/core c
国际化(几乎不用) http://java.sun.com/jsp/jstl/fmt fmt
SQL(过时) http://java.sun.com/jsp/jstl/sql sql
XML(过时) http://java.sun.com/jsp/jstl/xml x
Functions(几乎不用) http://java.sun.com/jsp/jstl/functions fn
2.2 Core标签使用
2.2.1 使用步骤
当前Web项目引入第三方jar包
当前JSP页面使用taglib指令引入core核心标签

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Demo01</title>
</head>
<body>
</body>
</html>

2.2.2 常用标签
标签 语法
c:if 相当于Java中的if(){} <c:if test=”boolean值>
true:显示标签体内容
false:隐藏标签体内容
注意:此标签没有else功能,需要使用就取反
c:forEach 1)普通for循环for(int i=1;i<=5;i++){i}
<c:forEach>
begin=”1”起始值(包含)
end=”5”结束值(包含)
step=”1”步长1
var=”i” 当前输出临时变量

  1. 增强for循环 for(User user:list){user} <c:forEach items=”${list}” var=”user” varStatus=”vs”>
    ${user}

    items=”list” 集合
    var=”user” 当前输出的临时变量
    varStatus=”vs” 变量状态
    index 当前索引 从0开始
    count 计数器 从1开始
    c:choose 相当于java中的switch语句 <c:choose>等价于switch
    <c:when>等价于case+break
    <c:otherwise>等价于default
    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
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <html>
    <head>
    <title>Demo01</title>
    </head>
    <body>
    <%
    User user = new User();
    user.setUsername("Jack");
    request.setAttribute("user",user);
    %>

    <c:if test="${empty user}">
    Welcome ,Please Login
    </c:if>
    <c:if test="${not empty user}">
    Welcome, ${user.username}
    </c:if>
    </body>
    </html>

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <html>
    <head>
    <title>Demo02</title>
    </head>
    <body>
    <h3>普通for循环</h3>
    <c:forEach begin="1" end="5" step="1" var="i"> <%--for循环将临时变量存储到pageContext域空间--%>
    ${i}<br>
    </c:forEach>

    <h3>增强for循环</h3>
    <%
    List list = new ArrayList();
    list.add(new User("A",1,"F"));
    list.add(new User("B",1,"F"));
    list.add(new User("C",1,"M"));
    list.add(new User("D",1,"M"));
    request.setAttribute("list",list);
    %>>
    ${requestScope.list}

    <c:forEach items="${list}" var="user" varStatus="vs">
    索引:${vs.index}<br>
    计数器:${vs.count}<br>
    ${user}<br>
    ${user.username}<br>
    </c:forEach>
    </body>
    </html>

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <html>
    <head>
    <title>Demo03</title>
    </head>
    <body>
    <h3>Choose标签</h3>
    <%
    Integer money=30000;
    request.setAttribute("money",money);
    %>
    <c:choose>
    <c:when test="${money==7000}">AAA</c:when>
    <c:when test="${money==8000}">BBB</c:when>
    <c:when test="${money==9000}">CCC</c:when>
    <c:when test="${money==30000}">DDD</c:when>
    <c:otherwise>
    ZZZZZZ
    </c:otherwise>
    </c:choose>
    </body>
    </html>
    三.三层架构
    3.1 概述
    通常意义上的三层架构就是将整个业务应用划分为:表示层、业务逻辑层、数据访问层
    区分层次的目的为了高聚合低耦合思想
    表示层:称为web层,与浏览器进行数据交互(控制器合视图)
    业务逻辑层:又称为service层,处理业务数据(if判断,for循环)
    数据访问(持久)层:称为Dao层,与数据库进行交互(每一条记录与JavaBean实体产生对应关系)

包目录结构
com.xxx 基本包(公司域名倒写)
com.xxx.dao 持久层
com.xxx.service 业务层
com.xxx.web 表示层
com.xxx.domain 实体(JavaBean)
com.xxx.util 工具
3.2 案例:用户信息列表显示
需求:MVC模式开发代码,完成用户显示列表功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

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
public class UserDao {
private static List<User> list = new ArrayList<>();

static {
list.add(new User("1","A","Male",18,"NY","123","sd@qq.com"));
list.add(new User("2","B","Female",18,"NY","234","ad@qq.com"));
list.add(new User("3","C","Male",18,"NY","345","sv@qq.com"));

}

public List<User> findAll() {
return list;
}}public class User {
private String SID;
private String Name;
private String Gender;
private Integer Age;
private String Address;
private String QQ;
private String EMAIL;

//此处省略无参、全参、toString,set/get
public class UserService {
public List<User> findAll() {
//调用DAO,查询
UserDao userDao = new UserDao();
List<User> list = userDao.findAll();
return list;
}
}
@WebServlet("/FindAllServlet")
public class FindAllServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//调用Service查询
UserService userService = new UserService();
List<User> list=userService.findAll();
//将list存储到request域
req.setAttribute("list",list);
//转发list.jsp
req.getRequestDispatcher("/list.jsp").forward(req,resp);
}
}
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
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>List</title>
</head>
<body>
<table border="1" cellpadding="0" cellspacing="0" width="500px" align="center">
<tr>
<th>编号</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>地址</th>
<th>QQ</th>
<th>邮箱</th>
</tr>
<c:forEach items="${list}" var="user">
<tr align="center">
<td>${user.SID}</td>
<td>${user.name}</td>
<td>${user.gender}</td>
<td>${user.age}</td>
<td>${user.address}</td>
<td>${user.QQ}</td>
<td>${user.EMAIL}</td>
</tr>
</c:forEach>
</table>
</body>
</html>

一.Session
1.1 概述
1.2 快速入门
HttpSession也是一个对象

说明 API
存储数据 void setAttribute(String name,Object value)
获取数据 Object getAttribute(String name)
删除数据 void removeAttribute(String name)
步骤分析

将数据存储到session中

说明 API
通过request对象,获取session对象 HttpSession session = req.getSession();
操作session的API,存储数据 session.setAttribute(“username”,”AAABBBCCCC”);
从session中获取数据
说明 API
通过request对象,获取session对象 HttpSession session = req.getSession();
操作session的API,获取数据 session.getAttribute(“username”);

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
@WebServlet("/SetSession")
public class SetSession extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过request对象,获取session对象
HttpSession session = req.getSession();
//操作session的API,存储数据
session.setAttribute("username","AAABBBCCCC");
}
}


@WebServlet("/GetSession")
public class GetSession extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过request对象,获取session对象
HttpSession session = req.getSession();
//操作session的API,获取数据
String username = (String) session.getAttribute("username");
System.out.println("GetSession:"+username);
}
}

1.3 工作原理
Session基于Cookie技术实现
HttpSession session = req.getSession();
如果用户是第一次访问:表示创建Session对象,生成编号
如果用户是第N次访问,根据浏览器携带编号找session对象

1.4 Session细节
1.4.1 客户端关闭,服务器不关闭,两次获取的Session数据是否相同
默认情况下,浏览器关闭再打开,两次获取的Session不同(基于cookie实现,浏览器关闭,cookie销毁)
设置cookie的存活时间(JESSIONID),代替服务器,覆盖JESSIONID,指定持久化时间

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
@WebServlet("/SessionDemo01")
public class SessionDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
//指定JESSIONID时间
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session对象
HttpSession session = req.getSession();
//向session中存数据
session.setAttribute("username","zZZ");
//获取SESSION 的唯一编号
String id = session.getId();
//设置cookie时长
Cookie cookie = new Cookie("JESSIONID", id);
//设置存活时间
cookie.setMaxAge(60*2);
//当前项目下共享
cookie.setPath(req.getContextPath());
//response响应cookie
resp.addCookie(cookie);
}
}

1.4.2 客户端不关闭,服务器关闭,两次获取的Session数据是否相同
当服务器正常关闭,重启后,两次获取的session数据一样
Tomcat实现了以下功能
钝化(序列化):当服务器正常关闭时,session中当数据,会序列化到磁盘
活化(反序列化):当服务器开启后,从磁盘中读取文件,反序列化到内存中
1.4.3 生命周期
何时创建

用户第一次调用req.getAttribute()时,创建
何时销毁

服务器非正常关闭
非活跃状态30分钟后销毁(tomcat/conf/web.xml官方已配置)
session.invalidate();立即销毁
作用范围

一次会话中,多次请求之间
注意:每一个浏览器和服务器之间都是独立的会话
1.4.4 URL重写
Session基于Cookie技术实现;浏览器Cookie可以禁用,一旦禁用,Session会出现问题
开发中,一般不考虑禁用Cookie用户

如果真要处理,可以使用URL重写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@WebServlet("/SessionDemo02")
public class SessionDemo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取session对象
HttpSession session = req.getSession();
//向session中存数据
session.setAttribute("username", "SIODJOISDJSO");

//定义URL
String url = "/Day37_war_exploded/GetSession";
//重写URL,拼接JESSIONID
url = resp.encodeURL(url);

resp.setContentType("text/html;charset=utf-8");
resp.getWriter().write("<a href'" + url + "'>Direct to get Session</a>");
}
}

1.5 Session特点
session存储数据在服务器
session存储类型任意(Object)
session存储大小和数据没有限制(相对于内存)
session存储数据相对安全
cookie和session的选择

cookie将数据保存在浏览器端,数据相对不安全,建议敏感数据不要放在cookie中,且数据大小有限制
成本低,对服务器要求不高
浏览器为了解决该不足,前端使用了localStroage

session将数据保存在服务器端,数据相对安全,数据大小比cookie中灵活
成本较高,对服务器压力大

二. 三大域对象总结
request,session,ServletContext

2.1 API
设置数据 void setAttribute(String name,Object o)
获取数据 Object getAttribute(String name)
删除数据 void removeAttribute(String name)
2.2 生命周期
2.2.1 ServleetContext域对象
何时创建 服务器正常启动,项目加载时创建
何时销毁 服务器关闭或项目卸载时,卸载
作用范围 整个web项目(共享数据)
2.2.2 HttpSession域对象
何时创建 用户第一次调用req.getSession() 方法时创建
用户访问携带的JESSIONID与服务器不匹配时创建
何时销毁 服务器非正常关闭时
未活跃时间30 min
session.invalidate();立即销毁
作用范围 一次会话中,多次请求间(共享数据)
2.2.3 HttpServletRequest域对象
何时创建 用户发送请求时创建
何时销毁 服务器做出响应后销毁
作用范围 一次请求中,多次转发间(共享数据)
2.3 小结
能用小的不用大的:request<session<servletContext

常用场景

request 一次查询的结果 (servlet转发jsp)
session 存放当前会话的私有数据
(验证码、购物车、等)
servletContext:若需要所有的Servlet都能访问到,才使用这个域对象

三.综合案例
3.1 商品购物车
3.1.1 需求分析
addCatrSetvlet

1.获取请求参数name(product)
2.返回:product 商品已加入购物车
3.从session中获取购物车
4.如果购物车为空,创建cart = new HashMap()
5.判断,此商品是否存在于购物车
6.不存在,添加商品数量1
7.存在,在原商品基础上+1
8.将购物车重写到session中

cart.jsp

1.从session中获取购物车对象
2.判断是否为空
3.如果空,显示购物车空
4.如果不为空,遍历展示商品和数量

3.1.2 代码实现
goods.jsp
addCartServlet
cart.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>

<head>
<title>Goods</title>
</head>

<body>

<h3>商品列表</h3>

<a href="/Day37_war_exploded/AddCartServlet?name=TV">TV,add to cart</a><br>
<a href="/Day37_war_exploded/AddCartServlet?name=PC">PC,add to cart</a><br>
<a href="/Day37_war_exploded/AddCartServlet?name=Phone">Phone,add to cart</a><br>
</body>
</html>
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
@WebServlet("/AddCartServlet")
public class AddCartServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置编码
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");

//获取请求参数
String product = req.getParameter("name");
resp.getWriter().write(product + ", has add to Cart");

//从session中获取购物车
Map<String, Integer> cart = (Map<String, Integer>) req.getSession().getAttribute("cart");

//判断购物车是否为空
if (cart == null) {
cart = new HashMap<>();
}

//判断购物车中是否包含本次商品
if (cart.containsKey(product)) {
//存在,product++
Integer oldCount = cart.get(product);//之前的数量
cart.put(product, ++oldCount);//数量加一
} else {
//不存在 ,加入购物车
cart.put(product, 1);
}

//重新将购物车写入到session中
req.getSession().setAttribute("cart", cart);

//继续浏览
resp.getWriter().write("<br><a href='/Day37_war_exploded/goods.jsp'>Continue View</a><br>");
//查看购物车
resp.getWriter().write("<br><a href='/Day37_war_exploded/cart.jsp'>Check Shopping Cart</a>");

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<table border="1" width="200px" align="center">
<tr>
<th>Product</th>
<th>Quantity</th>
</tr>
<%
//从session中获取购物车
Map<String, Integer> cart = (Map<String, Integer>) request.getSession().getAttribute("cart");
//判断是否为空
if (cart == null) {
out.write("No Product here<br>");
} else {
for (String product : cart.keySet()) {
out.write("<tr><td>" + product + "</td> <td>" + cart.get(product) + "</td><tr/>");
}
}
%>
<a href='/Day37_war_exploded/goods.jsp'>Continue View</a>
</table>

3.2 用户登陆(验证码)
3.2.1 需求分析
3.2.2 代码实现

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
@WebServlet("/LoginServlet")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
//课下作业实现非空判断
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
//获取用户输入验证码
String checkcode = req.getParameter("checkcode");
//获取session验证码
String codeSession = (String) req.getSession().getAttribute("code_session");
//校验匹配
if (!checkcode.equalsIgnoreCase(codeSession)) {
//验证码不匹配提示
req.setAttribute("Error", "Check Code Wrong");
//转发到login.jsp
req.getRequestDispatcher("/login.jsp").forward(req, resp);
//代码不再向下执行
return;
}
//获取用户输入到用户名和密码
String username = req.getParameter("username");
String password = req.getParameter("password");

//判断用户名密码
if (!("jack".equals(username) && "123".equals(password))) {
//用户名密码不匹配提示
req.setAttribute("Error", "Username or Password Wrong");
//转发到login.jsp
req.getRequestDispatcher("/login.jsp").forward(req, resp);
//代码不再向下执行
return;
}

//将用户名存入session
req.getSession().setAttribute("username", username);
//重定向到Succes.jsp
resp.sendRedirect(req.getContextPath() + "/success.jsp");
}
}
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
<form action="/war_exploded/LoginServlet" method="post">
Name: <input type="text" name="username"><br>
Password: <input type="password" name="password"><br>
Checkcode: <input type="text" name="checkcode"><img src="/Day37_war_exploded/CheckCodeServlet" id="image1"
alt=""><br>
<input type="submit" value="Login">
<span style="color: red">
<%
String error = (String) request.getAttribute("Error");
if (error != null) {
out.write(error);
}
%>

</span>
</form>
<script>
document.getElementById("image1").onclick = function () {
this.src = '/Day37_war_exploded/CheckCodeServlet?' + new Date().getTime();
}
</script>
</body>

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Success</title>
</head>
<body>
<%
//获取用户信息
String username = (String) request.getSession().getAttribute("username");
if (username != null){
out.write("Welcome ,"+username);
}
%>
</body>
</html>

总结
概述
在一次会话的多次请求之间共享数据,将数据保存到服务器端
实现原理
基于Cookie技术
jsessionid=xxxx
常用方法
获取session
request.getSession()
使用session
setAttribute()
getAttribute()
remomveAttribute()
生命周期
何时创建
用户第一次执行getSession()方法时,创建
用户携带的jssionid与服务器不匹配时,创建
何时销毁
服务器非正常关闭时
session.invalidate()
session默认销毁时间30分钟

tomcat目录/conf/web.xm
作用范围
一次会话中,多次请求之间
特点

  1. session在服务器端存储数据
  2. session存储数据格式可以是任意类型
  3. session存储数据没有大小限制(相对于内存)
  4. session存储数据相对安全
    三个域对象
    ServletContext
    创建
    销毁
    作用域
    HttpSession
    创建
    销毁
    作用域
    HttpServletRequest
    创建
    销毁
    作用域
    综合案例
    商品购物车
    用户登录(验证码)

一.会话概述
1.1 什么是会话
http协议是一个无状态协议,同一会话的多次请求相互独立,会话负责存储浏览器和服务器多次请求之间的数据

1.2 会话技术
客户端会话技术:Cookie
服务端会话技术:Session

二.Cookie
2.1 概述
作用:在一次会话的多个请求之间共享数据,将数据保存到客户端

2.2 快速入门
2.2.1 设置数据到Cookie中
创建cookie对象,设置数据
Cookie cookie = new Cookie(String name,String value);
通过response,响应(返回)cookie
response.addCookie(cookie);
2.2.2 从cookie中获取数据
通过request对象,接收cookie数组
Cookie[] cookies = request.getCookies()
遍历数组
for(Cookie cookie:cookies){
}

浏览器具有自动存储cookie的能力,也有请求携带cookie的能力

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
@WebServlet("/SetServlet")
public class SetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie = new Cookie("name","jack");
resp.addCookie(cookie);
}
}

@WebServlet("/GetServlet")
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过request对象,接收cookie数组
Cookie[] cookies = req.getCookies();
if (cookies != null) {
//遍历数组
for (Cookie cookie : cookies) {
String name = cookie.getName();
String value = cookie.getValue();
System.out.println(name+"="+value);
}
}
}
}

2.3 工作原理
基于HTTP协议:请求头cookie和响应头set-cookie

2.4 Cookie细节
2.4.1 服务器如何发送多个Cookie
创建多个cookie对象
Cookie cookie1 = new Cookie(“name”,”rose”);
Cookie cookie2 = new Cookie(“age”,”18”);

通过response响应多个cookie
response.addCookie(cookie1);
response.addCookie(cookie2);

1
2
3
4
5
6
7
8
9
10
11
12
13
@WebServlet("/MultipleCookie")
public class MultipleCookie extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Cookie cookie = new Cookie("name", "rose");
Cookie age = new Cookie("age", "18");
resp.addCookie(cookie);
resp.addCookie(age);
}

}
2.4.2 Cookie在浏览器的保存时间
默认情况下,浏览器关闭(会话结束),cookie销毁(浏览器内存中存储)
设置Cookie的存活时间
cookie.setMaxAge(int second);
正数:指定存活时间,持久化浏览器的磁盘中,到期后自动销毁
负数:默认浏览器关闭,cookie销毁
零:立即销毁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@WebServlet("/MaxAgeCookie")
public class MaxAgeCookie extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建Cookie对象
Cookie cookie = new Cookie("product", "MI");
//设置Cookie的存活时间,默认值-1,关闭自动销毁
// cookie.setMaxAge(-1);
//设定30s,自动销毁
// cookie.setMaxAge(30);
//立即销毁
cookie.setMaxAge(0);
//resp响应Cookie
resp.addCookie(cookie);
}
}

2.4.3 Cookie是否支持中文存储
Tomcat 8版本开始支持中文

但是不支持部分特殊字符,如:分号,空格,逗号等,会触发Rfc6265 CookieProcessor规范错误

建议使用通用方法存储字符
URLEncoder 编码
URLDecoder 解码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

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
@WebServlet("/EncodeCookie")
public class EncodeCookie extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String product = ("华为 30X,");
product= URLEncoder.encode("UTF-8");
Cookie cookie = new Cookie("Brand", "苹果");
Cookie cookie1 = new Cookie("product", product);
resp.addCookie(cookie);
}}

@WebServlet("/GetServlet")
public class GetServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//通过request对象,接收cookie数组
Cookie[] cookies = req.getCookies();
if (cookies != null) {
//遍历数组
for (Cookie cookie : cookies) {
String name = cookie.getName();
String value = cookie.getValue();
//解码
value = URLDecoder.decode("UTF-8");
System.out.println(name + "=" + value);
}
}
}}

2.4.4 Cookie共享数据的范围
2.4.4.1 同一个Tomcat服务器中,部署多个web项目,能否共享Cookie
默认情况下不可以
默认Cookie的携带路径,是当前设置cookie的servlet父路径

设置Cookie:http://localhost:8080/Day36_war_exploded/EncodeCookie
默认携带路径:http://localhost:8080/Day36_war_exploded/

指定Cookie携带路径
cookie.setPath(String Path);
例:cookie.setPath(“/“);

/ 相当于http://localhost:8080/

此cookie携带路径

http://localhost:8080/

访问:

http://localhost:8080/Day36_war_exploded/
http://localhost:8080/Day35_war_exploded/

携带路径不同,可以存储同名的cookie

在当前项目下共享cookie方法
cookie.setPath(“/项目名”);

2.4.4.2 不同Tomcat服务器之间的Cookie能否共享
默认情况下不可以
多个服务器之间的数据共享cookie,需要在同一个一级域名下
cookie.setDomain(“.xx.com”)
2.5 Cookie特点
Cookie存储的数据都在客户端
Cookie存储的数据只能是字符串
Cookie单个大学不能超过4kb
同一个域名下Cookie的数量不能超过50个
Cookie路径不同,可以重名出现
Cookie存储数据不太安全
三.综合案例
3.1 用户上次访问记录
需求:访问一个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
37
38
39
40
41
42
43
44
45
46
47

public class CookieUtils {
//根据指定名称,查找cookie对象
public static Cookie findByName(String name, Cookie[] cookies) {
//非空判断
if (cookies != null && cookies.length > 0) {
//遍历
for (Cookie cookie : cookies) {
//判断是否有指定名称的cookie
if (name.equals(cookie.getName())) {
return cookie;
}
}
}
return null;
}
}
@WebServlet("/LastTimeServlet")
public class LastTimeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html;charset=UTF-8");
//接收cookie数组,取出指定名称cookie对象
Cookie cookie = CookieUtils.findByName("last_time", req.getCookies());
//判断
if (cookie == null) {
//不存在
resp.getWriter().write("Welcome");
} else {
//存在
String value = cookie.getValue();
resp.getWriter().write("Welcome Back,Last Visit Time:" + value);
}
//创建cookie对象,记录本次访问时间
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd-HH:mm:ss");
String currentTime = simpleDateFormat.format(new Date());
cookie = new Cookie("last_time", currentTime);
//设置cookie存活1年
cookie.setMaxAge(60*60*24*365);
//resp响应cookie
resp.addCookie(cookie);
}
}

3.2 JSP初体验
Java服务端页面
简单来说:一个特殊的页面,即可定义html标签,又可定义Java代码
作用:简化书写,展示动态页面
本质:Servlet
脚本:JSP通过脚本方式来定义Java代码
<% Java代码 %> 就相当于Servlet中service方法

内置对象:在JSP页面中不需要获取和创建,可以直接使用对象
request
response
out
在JSP中响应内容,使用out
tomcat优先解析response缓冲区内容,再解析out缓冲区内容

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
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>

<head>
<title>Demo</title>
</head>

<body>

<h3>I am Title</h3>
<table border="1" width="300" align="center">
<tr>
<td>I am static resource</td>


</tr>

</table>
<%
// response.getWriter().write("I am JSP");
System.out.println("I am jsp");
out.write("Out");
%>
</body>
</html>

3.3 商品浏览记录

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

<h3>List</h3>

<a href="/Day36_war_exploded/GoodsInfoServlet?name=MI">MI</a><br>
<a href="/Day36_war_exploded/GoodsInfoServlet?name=HW">HW</a><br>
<a href="/Day36_war_exploded/GoodsInfoServlet?name=Apple">Apple</a><br>
<a href="/Day36_war_exploded/GoodsInfoServlet?name=TCL">TCL</a><br>
</body>

<body>
<%
//Java Code
//获取指定名称的Cookie对象
Cookie cookie = CookieUtils.findByName("goods_name", request.getCookies());
//判断是否存在浏览记录
if (cookie == null) {
out.write("No History");
} else {
//格式:MI-Apple..
String value = cookie.getValue();
for (String product : value.split("-")) {
out.write(product + "<br/>");
}
}
%>
</body>
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
@WebServlet("/GoodsInfoServlet")
public class GoodsInfoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置请求的解码方式
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
//获取请求参数name
String product = req.getParameter("name");
//展示当前商品详情
resp.getWriter().write("It's " + product);
//获取指定名称的Cookie对象
Cookie cookie = CookieUtils.findByName("goods_name", req.getCookies());

if (cookie == null) {
//如果不存在,将当前商品设置到cookie中
cookie = new Cookie("goods_name", product);
} else {
//如果存在,将浏览记录取出
String value = cookie.getValue();
//判断当前商品是否在此Cookie中
List<String> list = Arrays.asList(value.split("-"));
//如果不包含,追加,如果包含,不操作
if (!list.contains(product)) {
value=value+"-"+product;
}
//将value重置到Cookie中
cookie = new Cookie("goods_name", value);
}
//通过response响应到浏览器
cookie.setMaxAge(10);
resp.addCookie(cookie);

//制作a标签,实现继续浏览商品功能
resp.getWriter().write("<br/><a href='/Day36_war_exploded/goods.html'>Continue View</a><br/>");
//制作a标签,实现查看浏览记录功能
resp.getWriter().write("<a href='/Day36_war_exploded/history.jsp'>History</a><br/>");
}
}

一.ServletContext
1.1 概述
ServletContext是一个容器(域对象)可以存储键值对数据(String key,Object value),保存在ServletContext中的 数据不仅可以提供给所有的servlet使用,而且可以在整个项目范围内使用。

它的主要作用有3个:
作为域对象
可以获取当前应用下任何路径下的任何资源
获取初始化参数
获取文件MIME类型

1.2 域对象
说明 API
往servletcontext容器中存入数据,name为数据名称,object为数据的值 void setAttribute(String name,Object object)
从ServletContext中获取数据,根据指定的数据名称 Object getAttribute(String name)
从ServletContext中移除数据,根据指定的数据名称 void removeAttribute(String name)
生命周期
何时创建:项目加载时创建
何时销毁:项目卸载时销毁
作用范围:整个项目(多个Servlet都可以使用它)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@WebServlet(“/OneServlet”)
public class OneServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //向ServletContext域存储数据
    ServletContext sc1 = req.getServletContext();
    ServletContext sc2 = getServletContext();
    sc1.setAttribute("user","jack");
    resp.getWriter().write("Set Message");
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@WebServlet(“/TwoServlet”)
public class TwoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //从ServletContext域存储数据
    String user = (String) req.getServletContext().getAttribute("user");
    resp.getWriter().write("Get Message:"+user);
}

}
1.3 获取资源在服务器的真实地址
可以实现Web项目的移植性,动态获取文件真实路径
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@WebServlet(“/RealPath”)
public class RealPath extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //获取web.xml文件真实路径
    String realPath = req.getServletContext().getRealPath("/WEB-INF/web.xml");
    resp.getWriter().write(realPath);
}

}
1.4 获取全局的配置函数
读取web.xml配置文件中标签,实现参数和代码的解耦(多个Servlet可以获取)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@WebServlet(“/ContextPath”)
public class ContextPath extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //获取全局参数
    String value = req.getServletContext().getInitParameter("encode");
    System.out.println(value);
    resp.getWriter().write(value);
}

}
1
2
3
4
5

encode UTF-8 1.5 获取文件MIME类型 互联网通信中的一种数据类型 格式:大类型/小类型 例如:text/html image/jpeg

1
Get MIME
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@WebServlet(“/MIMEServlet”)
public class MIMEServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //获取请求参数
    String filename = req.getParameter("filename");
    //获取指定文件的mime类型
    String mimeType = req.getServletContext().getMimeType(filename);
    resp.getWriter().write(filename+"="+mimeType);
}

}
1.6 案例:统计网站的访问次数
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
@WebServlet(value = “/CountServlet”,loadOnStartup = 4)//服务器启动时,创建此servlet对象
public class CountServlet extends HttpServlet {
@Override
public void init() throws ServletException {
getServletContext().setAttribute(“count”,0);
}

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

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //设置response响应编码
    resp.setContentType("text/html;charset=utf-8");
    resp.getWriter().write("<h1>Welcome</h1>");

    //用户每次访问,从域中取出,++再存入
    Integer count = (Integer) req.getServletContext().getAttribute("count");
    count++;
    getServletContext().setAttribute("count", count);

    resp.getWriter().write("<div>You are the "+count+" visitor</div>");
}

}
二.Response
2.1 概述
response对象表示web服务器给浏览器返回的响应信息

作用:开发人员可以使用response对象方法,设置要返回给浏览器的响应信息

Response体系结构

ServletResponse

HttpServletResponse

org.apache.catalina.connector.ResponseFacade 实现类(由Tomcat提供)
2.2 设置Http响应消息
2.2.1 响应行
格式
协议/版本号 状态码
例如
HTTP/1.1 200
API
说明 API
设置状态码 void setStatus()
2.2.2 响应头
格式
响应头名称:响应头的值
例如
Location:http://www.oracle.com
API
说明 API
设置指定头名称和对应的值 void setHeader(String name,String vale);
2.2.3 响应体
API(输出流对象)
说明 API
字符输出流 PrintWriter getWriter()
字节输出流 ServletOutputStream getOutputStream()
注意:在同一个Servlet中,不能同时存在两种流,互斥

2.3 响应重定向
2.3.1 重定向特点
地址栏会发生改变
重定向是二次请求
重定向是客户端(浏览器)行为,可以跳转到服务器外部资源
不能使用request域共享数据
2.3.2 方式一
说明 API
设置状态码 response.setStatus(302)
设置响应头Location response.setHeader(“Location”,”重定向网络地址”);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@WebServlet(“/AServlet”)
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println("AServlet Run");
   resp.setStatus(302);
   resp.setHeader("Location","/Day35_war_exploded/BServlet");
}

}
1
2
3
4
5
6
7
8
9
10
11
12
@WebServlet(“/BServlet”)
public class BServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println("Bservlet Run");
}

}
2.3.3 方式二
说明 API
response专门处理重定向的方法 response.sendRedirect(“重定向网络地址”)
1
2
3
4
5
6
7
8
9
10
11
12
13
@WebServlet(“/AServlet”)
public class AServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    System.out.println("AServlet Run");
    resp.sendRedirect("http://www.baidu.com");
}

}
2.3.4 转发与重定向区别
哪个对象
API
转发(request对象的方法) request.getRequestDispatcher(“/bServlet”).forward(request,response);
重定向(response对象的方法) response.sendRedirect(“/Day35_war_exploded/bServlet”);
几次请求
转发 重定向
地址栏 没有改变 发生了改变
浏览器 发了一次请求 发了两次请求
服务器 只有一对请求和响应对象 有两对请求和响应对象
发生的位置 服务器 浏览器
小结
写法 说明
转发 (“/servlet资源路径”) 服务器内部行为
重定向 (“/虚拟路径(项目名)/servlet资源路径”) 浏览器外部行为
使用场景

使用场景 说明
如果需要传递数据(request域) 使用转发
如果不需要传递数据(request域) 使用重定向
2.4 响应定时刷新
说明 API
通过response设置响应头Refresh response.setHeader(“Refresh”,”间隔时间(秒);跳转页面”)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@WebServlet(“/RefreshServlet”)
public class RefreshServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.setHeader("Refresh","3;http://www.baidu.com");
    resp.setContentType("text/html;charset=utf-8");
    resp.getWriter().write("Success,Redirect After 3 seconds");
}

}
2.5 响应中文
步骤:

说明 API
统一服务器编码:指定服务器响应编码方式 resp.setContentType(“text/html;charset=utf-8”);
通过response获取字符输出流 PrintWriter pw = response.getWriter();
通过字符输出流输出文本 pw.write()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@WebServlet(“/EncodeServlet”)
public class EncodeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //可以解决客户端中文乱码,但是编码不统一

// resp.setCharacterEncoding(“GBK”);
resp.setContentType(“text/html;charset=utf-8”);
// PrintWriter pw = resp.getWriter();
// pw.write(“中文”);

    //链式编程
    resp.getWriter().write("中文");
}

}
综合案例
3.1 点击切换验证码
在页面展示登陆验证码,点击可以更换新验证码
作用:防止表单的恶意提交
本质:图片
1
2
3
4
5
6
7
8

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
@WebServlet(“/CheckCodeServlet”)
public class CheckCodeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

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

// 创建画布
int width = 120;
int height = 40;
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 获得画笔
Graphics g = bufferedImage.getGraphics();
// 填充背景颜色
g.setColor(Color.white);
g.fillRect(0, 0, width, height);
// 绘制边框
g.setColor(Color.red);
g.drawRect(0, 0, width - 1, height - 1);
// 生成随机字符
// 准备数据
String data = “ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890”;
// 准备随机对象
Random r = new Random();
// 声明一个变量 保存验证码
String code = “”;
// 书写4个随机字符
for (int i = 0; i < 4; i++) {
// 设置字体
g.setFont(new Font(“宋体”, Font.BOLD, 28));
// 设置随机颜色
g.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));

        String str = data.charAt(r.nextInt(data.length())) + "";
        g.drawString(str, 10 + i * 28, 30);

        //  将新的字符 保存到验证码中
        code = code + str;
    }
    //  绘制干扰线
    for (int i = 0; i < 6; i++) {
        //  设置随机颜色
        g.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));

        g.drawLine(r.nextInt(width), r.nextInt(height), r.nextInt(width), r.nextInt(height));
    }

    //  将验证码 打印到控制台
    System.out.println(code);

    //  将验证码放到session中
    req.getSession().setAttribute("code_session", code);

    //  将画布显示在浏览器中
    ImageIO.write(bufferedImage, "jpg", resp.getOutputStream());
}

}
3.2 文件下载
用户点击页面的链接,浏览器开始下载文件。
3.2.1 使用链接下载文件
1
Download_XML

缺点
浏览器可识别的媒体类型,是直接打开而不是下载
不能判断用户是否登录(vip),进行限制

3.2.2 使用Servlet下载文件
二个响应头二个字节流

说明 API
被下载文件的字节输入流 FileInputStream
response字节输出流 response字节输出流
告知客户端下载文件的MIME类型 Content-Type:MIME类型
告知浏览器以附件的方式保存 Content-Disposition:attachment;filename=文件名
1
Download_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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@WebServlet(“/DownloadServlet”)
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //获取文件名
    String filename = req.getParameter("filename");
    //获取文件真实路径,封装字节输入流
    ServletContext servletContext = req.getServletContext();
    String realPath = servletContext.getRealPath("/download/" + filename);
    FileInputStream in = new FileInputStream(realPath);

    //告诉浏览器下载文件MIME类型 Content-Type
    String mimeType = servletContext.getMimeType(filename);
    resp.setContentType(mimeType);

    //告知浏览器以附件的方式保存 Content-Disposition:attachment;filename=文件名
    //解决中文乱码和浏览器兼容性
    String userAgent = req.getHeader("user-agent");
    //调用工具处理
    filename=DownLoadUtils.getName(userAgent,filename);
    resp.setHeader("content-disposition", "attachment;filename=" + filename);

// resp.setHeader(“Content-Disposition”, “attachment;filename=” + filename);
//获取response的字节输出流
ServletOutputStream out = resp.getOutputStream();
//IO流Copy
// byte[] bys = new byte[1024];
// int len = 0;
// while ((len = in.read(bys)) != -1) {
// out.write(bys, 0, len);
// }
IoUtil.copy(in,out);
//释放资源
out.close();//可以交给Tomcat关闭
in.close();
}
}
中文乱码问题
如果该下载文件名是中文的话,会出现乱码…
谷歌和绝大多数的浏览器是通过 url编码
URLEncode() 编码
URLDecode() 解码
火狐浏览器 base64编码
如下工具包解决乱码问题:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DownLoadUtils {

public static String getName(String agent, String filename) throws UnsupportedEncodingException {
    if (agent.contains("Firefox")) {
        // 火狐浏览器
        BASE64Encoder base64Encoder = new BASE64Encoder();
        filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
    } else {
        // 其它浏览器
        filename = URLEncoder.encode(filename, "utf-8");
    }
    return filename;
}

}
3.2.2.1 hutool工具包
官网:https://www.hutool.cn/

hutool-all-5.2.3.jar
总结
一 ServletContext
概述
代表当前web项目对象
主要作用
共享数据(域对象)

获取资源文件在服务器上真实路径

获取全局的配置参数

web.xml中配置
获取文件MIME类型

互联网传输数据时,识别文件的类型
案例:统计网站的访问次数
二 Response
概述
开发人员可以使用response对象的方法,设置要返回给浏览器的响应信息
Response设置响应消息
设置响应行

void setStatus(int sc)
设置响应头

void setHeader(String name, String value)
设置响应体

ServletOutputStream getOutputStream() 字节输出流
PrintWriter getWriter() 字符输出流
响应重定向
转发与重定向的区别

转发

地址栏: 没有改变
浏览器: 发了一次请求
服务器: 只有一对请求和响应对象
发生的位置: 服务器
重定向

地址栏: 发生了改变
浏览器: 发了两次请求
服务器: 有两对请求和响应对象
发生的位置: 浏览器
响应定时刷新
响应中文
response.setContentType(“text/html;charset=utf-8”);
三 综合案例
点击切换验证码
随机数欺骗浏览器
文件下载
解决了中文编码
hutool工具包