0%

一. 介绍zookeeper
目标
了解Zookeeper的概念
了解分布式的概念
学习路径
Zookeeper概述
Zookeeper的发展历程
什么是分布式
Zookeeper的应用场景
1.1 zookeeper概述
Apache ZooKeeper的系统为分布式协调是构建分布式应用的高性能服务。
ZooKeeper 本质上是一个分布式的小文件存储系统。提供基于类似于文件系统的目录树方式的数据存储,并且可以对树中的节点进行有效管理。从而用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。
ZooKeeper 适用于存储和协同相关的关键数据,不适合用于大数据量存储。

1.2 zookeeper被大量使用
Hadoop:使用ZooKeeper 做Namenode 的高可用。
HBase:保证集群中只有一个master,保存hbase:meta表的位置,保存集群中的RegionServer列表。
Kafka:集群成员管理,controller 节点选举。
1.3 什么是分布式
1.3.1 集中式系统
集中式系统,集中式系统中整个项目就是一个独立的应用,整个应用也就是整个项目,所有的东西都在一个应用里面。部署到一个服务器上。
布署项目时,放到一个tomcat里的。也称为单体架构

1.3.2 分布式系统
多台计算机构成
计算机之间通过网络进行通信
彼此进行交互
共同目标
1.3.3 小结
集中式:开发项目时都是在同一个应用里,布署到同一个tomcat,只有一个tomcat即可

分布:分多个工程开发,布署多个tomcat里

1.4 zookeeper的应用场景
1.4.1 注册中心
分布式应用中,通常需要有一套完整的命名规则,既能够产生唯一的名称又便于人识别和记住,通常情况下用树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构。通过调用Zookeeper提供的创建节点的API,能够很容易创建一个全局唯一的path,这个path就可以作为一个名称。
Dubbo中使用ZooKeeper来作为其命名服务,维护全局的服务地址列表。

1.4.2 配置中心
​ 数据发布/订阅即所谓的配置中心:发布者将数据发布到ZooKeeper一系列节点上面,订阅者进行数据订阅,当数据有变化时,可以及时得到数据的变化通知,达到动态获取数据的目的。

ZooKeeper 采用的是推拉结合的方式。

推: 服务端会推给注册了监控节点的客户端 Wathcer 事件通知

拉: 客户端获得通知后,然后主动到服务端拉取最新的数据

1.4.3 分布式锁——非公平锁
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁。

1.4.4 分布式队列——公平锁
在传统的单进程编程中,我们使用队列来存储一些数据结构,用来在多线程之间共享或传递数据。分布式环境下,我们同样需要一个类似单进程队列的组件,用来实现跨进程、跨主机、跨网络的数据共享和数据传递,这就是我们的分布式队列。

1.4.5 负载均衡
负载均衡是通过负载均衡算法,用来把对某种资源的访问分摊给不同的设备,从而减轻单点的压力。

每台工作服务器在启动时都会去ZooKeeper的servers节点下注册临时节点,每台客户端在启动时都会去servers节点下取得所有可用的工作服务器列表,并通过一定的负载均衡算法计算得出一台工作服务器,并与之建立网络连接

1.4.6 小结
Zookeeper概述
Zookeeper的发展历程
什么是分布式 应用是布署多台服务器上(多个tomcat)
Zookeeper的应用场景
注册中心(房产中介)
配置中心(多台应用(服务) 修改配置文件,不需要重启)
分布式锁 多个应用(服务)同一时刻,只有一个服务能够执行某个操作
分布式队列:使得多进程、多服务间可以同步数据、传输数据、协同工作
负载均衡:使用多应用的调用频率比较平均
二.zookeeper环境搭建
目标

安装Zookeeper

学习路径

Zookeeper的发展历程
什么是分布式
Zookeeper的应用场景
2.1 前提
安装Docker

2.2安装zookeeper
2.2.1 下载

1
2
3
4
5
6
#Docker拉取镜像
$ docker pull zookeeper
#查看镜像文件
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
zookeeper latest 454af3da184c 6 days ago 252MB

2.2.2 修改配置文件

使用一个目录,创建zk配置文件/数据/日志目录

1
2
3
4
5
6
$ mkdir conf  data log
$ tree zk
zk
├── conf
├── data
└── log

暂时启动zookeeper

1
$ docker run -d --name zk --restart always zookeeper

复制配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ docker cp -a zk:/conf/zoo.cfg ~/Program/zk/conf/zoo.cfg    
$ cat ~/Program/zk/conf/zoo.cfg

dataDir=/data
dataLogDir=/datalog
tickTime=2000
initLimit=5
syncLimit=2
autopurge.snapRetainCount=3
autopurge.purgeInterval=0
maxClientCnxns=60
standaloneEnabled=true
admin.enableServer=true
server.1=localhost:2888:3888;2181

停止并删除未配置的zookeeper容器

1
$ docker stop zk && docker rm zk

指定保存数据的目录:data目录

如果需要日志,可以创建log文件夹,指定dataLogDir属性

2.2.3 启动zookeeper

1
2
3
4
5
6
7
8
$ docker run -d --name zk --restart always \  
-p 2181:2181 -p 2888:2888 -p 3888:3888 \
-v /Users/swzxsyh/Program/zk/conf/zoo.cfg:/conf/zoo.cfg \
-v /Users/swzxsyh/Program/zk/data:/data \
-v /Users/swzxsyh/Program/zk/log:/datalog \
zookeeper

#zookeeper客户端端口,跟随端口,选择端口

2.2.4 启动客户端测试

下载客户端,启动

$ ./zkCli.sh
/usr/bin/java
2020-06-16 17:13:31,750 [myid:] - INFO [main:ClientCnxn@1653] - zookeeper.request.timeout value is 0. feature enabled=

省略中间输出…

出现此行即说明连接成功

Welcome to ZooKeeper!
2020-06-16 17:13:31,757 [myid:localhost:2181] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1112] - Opening socket connection to server localhost/127.0.0.1:2181. Will not attempt to authenticate using SASL (unknown error)
JLine support is enabled
2020-06-16 17:13:31,834 [myid:localhost:2181] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@959] - Socket connection established, initiating session, client: /127.0.0.1:64028, server: localhost/127.0.0.1:2181
[zk: localhost:2181(CONNECTING) 0] 2020-06-16 17:13:31,896 [myid:localhost:2181] - INFO [main-SendThread(localhost:2181):ClientCnxn$SendThread@1394] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x100014a6eed0000, negotiated timeout = 30000

WATCHER::

WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0]
2.2.5 小结
pull镜像
创建data/conf/log目录
复制zoo.cfg文件并加载启动
三.zookeeper基本操作
目标

Zookeeper的客户端命令
Zookeeper的java的api操作
学习路径

Zookeeper的数据结构

节点的分类

持久性
临时性
客户端命令(创建、查询、修改、删除)

Zookeeper的java的api介绍(创建、查询、修改、删除)

Zookeeper的watch机制

NodeCache
PathChildrenCache
TreeCache
3.1 zookeeper数据结构
ZooKeeper 的数据模型是层次模型。层次模型常见于文件系统。

每个目录下面可以创建文件,也可以创建子目录,最终构成了一个树型结构。通过这种树型结构的目录,可以将文件分门别类的进行存放,方便我们后期查找。

层次模型和key-value 模型是两种主流的数据模型。ZooKeeper 使用文件系统模型主要基于以下两点考虑
文件系统的树形结构便于表达数据之间的层次关系。
文件系统的树形结构便于为不同的应用分配独立的命名空间(namespace 路径url)
ZooKeeper 的层次模型称作data tree。Datatree 的每个节点叫作znode(Zookeeper node)。不同于文件系统,每个节点都可以保存数据。每个节点都有一个版本(version)。版本从0 开始计数。

如图所示,data tree中有两个子树,用于应用1( /app1)和应用2(/app2)。

每个客户端进程pi 创建一个znode节点 p_i 在 /app1下, /app1/p_1就代表一个客户端在运行。

3.2 节点的分类
一个znode可以是持久性的,也可以是临时性的
持久性znode[PERSISTENT],这个znode一旦创建不会丢失,无论是zookeeper宕机,还是client宕机。
临时性的znode[EPHEMERAL],如果zookeeper宕机了,或者client在指定的timeout时间内没有连接server,都会被认为丢失。 -e
znode也可以是顺序性的,每一个顺序性的znode关联一个唯一的单调递增整数。这个单调递增整数是znode名字的后缀。
持久顺序性的znode(PERSISTENT_SEQUENTIAL):znode 处理具备持久性的znode的特点之外,znode的名称具备顺序性。 -s
临时顺序性的znode(EPHEMERAL_SEQUENTIAL):znode处理具备临时性的znode特点,znode的名称具备顺序性。-s
3.3 客户端命令
3.3.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
[zk: localhost:2181(CONNECTED) 8] help
ZooKeeper -server host:port cmd args
addauth scheme auth
close
config [-c] [-w] [-s]
connect host:port
create [-s] [-e] [-c] [-t ttl] path [data] [acl]
delete [-v version] path
deleteall path
delquota [-n|-b] path
get [-s] [-w] path
getAcl [-s] path
history
listquota path
ls [-s] [-w] [-R] path
ls2 path [watch]
printwatches on|off
quit
reconfig [-s] [-v version] [[-file path] | [-members serverID=host:port1:port2;port3[,…]]] | [-add serverId=host:port1:port2;port3[,…]] [-remove serverId[,…]*]
redo cmdno
removewatches path [-c|-d|-a] [-l]
rmr path
set [-s] [-v version] path data
setAcl [-s] [-v version] [-R] path acl
setquota -n|-b val path
stat [-w] path
sync path
3.3.2 查询根路径下的节点
1
2
[zk: localhost:2181(CONNECTED) 9] ls /zookeeper
[config, quota]
3.3.3 创建普通永久节点
1
2
3
4
5
#创建HelloZK节点,值为Hello,Zookeeper
[zk: localhost:2181(CONNECTED) 10] create /HelloZK “Hello,Zookeeper”
Created /HelloZK
[zk: localhost:2181(CONNECTED) 11] ls /HelloZK
[]
3.3.4 创建带序号永久节点
1
2
[zk: localhost:2181(CONNECTED) 13] create -s /HelloZK_SEQ “ZK With SEQUENCE”
Created /HelloZK_SEQ0000000001
3.3.5 创建普通临时节点
1
2
3
4
5
6
7
8

-e:表示普通临时节点

[zk: localhost:2181(CONNECTED) 17] create -e /Hello_ZK_Normal “Normal Node”
Created /Hello_ZK_Normal
[zk: localhost:2181(CONNECTED) 19] ls /
[HelloZK, HelloZK_SEQ0000000001, Hello_ZK_Normal, zookeeper]

关闭客户端,再次打开查看 Hello_ZK_Normal 节点消失

[zk: localhost:2181(CONNECTED) 1] ls /
[HelloZK, HelloZK_SEQ0000000001, zookeeper]
3.3.6 创建带序号临时节点
1
2
3
4
5
6
7

-e:表示普通临时节点

-s:表示带序号节点

[zk: localhost:2181(CONNECTED) 2] create -e -s /Hello_ZK_SEQ_TEMP “Temp And Sequence”
Created /Hello_ZK_SEQ_TEMP0000000003

关闭客户端,再次打开查看 Hello_ZK_SEQ_TEMP 节点消失

[zk: localhost:2181(CONNECTED) 0] ls /
[HelloZK, HelloZK_SEQ0000000001, zookeeper]
3.3.7 查询节点数据
1
2
3
4
5
6
7
8
9
10
11
12
13
[zk: localhost:2181(CONNECTED) 7] get -s /HelloZK
Hello,Zookeeper
cZxid = 0x2
ctime = Tue Jun 16 17:28:46 CST 2020
mZxid = 0x2
mtime = Tue Jun 16 17:28:46 CST 2020
pZxid = 0x2
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 15
numChildren = 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

­­­­­­­­­­­节点的状态信息,也称为stat结构体­­­­­­­­­­­­­­­­­­­

创建该znode的事务的zxid(ZooKeeper Transaction ID)

事务ID是ZooKeeper为每次更新操作/事务操作分配一个全局唯一的id,表示zxid,值越小,表示越先执行

cZxid = 0x4454 # 0x0表示十六进制数0
ctime = Thu Jan 01 08:00:00 CST 1970 # 创建时间
mZxid = 0x4454 # 最后一次更新的zxid
mtime = Thu Jan 01 08:00:00 CST 1970 # 最后一次更新的时间
pZxid = 0x4454 # 最后更新的子节点的zxid
cversion = 5 # 子节点的变化号,表示子节点被修改的次数
dataVersion = 0 # 表示当前节点的数据变化号,0表示当前节点从未被修改过
aclVersion = 0 # 访问控制列表的变化号 access control list

如果临时节点,表示当前节点的拥有者的sessionId

ephemeralOwner = 0x0 # 如果不是临时节点,则值为0
dataLength = 13 # 数据长度
numChildren = 1 # 子节点的数量
3.3.8 修改节点数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[zk: localhost:2181(CONNECTED) 9] set /HelloZK “Modify HelloZK”

WATCHER::

WatchedEvent state:SyncConnected type:NodeDataChanged path:/HelloZK
[zk: localhost:2181(CONNECTED) 10]
[zk: localhost:2181(CONNECTED) 10] get -s /HelloZK
Modify HelloZK
cZxid = 0x2
ctime = Tue Jun 16 17:28:46 CST 2020
mZxid = 0xc
mtime = Tue Jun 16 17:40:47 CST 2020
pZxid = 0x2
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 14
numChildren = 0
3.3.9 创建子节点
1
2
3
4
5
[zk: localhost:2181(CONNECTED) 36] create /TestDelete
Created /TestDelete
[zk: localhost:2181(CONNECTED) 37] create /TestDelete/RMR
Created /TestDelete/RMR
[zk: localhost:2181(CONNECTED) 38]
3.3.10 删除节点
1
2
3
[zk: localhost:2181(CONNECTED) 11] delete /HelloZK_SEQ0000000001
[zk: localhost:2181(CONNECTED) 12] ls /
[HelloZK, zookeeper]
3.3.11 递归删除节点
1
2
3
4
5
6
7
8

有子节点的使用delete会提示目录非空

[zk: localhost:2181(CONNECTED) 39] delete /TestDelete
Node not empty: /TestDelete

需用rmr命令删除,现已更新为deleteall

[zk: localhost:2181(CONNECTED) 12] rmr /TestDelete
The command ‘rmr’ has been deprecated. Please use ‘deleteall’ instead.
[zk: localhost:2181(CONNECTED) 13] ls /
[jsodjsd0000000011, sjdiosdjsoi, zookeeper]
3.3.12 查看节点状态
1
2
3
4
5
6
7
8
9
10
11
12
[zk: localhost:2181(CONNECTED) 2] stat /zookeeper
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -2
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 2
3.3.13 日志的可视化
日志存储路径

1
~/Program/zk/log/version-2
日志都是以二进制文件存储的,需要使用命令行查看

1
2
3
4
5
6
7
8

Docker拉取了最新镜像,需要4个jar包了

$ ls | grep jar
log4j-1.2.17.jar
slf4j-api-1.7.25.jar
zookeeper-3.6.1.jar
zookeeper-jute-3.6.1.jar

然后使用命令行查看

$ java -classpath :./log4j-1.2.17.jar:slf4j-api-1.7.25.jar:zookeeper-3.6.1.jar:zookeeper-jute-3.6.1.jar org.apache.zookeeper.server.LogFormatter log.1
3.3.14 小结
ls 查看
help查看所有命令
create 路径 数据 -s 代表有序 -e 代临时
get 路径 查询
set 路径 新的数据
delete 路径 单一路径,没有子节点
rmr 路径 递归删除
stat 路径 查看节点状态
日志,依赖4个jar包
3.4 zookeeper的Java Api介绍
3.4.1 ZooKeeper常用Java API
原生Java API

ZooKeeper 原生Java API位于org.apache.ZooKeeper包中

ZooKeeper-3.x.x. Jar 为官方提供的 java API

Apache Curator

Apache Curator是 Apache ZooKeeper的Java客户端库。

Curator.项目的目标是简化ZooKeeper客户端的使用。

ZkClient

开源的ZooKeeper客户端,zkclient-x.x.Jar也是在原生 api 基础之上进行扩展的开源 JAVA 客户端。

3.4.2 创建Java Maven工程,导入依赖
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


4.0.0

org.example
zookeeper
1.0-SNAPSHOT

org.apache.zookeeper zookeeper 3.4.7 org.apache.curator curator-framework 4.0.1 org.apache.curator curator-recipes 4.0.1 junit junit 4.12 3.4.3 CURD节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 public class TestZookeeper { //连接地址 String url = "127.0.0.1:2181"; //会话超时时间 int sessionTimeoutMs = 1000; //连接超时时间 int connectionTimeoutMs = 1000; //重试策略 RetryPolicy retryPolicy = null; //创建客户端 CuratorFramework client = null;

String NodeName = null;

@Before
public void createLinkToZK() {
//重试策略
/**
* RetryPolicy: 失败的重试策略的公共接口
* ExponentialBackoffRetry是 公共接口的其中一个实现类
* 参数1: 初始化sleep的时间,用于计算之后的每次重试的sleep时间
* 参数2:最大重试次数
参数3(可以省略):最大sleep时间,如果上述的当前sleep计算出来比这个大,那么sleep用这个时间
/
retryPolicy = new ExponentialBackoffRetry(1000, 1);
//创建客户端
/
*
* 参数1:连接的ip地址和端口号
* 参数2:会话超时时间,单位毫秒
* 参数3:连接超时时间,单位毫秒
* 参数4:失败重试策略
*/
client = CuratorFrameworkFactory.newClient(url, sessionTimeoutMs, connectionTimeoutMs, retryPolicy);
//启动客户端
client.start();
}

@After
public void closeLike() throws Exception {
if (NodeName != null) {
requireNode(NodeName);
} else if (NodeName == null) {
System.out.println(“No Node”);
}
//关闭客户端
client.close();
}

//1. 创建一个空节点(a)(只能创建一层节点)
@Test
public void createOneLevelEmptyNode() throws Exception {
NodeName = “/OneLevelEmptyNode”;
client.create().forPath(NodeName);
}

//2. 创建一个有内容的b节点(只能创建一层节点)
@Test
public void createOneLevelNodeWithMessage() throws Exception {
NodeName = “/OneLevelNodeWithMessage”;
client.create().forPath(NodeName, “OneLevelNodeWithMessage”.getBytes());
}

//3. 创建持久节点,同时创建多层节点
@Test
public void createMultilevelPersistentNode() throws Exception {
NodeName = “/Multilevel/Persistent/Node”;
client.create().creatingParentsIfNeeded().forPath(NodeName, “MultilevelPersistentNode”.getBytes());
}

//4. 创建带有的序号的持久节点
@Test
public void creatPersistentNode() throws Exception {
// NodeName = “/Persistent/Node”;
client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT_SEQUENTIAL).forPath(“/Persistent/Node”, “PERSISTENT_SEQUENTIAL”.getBytes());
}

//5. 创建临时节点(客户端关闭,节点消失),设置延时5秒关闭(Thread.sleep(5000))
@Test
public void creatTempNode() throws Exception {
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(“/Temporary/Node”, “Temporary Node”.getBytes());
}

//6. 创建临时带序号节点(客户端关闭,节点消失),设置延时5秒关闭(Thread.sleep(5000))
@Test
public void creatTempNodeWithSequenceNode() throws Exception {
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(“/TempNode/With/Sequence/Node”, “creatTempNodeWithSequence”.getBytes());
}

//修改节点描述
@Test
public void updateNodeMessage() throws Exception {
NodeName = “/OneLevelNodeWithMessage”;
client.setData().forPath(NodeName, “Update Node Message”.getBytes());
}

//节点数据查询
public void requireNode(String s) throws Exception {
byte[] bytes = client.getData().forPath(String.valueOf(s));
System.out.println(new String(bytes));
}

//删除单个节点
@Test
public void deleteSingleNode() throws Exception {
client.create().forPath(“/testAddForDelete”);
client.delete().forPath(“/testAddForDelete”);
}

//删除多个节点
@Test
public void deleteMultiNode() throws Exception {
client.create().creatingParentsIfNeeded().forPath(“/DeleteMultiNode/deleteMultiNode”, “Create Multi NodeForDelete”.getBytes());
client.delete().deletingChildrenIfNeeded().forPath(“/DeleteMultiNode”);
}

//强制删除
@Test
public void forceDelete() throws Exception {
//NodeName=”/forceDelete/forceDelete”;
client.create().creatingParentsIfNeeded().forPath(“/forceDelete/forceDelete”, “Create Multi NodeForDelete”.getBytes());
client.delete().guaranteed().deletingChildrenIfNeeded().forPath(“/forceDelete”);
}
}
3.4.4 小结
Curator是Appache封装操作Zookeeper的客户端, 操作zookeer数据变得更简单

使用步骤:

创建重试策略

创建客户端 url:port

启动客户端

使用客户端对节点操作

– create forPath, creatingparent…….., withMode(持久,临时)

– setData 修改数据

– getData 查询数据

– delete 删除数据, deletingChildrenIfNeeded递归删除

关闭客户端

3.5 watch机制

3.5.1 什么是watch机制
​ zookeeper作为一款成熟的分布式协调框架,订阅-发布功能是很重要的一个。订阅发布功能,就是观察者模式。观察者会订阅一些感兴趣的主题,然后这些主题一旦变化了,就会自动通知到这些观察者。

zookeeper的订阅发布也就是watch机制,是一个轻量级的设计。因为它采用了一种推拉结合的模式。一旦服务端感知主题变了,那么只会发送一个事件类型和节点信息给关注的客户端,而不会包括具体的变更内容,所以事件本身是轻量级的,这就是所谓的“推”部分。然后,收到变更通知的客户端需要自己去拉变更的数据,这就是“拉”部分。watche机制分为添加数据和监听节点。

Curator在这方面做了优化,Curator引入了Cache的概念用来实现对ZooKeeper服务器端进行事件监听。Cache是Curator对事件监听的包装,其对事件的监听可以近似看做是一个本地缓存视图和远程ZooKeeper视图的对比过程。而且Curator会自动的再次监听,我们就不需要自己手动的重复监听了。

Curator中的cache共有三种

NodeCache(监听和缓存根节点变化) 只监听单一个节点(变化 添加,修改,删除)
PathChildrenCache(监听和缓存子节点变化) 监听这个节点下的所有子节点(变化 添加,修改,删除)
TreeCache(监听和缓存根节点变化和子节点变化) NodeCache+ PathChildrenCache 监听当前节点及其下的所有子节点的变化
3.5.2 NodeCache
介绍

NodeCache是用来监听节点的数据变化的,当监听的节点的数据发生变化的时候就会回调对应的函数。

增加监听

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
public class TestCuratorCache {
//连接地址
String url = “127.0.0.1:2181”;
//会话超时时间
int sessionTimeoutMs = 1000;
//连接超时时间
int connectionTimeoutMs = 1000;
//重试策略
RetryPolicy retryPolicy = null;
//创建客户端
CuratorFramework client = null;

@Before
public void createLinkToZK() {
//重试策略
retryPolicy = new ExponentialBackoffRetry(1000, 1);
//创建客户端
client = CuratorFrameworkFactory.newClient(url, sessionTimeoutMs, connectionTimeoutMs, retryPolicy);
//启动客户端
client.start();
}

@After
public void closeLike() throws Exception {

//关闭客户端
client.close();

}

@Test
public void testNodeCache() throws Exception {
client.create().forPath(“/testNodeCache”);

/*
    *NodeCache(客户端, 被监听节点);
     */
NodeCache nodeCache = new NodeCache(client, "/testNodeCache");
/*
    * true  得到数据
    * false 不能获取节点数据
    */
nodeCache.start(true);

System.out.println(new String(nodeCache.getCurrentData().getData()));

client.setData().forPath("/testNodeCache", "Update Node Cache".getBytes());
nodeCache.getListenable().addListener(new NodeCacheListener() {
  @Override
  public void nodeChanged() throws Exception {
    String data = new String(nodeCache.getCurrentData().getData());
    Thread.sleep(1000 * 2);
    System.out.println(nodeCache.getCurrentData().getPath() + "  \t\t " + data);
  }
});
Thread.sleep(1000 * 4);
client.delete().forPath("/testNodeCache");

}
}
测试

3.5.3 PathChildrenCache
介绍

PathChildrenCache是用来监听指定节点 的子节点变化情况

增加监听

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
@Test
public void testPathChildrenCache() throws Exception {

client.create().forPath(“/testPathChildrenCache”);
/*
* new PathChildrenCache(客户端, 节点 , 是/否得到数据);
/
PathChildrenCache pathChildrenCache = new PathChildrenCache(client, “/testPathChildrenCache”, true);
///
*
// * NORMAL: 普通启动方式, 在启动时缓存子节点数据
// * POST_INITIALIZED_EVENT:在启动时缓存子节点数据,提示初始化
// * BUILD_INITIAL_CACHE: 在启动时什么都不会输出
// * 在官方解释中说是因为这种模式会在start执行执行之前先执行rebuild的方法,而rebuild的方法不会发出任何事件通知。
// */
pathChildrenCache.start(PathChildrenCache.StartMode.POST_INITIALIZED_EVENT);

System.out.println(pathChildrenCache.getCurrentData());

client.create().forPath(“/testPathChildrenCache/A”);
client.setData().forPath(“/testPathChildrenCache/A”, “Update Node Message”.getBytes());
pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent pathChildrenCacheEvent) throws Exception {
if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.CHILD_UPDATED) {

    System.out.println("CHILD_UPDATED Node: "+pathChildrenCacheEvent.getData().getPath());
    System.out.println("CHILD_UPDATED Node Date :"+new String(pathChildrenCacheEvent.getData().getPath()));
  } else if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.INITIALIZED) {
    System.out.println("Initialization");
  } else if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.CHILD_REMOVED) {
    System.out.println("CHILD_REMOVED Node: "+pathChildrenCacheEvent.getData().getPath());
    System.out.println("CHILD_REMOVED Node Date :"+new String(pathChildrenCacheEvent.getData().getPath()));
  } else if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.CHILD_ADDED) {
    System.out.println("CHILD_ADDED Node: "+pathChildrenCacheEvent.getData().getPath());
    System.out.println("CHILD_ADDED Node Date :"+new String(pathChildrenCacheEvent.getData().getPath()));
  } else if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.CONNECTION_SUSPENDED) {
    System.out.println("CONNECTION_SUSPENDED");
  } else if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.CONNECTION_RECONNECTED) {
    System.out.println("CONNECTION_RECONNECTED");
  } else if (pathChildrenCacheEvent.getType() == PathChildrenCacheEvent.Type.CONNECTION_LOST) {
    System.out.println("CONNECTION_LOST");
  }
}

});
client.delete().guaranteed().deletingChildrenIfNeeded().forPath(“/testPathChildrenCache”);
}
3.5.4 TreeCache
介绍

TreeCache有点像上面两种Cache的结合体,NodeCache能够监听自身节点的数据变化(或者是创建该节点),PathChildrenCache能够监听自身节点下的子节点的变化,而TreeCache既能够监听自身节点的变化、也能够监听子节点的变化。

添加监听

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
@Test
public void testTreeCache() throws Exception {

client.create().forPath(“/testTreeCache”);
//监听当前Node Data Tree
TreeCache treeCache = new TreeCache(client, “/testTreeCache”);
treeCache.start();

System.out.println(treeCache.getCurrentData(“/testTreeCache”));

treeCache.getListenable().addListener(new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception {
if (treeCacheEvent.getType() == TreeCacheEvent.Type.NODE_ADDED) {
System.out.println(“NODE_ADDED: “+treeCacheEvent.getData().getPath());

    client.delete().deletingChildrenIfNeeded().forPath("/testTreeCache/Test");

  } else if (treeCacheEvent.getType() == TreeCacheEvent.Type.NODE_REMOVED) {
    System.out.println("NODE_REMOVED: "+treeCacheEvent.getData().getPath());

  } else if (treeCacheEvent.getType() == TreeCacheEvent.Type.NODE_UPDATED) {
    System.out.println("NODE_UPDATED: "+treeCacheEvent.getData().getPath());

  } else if (treeCacheEvent.getType() == TreeCacheEvent.Type.INITIALIZED) {
    System.out.println("INITIALIZED");
  } else if (treeCacheEvent.getType() == TreeCacheEvent.Type.CONNECTION_SUSPENDED) {
    System.out.println("CONNECTION_SUSPENDED");
  } else if (treeCacheEvent.getType() == TreeCacheEvent.Type.CONNECTION_RECONNECTED) {
    System.out.println("CONNECTION_RECONNECTED");
  } else if (treeCacheEvent.getType() == TreeCacheEvent.Type.CONNECTION_LOST) {
    System.out.println("CONNECTION_LOST");
  }
}

});
client.delete().guaranteed().deletingChildrenIfNeeded().forPath(“/testTreeCache”);
}
3.5.5 小结
Zookeeper:分布小文件存储系统,树形层级数据结构,路径唯一

动物管理员,管的是服务
应用:注册中心、配置中心、分布式锁、分布式队列、负载均衡
配置zoo.cfg dataPath, 2181

节点类型:持久化(有序与无序 -s),临时(-e 临时, -s 有序与无序) crud

api: Curator 创建重试策略->客户端(url ip:port)->启动->使用client操作crud->关闭

Watch: 监听节点数据变化,一旦生变更(添加、修改、删除)时,服务端通知客户端(addListener)

NodeCache: 听监听单一节点变化

PathChildrenCache: 监听节点下的子节点
TreeCache: NodeCache+PathChildrenCache

四.zookeeper集群搭建
目标

搭建Zookeeper集群

操作路径

了解集群

集群介绍
集群模式
集群原理及架构图

搭建集群(使用linux)

集群中Leader选举机制
测试集群

4.1 了解集群
4.1.1 集群介绍
Zookeeper 集群搭建指的是 ZooKeeper 分布式模式安装。 通常由 2n+1台 servers 组成 奇数。 这是因为为了保证 Leader 选举(基于 Paxos 算法的实现) 能或得到多数的支持,所以 ZooKeeper 集群的数量一般为奇数。

4.1.2 集群模式
伪分布式集群搭建
完全分布式集群搭建
4.2 架构图

集群工作的核心
事务请求(写操作) 的唯一调度和处理者,保证集群事务处理的顺序性;
集群内部各个服务器的调度者。
对于 create, setData, delete 等有写操作的请求,则需要统一转发给leader 处理, leader 需要决定编号、执行操作,这个过程称为一个事务。

Leader:Zookeeper(领导者):

集群工作的核心
事务请求(写操作) 的唯一调度和处理者,保证集群事务处理的顺序性;
集群内部各个服务器的调度者。
对于 create, setData, delete 等有写操作的请求,则需要统一转发给leader 处理, leader 需要决定编号、执行操作,这个过程称为一个事务。

Follower(跟随者)

处理客户端非事务(读操作) 请求,转发事务请求给 Leader;参与集群 Leader 选举投票 2n-1台可以做集群投票。

Observer:观察者角色

针对访问量比较大的 zookeeper 集群, 还可新增观察者角色。观察 Zookeeper 集群的最新状态变化并将这些状态同步过来,其对于非事务请求可以进行独立处理,对于事务请求,则会转发给 Leader服务器进行处理。
不会参与任何形式的投票只提供非事务服务,通常用于在不影响集群事务处理能力的前提下提升集群的非事务处理能力。

4.3 搭建过程
4.3.1 配置zookeeper-compose.yml
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
version: ‘2’
services:
zoo1:
image: zookeeper:3.4.14
restart: always
container_name: zoo1
ports:
- “2181:2181”
environment:
ZOO_MY_ID: 1
ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888

zoo2:
    image: zookeeper:3.4.14
    restart: always
    container_name: zoo2
    ports:
        - "2182:2181"
    environment:
        ZOO_MY_ID: 2
        ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
 
zoo3:
    image: zookeeper:3.4.14
    restart: always
    container_name: zoo3
    ports:
        - "2183:2181"
    environment:
        ZOO_MY_ID: 3
        ZOO_SERVERS: server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888

这个配置文件会告诉 Docker 分别运行三个 zookeeper 镜像, 并分别将本地的 2181, 2182, 2183 端口绑定到对应的容器的2181端口上.
ZOO_MY_ID 和 ZOO_SERVERS 是搭建 ZK 集群需要设置的两个环境变量, 其中 ZOO_MY_ID 表示 ZK 服务的 id, 它是1-255 之间的整数, 必须在集群中唯一. ZOO_SERVERS 是ZK 集群的主机列表.

4.3.2 启动zookeeper
1
2
3
4
5
$ docker-compose -f zookeeper-compose.yml up -d
Creating network “program_default” with the default driver
Creating zoo3 … done
Creating zoo2 … done
Creating zoo1 … done
4.3.3 查看zookeeper状态
1
2
3
4
5
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
63dacd015bf9 zookeeper:3.4.14 “/docker-entrypoint.…” 6 minutes ago Up 6 minutes 2888/tcp, 0.0.0.0:2181->2181/tcp, 3888/tcp zoo1
2bfaa57664f9 zookeeper:3.4.14 “/docker-entrypoint.…” 6 minutes ago Up 6 minutes 2888/tcp, 3888/tcp, 0.0.0.0:2182->2181/tcp zoo2
4ff61abb0d2f zookeeper:3.4.14 “/docker-entrypoint.…” 6 minutes ago Up 6 minutes 2888/tcp, 3888/tcp, 0.0.0.0:2183->2181/tcp zoo3
4.4 leader选举
在领导者选举的过程中,如果某台ZooKeeper获得了超过半数的选票,则此ZooKeeper就可以成为Leader了。

服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking(选举状态)。

服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,

每个Server发出一个投票由于是初始情况,1和2都会将自己作为Leader服务器来进行投票,每次投票会包含所推举的服务器的myid和ZXID,使用(myid, ZXID)来表示,此时1的投票为(1, 0),2的投票为(2, 0),然后各自将这个投票发给集群中其他机器。
接受来自各个服务器的投票集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自LOOKING状态的服务器

处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下

    · 优先检查ZXID。ZXID比较大的服务器优先作为Leader。

    · 如果ZXID相同,那么就比较myid。myid较大的服务器作为Leader服务器

由于服务器2的编号大,更新自己的投票为(2, 0),然后重新投票,对于2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可,此时集群节点状态为LOOKING。

统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息

服务器3启动,进行统计后,判断是否已经有过半机器接受到相同的投票信息,对于1、2、3而言,已统计出集群中已经有3台机器接受了(3, 0)的投票信息,此时投票数正好大于半数,便认为已经选出了Leader,

改变服务器状态。一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING

所以服务器3成为领导者,服务器1,2成为从节点。

4.5 测试集群
测试使用任何一个IP都可以获取

测试如果有个机器宕机,(./zkServer.sh stop),会重新选取领导者。

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

CuratorFramework client = null;

@After
public void closeLike() throws Exception {
    //关闭客户端
    client.close();
}

@Test
public void createNode() throws Exception {
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 1);
    CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 3000, 3000, retryPolicy);
    client.start();
    client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/createNode/Nodes");
}

@Test
public void updateNode() throws Exception {
    //创建失败策略对象
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 1);
    client = CuratorFrameworkFactory.newClient("127.0.0.1:2182", 1000, 1000, retryPolicy);
    client.start();
    client.setData().forPath("/createNode/Nodes", "Update Node".getBytes());
    client.close();
}

@Test
public void getData() throws Exception {
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 1);
    client = CuratorFrameworkFactory.newClient("127.0.0.1:2183", 1000, 1000, retryPolicy);
    client.start();
    byte[] bytes = client.getData().forPath("/createNode/Nodes");
    System.out.println(new String(bytes));
    client.close();
}

@Test
public void deleteNode() throws Exception {
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 1);
    client = CuratorFrameworkFactory.newClient("127.0.0.1:2183", 1000, 1000, retryPolicy);
    client.start();
    client.delete().deletingChildrenIfNeeded().forPath("/createNode");
    client.close();
}

}
4.6 小结
什么是集群:把具有相同功能应用布署多个到网络中,聚集一起,实现数据的同步,一旦有应用服务挂了,集群中的其它应用服务可以顶上,保证应用的正常运作

Zookeeper集群架构

Leader领导者 可以进行读写,写操作成功后会同步到各个从和观察者

Follower跟从者 只能读,如果有写操作时则转给Leader领导者处理 参与选举

Observer观察者 只能读,有写时也会转给Leader, 不参与Leader的选举

集群服务器个要为奇数2n+1,防止服务器down机,可以选举出新的leader

Leader先举机制

先投自己一票,票数相同时 票数>zxid>myid,其它zookeeper改为大者一票。集群中半数以上的票数才能成Leader

一.Maven
1.1 Maven基础回顾
目标

回顾Maven基础课程

学习路径

Maven好处
安装配置Maven
三种仓库
常见命令
坐标的书写规范
如何添加坐标
依赖范围
1.1.1 回顾
什么是Maven

Maven 是专门用于构建和管理Java相关项目的工具。

Maven好处

节省磁盘空间
可以一键构建
可以跨平台
应用在大型项目时可以提高开发效率(jar包管理,maven的工程分解,可分模块开发)
安装配置 maven

三种仓库

本地仓库,远程仓库(私服), 公司内部中央仓库

常见的命令

Compile
Test
Package(jar,war)
Install(安装到本地仓库)
Deploy(部署到远程仓库(私服))
Clean(target下生成的文件)
坐标的书写规范

groupId 公司或组织域名的倒序
artifactId 项目名或模块名
version 版本号
如何添加坐标

在本地仓库中搜索
互联网上搜,推荐网址 http://www.mvnrepository.com/
依赖范围

属性:

依赖范围 对于编译classpath有效 对于测试classpath有效 对于运行classpath有效 例子
Compile(默认) Y Y Y Spring-core
Test - Y - Junit
Provided Y Y - servlet-api
Runtime - Y Y JDBC驱动
System Y Y - 本地的,Maven仓库之外的类库
1.1.2 小结
Maven好处
安装配置Maven(熟练)
三种仓库(本地仓库(阿里镜像),中央仓库(远程仓库),私服(代理仓库))
常见命令(clean compile test package install deploy)
坐标的书写规范(groupId artifactId version)
如何添加坐标(http://www.mvnrepository.com/)
依赖范围
私服的配置(settings.xml配置私服 工作中大概率要配置的)
1.2 Maven中jar包冲突问题
目标

使用maven依赖导入jar包,出现相同名称但版本不一致的jar包

学习路径

演示jar包冲突问题:遵循第一声明者优先原则
解决jar包冲突问题,路径近者优先
解决jar包冲突问题,直接排除法
首先创建一个Mavenjar包工程(New Project-Maven)

1.2.1 第一声明优先原则
哪个jar包在靠上的位置,这个jar包就是先声明的,先声明的jar包下的依赖包,可以优先引入项目中。

在pom.xml中引入如下坐标,分别是spring中不同的版本。

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


4.0.0

com.test
Maven_Advanced
1.0-SNAPSHOT

jar

org.springframework spring-context 5.0.2.RELEASE
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-beans</artifactId>
  <version>4.2.4.RELEASE</version>
</dependency>
在控制面板的maven面板,点击查看依赖关系按钮,看到了包和包之间的依赖关系存在冲突,都使用了spring-core包,关系图如下:

看看他们依赖包的导入,发现导入的包却没有问题,包使用的都是5.0.2的版本。

我们把上面2个包的顺序调换后就变成了低版本的依赖导入。

1.2.2 路径近者优先原则
直接依赖:项目中直接导入的jar包就是项目的直接依赖包。
传递依赖(间接依赖):项目中没有直接导入的jar包,可以通过中直接依赖包传递到项目中去。
直接依赖比传递依赖路径近,最终进入项目的jar包会是路径近的直接依赖包。
修改jar包,直接引入依赖spring-core

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

org.springframework spring-beans 4.2.4.RELEASE
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>

<!--引入直接依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.2.8.RELEASE</version>
</dependency>
此时优先引入的是直接依赖的引用

1.2.3 直接排除法
问题:整合项目需要用到5的版本,引入4的版本,会不会出现异常(类没有找到,方法没有找到)
当我们需要排除某个jar包的依赖时,在配置exclusions标签的时候,内部可以不写版本号。pom.xml依赖如下:

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

org.springframework spring-beans 4.2.4.RELEASE org.springframework spring-core
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
解决方案

pom.xml添加exclusions

1
2
3
4
5
6


spring-core
org.springframework


1.2.4 小结
出现1个项目存在多个同种jar包的时候,需要我们进行解决maven的jar包冲突问题(异常:Class not found、Method not found等)

路径近者
节点路径相同时,使用第一声明优先(xml上下顺序有关)
无法通过依赖管理原则排除的,使用直接排除法
1.3 工程分层
之前项目开发:

工程分层后的开发:所有的service和dao的代码都在一起,1:增强程序的通用性,2:降低了代码的耦合性

目标

实现项目工程分解,掌握聚合和继承的作用

学习路径

聚合和继承
准备数据库环境
ssm_parent(父工程) pom 引入依赖
ssm_model(子工程) 实体类
ssm_dao(子工程) 数据库操作
ssm_service(子工程) 完业务操作
ssm_web(子工程) 收集请求参数,调用业务,返回结果给前端
测试(工程发布tomcat)
1.3.1 聚合(多模块)和继承
继承和聚合结构图:

特点1(继承)

ssm_parent父工程:存放项目的所有jar包。

ssm_web和ssm_service和ssm_dao有选择的继承jar包,并在工程中使用。这样可以消除jar包重复,并锁定版本

特点2(聚合)

ssm_web依赖于ssm_service,ssm_service依赖于ssm_dao,启动ssm_web,便可访问程序。

执行安装的时候,执行ssm_parent,就可以将所有的子工程全部进行安装。

理解继承和聚合总结

通常继承和聚合同时使用。

何为继承

继承是为了消除重复,如果将 dao、 service、 web 分开创建独立的工程则每个工程的 pom.xml 文件中的内容存在重复,比如:设置编译版本、锁定 spring 的版本的等,可以将这些重复的 配置提取出来在父工程的 pom.xml 中定义。

何为聚合

项目开发通常是分组分模块开发, 每个模块开发完成要运行整个工程需要将每个模块聚合在 一起运行,比如: dao、 service、 web 三个工程最终会打一个独立的 war 运行。

1.3.2 准备数据库环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SET FOREIGN_KEY_CHECKS=0;

CREATE DATABASE Maven;
use Maven;
DROP TABLE IF EXISTS items;
CREATE TABLE items (
id int(10) NOT NULL auto_increment,
name varchar(20) default NULL,
price float(10,0) default NULL,
pic varchar(40) default NULL,
createTime datetime default NULL,
detail varchar(200) default NULL,
PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

INSERT INTO items VALUES (‘1’, ‘Test01’, ‘1000’, null, ‘2018-03-13 09:29:30’, ‘带我走上人生巅峰’);
INSERT INTO items VALUES (‘2’, ‘Test02’, null, null, ‘2018-03-28 10:05:52’, ‘插入测试’);
INSERT INTO items VALUES (‘3’, ‘Test03’, ‘199’, null, ‘2018-03-07 10:08:04’, ‘插入测试’);
INSERT INTO items VALUES (‘7’, ‘Test04’, null, null, null, null);
INSERT INTO items VALUES (‘8’, ‘Test05’, null, null, null, null);
1.3.3 ssm_parent
创建ssm_parent的Maven Project
删除src文件夹
1.3.3.1 pom.xml
父工程:pom

详细信息
小结

pom
管理所有的jar包(并锁定版本)
父工程不需要开发的代码,src的文件夹可以删除
1.3.4 ssm_model
选择ssm_parent=>New->Moudle->Maven->创建子项目ssm_model(即实体类)

1.3.4.1 pom.xml
查看子工程pom.xml

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



ssm_parent
com.test
1.0-SNAPSHOT

4.0.0

<artifactId>ssm_model</artifactId>
<packaging>jar</packaging>
回去查看ssm_parent的pom.xml,自动聚合类ssm_model

1
2
3

ssm_model

1.3.4.2 创建Items.java
1
2
3
4
5
6
7
8
9
public class Items {
private Integer id;
private String name;
private Float price;
private String pic;
private Date createTime;
private String detail;
//此处省略getter/setter,toString
}
小结
jar
管理项目中所有的实体类
1.3.5 ssm_dao
操作路径
配置pom.xml
创建spring-mybatis.xml(spring整合mybatis的配置)
创建ItemsDao.java
创建ItemsDao.xml
1.3.5.1 pom.xml
详细信息
查看父工程pom.xml

1
2
3
4

ssm_model
ssm_dao

1.3.5.2 创建spring-mybatis.xml
操作路径

设置数据库连接池
配置SqlSessionFactoryBean
添加Dao层接口扫描,让Dao被spring管理
spring-mybatis.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








<!--2.配置sqlSessionFactoryBean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--配置数据源-->
    <property name="dataSource" ref="dataSource"/>
    <!--别名配置-->
    <property name="typeAliases" value="com.test.domain"/>
</bean>

<!--3.dao接口扫描-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.test.dao"/>
</bean>
1.3.5.3 创建ItemsDao.java 1 2 3 4 5 public interface ItemsDao { List findAll();
int save(Items items);

}
1.3.5.4 创建ItemsDao.xml
com/test/dao/ItemsDao.xml

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

INSERT INTO items(name,price,pic,creaTetime,detail) VALUES(#{name},#{price},#{pic},#{createTime},#{detail}) 1.3.5.5 dao测试 1 2 3 4 5 6 7 8 9 10 @Test public void testFindAll() { ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("classpath:spring-mybatis.xml"); ItemsDao itemsDao = classPathXmlApplicationContext.getBean(ItemsDao.class); List all = itemsDao.findAll();

for (Items items : all) {
System.out.println(items);
}
}
小结
jar
引入依赖 ssm_model 、mybatis的依赖…
spring-mybatis.xml(spring的配置文件)(spring整合mybatis的框架)
数据源
工厂
包扫描(dao)
接口ItemsDao.java
创建映射文件ItemsDao.xml (映射文件路径与包名相同, resources目录下的)
1.3.6 ssm_service
操作路径
pom.xml
创建spring-service.xml(spring的声明式事务处理)
创建ItemsService接口
创建ItemsServiceImpl实现类
1.3.6.1 pom.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
43
44
45
46
47
48
49



ssm_parent
com.test
1.0-SNAPSHOT

4.0.0

<artifactId>ssm_service</artifactId>


<!--jar包-->
<packaging>jar</packaging>

<!--依赖-->
<dependencies>
    <!--依赖dao-->
    <dependency>
        <groupId>com.test</groupId>
        <artifactId>ssm_dao</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <!-- spring -->
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
    </dependency>
</dependencies>
1.3.6.2 创建spring-service.xml 操作路径 创建一个事务管理器 配置事务的通知,及传播特性,对切入点方法的细化 AOP声明式事务配置(配置切入点,通知关联切入点) 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
<!--2.方式一:声明式事务配置-->
<tx:advice id="txAdvice" transaction-manager="txtManager">
    <!--配置传播特性属性-->
    <tx:attributes>
        <!--
            对应方法参与事务并且在事务下执行,事务隔离剂别使用默认隔离级别,发生异常需要事务回滚
        -->
      <tx:method name="add*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception" />
      <tx:method name="insert*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception" />
      <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception" />
      <tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception" />
      <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception" />
      <tx:method name="edit*" isolation="DEFAULT" propagation="REQUIRED" rollback-for="java.lang.Exception" />
        <!--
            只读操作
        -->
        <tx:method name="*" read-only="true" />
    </tx:attributes>
</tx:advice>
 <!--AOP声明式事务配置(配置切入点,通知关联切入点)-->
<aop:config>
    <!--切点指点-->
    <aop:pointcut id="tranpointcut" expression="execution(* com.test.service.*.*(..))" />
    <!--配置通知-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="tranpointcut" />
</aop:config>
<!--方式二:注解方式事务配置-->
<!--<tx:annotation-driven transaction-manager="txtManager"/>-->
<!--3.扫描service-->
<context:component-scan base-package="com.test.service"/>
<!--4.引入spring-mybatis.xml-->
<import resource="spring-mybatis.xml" />
1.3.6.3 创建ItemsService接口 1 2 3 4 5 public interface ItemsService { List findAll();
void save(Items items);

}
1.3.6.4 创建ItemsServiceImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Service
public class ItemsServiceImpl implements ItemsService {
@Autowired
private ItemsDao itemsDao;

@Override
public List<Items> findAll() {
    return itemsDao.findAll();
}

@Override
public void save(Items items) {
    itemsDao.save(items);
}

}
1.3.6.5 service测试
1
2
3
4
5
6
7
8
9
10
11
12
public class ServiceTest {
@Test
public void testService(){
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(“classpath:spring-service.xml”);
ItemsService bean = classPathXmlApplicationContext.getBean(ItemsService.class);
List all = bean.findAll();

for (Items items : all) {
  System.out.println(items);
}

}
}
小结
jar
引入依赖 ssm_dao 、spring相关的依赖
spring-service.xml(声明式事务处理)
扫描业务层
事务管理器
通知->切的方法
aop:config
要切的包 表达式
把advice与pointcut整合起来
注解式事务
接口ItemsService.java
实现类ItemsServiceImpl.java
1.3.7 ssm_web
操作路径
pom.xml
创建web.xml
创建springmvc.xml
创建ItemsController.java
创建页面items.jsp
1.3.7.1 pom.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
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



ssm_parent
com.test
1.0-SNAPSHOT

4.0.0

ssm_web

war

com.test ssm_service 1.0-SNAPSHOT
<!--导入springmvc-->
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
</dependency>

<!--servletAPI -->
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>javax.servlet.jsp</groupId>
  <artifactId>jsp-api</artifactId>
  <scope>provided</scope>
</dependency>

<!--jstl表达式 -->
<dependency>
  <groupId>jstl</groupId>
  <artifactId>jstl</artifactId>
</dependency>
org.apache.tomcat.maven tomcat7-maven-plugin 18081 / UTF-8 1.3.7.2 创建web.xml 操作路径

配置编码过滤器

springmvc前端核心控制器

1
2
3
4
5
6
7
8
9
10
11
12
.
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ ├── resources
│ │ └── webapp
│ │ └── WEB-INF
│ │ └── web.xml
│ └── test
│ └── java
└── ssm_web.iml
web.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




encodingFilter
org.springframework.web.filter.CharacterEncodingFilter


encoding
UTF-8



encodingFilter
/*

<!--2.springmvc核心控制器-->
<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-mvc.xml</param-value>
    </init-param>
    <!--启动优先级-->
    <load-on-startup>1</load-on-startup>
</servlet>
<!--3.指定映射拦截-->
<servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
1.3.7.3 创建springmvc.xml 操作路径

包扫描
视图解析器
springmvc注解驱动,自动配置mvc的处理器适配器和处理映射器
静态资源不过滤
mport导入spring.xml
springmvc.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23



<context:component-scan base-package=”com.test.controller”/>






mvc:annotation-driven/

mvc:default-servlet-handler/



1.3.7.4 创建ItemsController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

@Controller
@RequestMapping(“/list”)
public class ItemsController {

@Autowired
private ItemsService itemsService;

@GetMapping("/findAll")
public String findAll(Model model){
    List<Items> list = itemsService.findAll();
    for (Items items : list) {
        System.out.println(items);
    }
    model.addAttribute("list",list);
    return "list";
}

@PostMapping("/save")
public String  save(Items items){
    itemsService.save(items);
    return "redirect:/list/findAll";
}

}
1.3.7.5 创建页面items.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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<%@ taglib prefix=”c” uri=”http://java.sun.com/jsp/jstl/core“ %>
<%@ page contentType=”text/html;charset=UTF-8” language=”java” %>

Title
名称
价格
创建日期
详情

ID name price createTime detail
${item.id} ${item.name} ${item.price} ${item.pic} ${item.createTime} ${item.detail}
1.3.7.6 小结 war 引入依赖 ssm_service 、 springmvc 、 servlet_api等 springmvc.xml(springmvc的相关配置) web.xml(springmvc的核心控制器、post乱码过滤器) 类ItemsController.java 测试页面items.jsp 1.3.8 测试 操作路径 测试打包package、安装install(演示maven聚合) 发布到外部tomcat 使用maven的tomcat插件发布 1.3.8.1 方式一:tomcat插件发布(配置父工程) parent是有聚合的功能,不需要将ssm_parent,ssm_model,ssm_dao,ssm_service安装到本地仓库。

1.3.8.2 方式二:tomcat插件发布(配置web工程)
需要将ssm_parent,ssm_model,ssm_dao,ssm_service安装到本地仓库。

1.3.8.3 方式三:发布到外部tomcat
当使用外部tomcat进行开发的时候,不需要将ssm_parent,ssm_model,ssm_dao,ssm_service安装到本地仓库。

1.3.8.4 小结
1:拆分与聚合和继承

​ 拆分:解耦,代码最大程度的重复使用,维护方便

​ 聚合:靠父工程来聚合,单一工程是没法完成工作。

​ 继承:父工程引入依赖,子工程就不需要再引了

2: Maven的jar包冲突解决

路径近者优先
路径相同,第一声明优先
路径相同,使用强制排除exclusions
3:准备数据库环境

ssm_parent(父工程)聚合,引入依赖管理

ssm_model(子工程) 实体类

ssm_dao(子工程) mybatis, dataSource, sqlSessionFactorybean, MapperScannerConfigurer

注:映射文件的路径必须与包名路径一样

ssm_service(子工程)

扫service包,事务管理器,通知、切面。

ssm_web(子工程)

SpringMVC.xml 扫controller, 视图解析器,注解驱动,静态资源过滤
web.xml 编码过滤器,前端核心控制器(load-on-startup)
8:测试(工程发布tomcat)

推荐使用本地tomcat
1.4 补充
Maven项目,如果正在下载一个jar,但是突然断网,此时,就会产生一个m2e-lastUpdated.,别指望下次上网就会自动下载,必须手动删除该文件,然后再进行下载。

二.Git
学习目标:

能够概述git基本概念
能够概述git工作流程
能够使用git基本命令
能够使用idea操作git
2.1 Git版本控制系统目标
速度
简单的设计
对非线性开发模式的强力支持(允许上千个并行开发的分支)
完全分布式
有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)
2.2 Git与svn对比
SVN是集中式版本控制系统,版本库是集中放在中央服务器的

集中管理方式在一定程度上看到其他开发人员在干什么,而管理员也可以很轻松掌握每个人的开发权限。

但是相较于其优点而言,集中式版本控制工具缺点很明显:

服务器单点故障
容错性差
Git是分布式版本控制系统,那么它就没有中央服务器的

小结

git: 是一个代码(文档)版本的管理工具

svn:集中式版本控制工具(服务器完成对文件的版本控制)(一个仓库)

git:分布式版本控制工具(客户端、服务器都可以完成对文件的版本控制)(两个仓库,本地仓库、远程仓库)

2.3 git工作流程
一般工作流程如下:

从远程仓库中克隆 Git 资源作为本地仓库。

从本地仓库中checkout代码然后进行代码修改

在提交前先将代码提交到暂存区。

提交执行commit命令。提交到本地仓库。本地仓库中保存修改的各个历史版本。

在修改完成后,需要和团队成员共享代码时,可以将本地仓库的代码push到远程仓库。

小结

本地仓库——存入工程代码zip
工作区—— idea工作目录(代码明文)
暂存区
​ 命令:

远程仓库:(代码与他人共享的地方)

add(添加到暂存区);

commit(提交到本地仓库)

clone(远程仓库的代码克隆到本地仓库 所有的版本);

pull(将代码从远程仓库拉取到本地开发 只拉取某一版本的代码, 会尝试合并(远程与本地));

push(将代码从本地仓库推送到远程仓库)

clone与pull的区别

​ clone:第一次连接远程仓库 所有的版本 初始化

​ pull:第一次用clone命令,如果已经连接是哪个,其他都用pull命令拉取 只拉取某一版本的代码 会尝试合并(远程与本地)

2.4 使用git管理文件版本
目标

使用git在本地仓库完成版本控制,及相关命令的是使用

学习路径

创建版本库
添加/修改/删除文件/删除文件并保留副本
将java工程提交到版本库
忽略文件(提交版本库时,可忽略某些文件)
2.4.1 创建版本库
什么是版本库呢?版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的新增、修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。由于git是分布式版本管理工具,所以git在不需要联网的情况下也具有完整的版本管理能力。

创建本地仓库执行命令

1
$ git init
查看

1
2
$ ls -a | grep git
.git
创建服务器仓库

1
$ git –bare init
小结

git init 有工作空间,可以编码,一般用于客户端

git –bare init没有有工作空间,不可以编码,一般用于服务端

概念:

版本库

“.git”目录就是版本库,将来文件都需要保存到版本库中。

工作目录(工作区)

包含“.git”目录的目录,也就是.git目录的上一级目录就是工作目录。只有工作目录中的文件或者是文件夹才能保存到版本库中。

2.4.2 添加文件
2.4.2.1 添加文件过程
在git init的目录下创建一个文件

1
$ touch readme.txt
使用git add 命令即加入缓存区

1
$ git add readme.txt
使用git commit 添加注释并提交

1
$ git commit -m “Comment Message”
2.4.2.2 工作区和暂存区
Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。

什么是工作区(Working Directory)?

​ 工作区就是你在电脑里能看到的目录,比如我的reporstory/repo1文件夹就是一个工作区。

什么是版本库?

​ 在这个工作区目录中的“.git”隐藏文件夹是版本库。

​ Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。

可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。

2.4.2.3 修改文件
2.4.2.3.1 提交修改
修改文件

显示修改文件

1
$ git status
显示修改内容

1
$ git diff readme.txt
使用git add 命令即加入缓存区

1
$ git add readme.txt
使用git commit 添加注释并提交

1
$ git commit -m “New Comment Message”
2.4.2.3.2 查看修改历史
查看修改历史

1
2
3
4
5
6
7
8
9
10
$ git log | less | grep -v “swzxsyh”
commit 360abaca1a57aa9a9652c57fc3b06cf8a2c16742
Date: Tue Jan 29 17:01:39 2019 +0800

Learning Part of C Language

commit 9b1e91b72926493818ccbafd1447c2ae3fe8af55
Date: Tue Jan 29 16:57:04 2019 +0800

Cisco Packet Tracer ReadME
恢复版本

1
$ git checkout –
2.4.2.3.3 差异比较
1
$ git diff 新版本号 旧版本号
2.4.2.3.4 还原修改
1
$ git reset HEAD
注意:此操作会撤销所有未提交的修改,所以当使用还原操作是需要慎重

2.4.2.4 删除文件
需要删除无用的文件时可以使用git提供的删除功能直接将文件从版本库中删除。

1
2
3
4
5
6
7
$ rm readme.txt

$ git rm readme.txt

$ git commit -m “rm readme.txt”

$ git push resp
2.4.2.5 删除文件并保留副本
1
2
3
4
#对于一个文件:
git rm –cached mylogfile.log
#对于一个目录:
git rm –cached -r mydirectory
2.4.2.6 案例:将java工程-提交到版本库
创建HelloProjet项目,将工程添加到暂存区。

1
$ git add HelloProjet/
忽略文件或文件夹

并不是所有文件都需要保存到版本库中的例如“out”目录及目录下的文件就可以忽略。

需要创建.gitignore文件,并添加ignore内容

1
$ echo “out.idea” >> .gitignore
提交代码

1
$ git commit -m “HelloProjet”
2.4.2.7 忽略文件(.gitignore)语法规范
空行或是以 # 开头的行即注释行将被忽略。

可以在前面添加正斜杠 / 来避免递归,下面的例子中可以很明白的看出来与下一条的区别。

可以在后面添加正斜杠 / 来忽略文件夹,例如 build/ 即忽略build文件夹。

可以使用 ! 来否定忽略,即比如在前面用了 *.apk ,然后使用 !a.apk ,则这个a.apk不会被忽略。

*用来匹配零个或多个字符,如 *.[oa] 忽略所有以”.o”或”.a”结尾, *~ 忽略所有以 ~ 结尾的文件(这种文件通常被许多编辑器标记为临时文件); [] 用来匹配括号内的任一字符,如 [abc] ,也可以在括号内加连接符,如 [0-9] 匹配0至9的数; ? 用来匹配单个字符。

看了这么多,还是应该来个例子:

gitignore文件

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

忽略 .a 文件

*.a

不忽略 lib.a, 尽管已经在前面忽略了 .a 文件

!lib.a

仅在当前目录下忽略 TODO 文件, 但不包括子目录下的 subdir/TODO

/TODO

忽略 build/ 文件夹下的所有文件

build/

忽略 doc/notes.txt, 不包括 doc/server/arch.txt

doc/*.txt

忽略所有的 .pdf 文件,包括在 doc/ directory 下的

doc/**/*.pdf
2.4.2.8 小结
创建版本库

git init:初始化仓库(包含工作目录) –bare[创建的是纯仓库]

添加文件

git add:把文件添加进暂存区

git commit提交文件至本地仓库 添加提交日志

修改文件

删除文件

删除文件并保留副本

忽略文件(提交版本库时,可忽略某些文件。提交工程源码与jar和pom.xml,其它不提交)

将java工程提交到版本库【重点-不要提交忽略文件】

2.5 远程仓库
目标

使用git在远程仓库完成版本控制,及相关命令的使用,远程仓库可实现项目组人员之间的文件版本控制

学习路径

添加到远程仓库

(1)在github上创建仓库

(2)什么是ssh协议

(3)使用ssh协议同步到远程仓库

(4)使用https协议同步到远程仓库

从远程仓库上克隆

从远程仓库取代码

解决多人协作中版本冲突问题

搭建私有Git服务器(linux环境)

2.5.1 添加远程仓库
此时在本地已有一个Git仓库,想让其他人来协作开发,此时可以把本地仓库同步到远程仓库,同时还增加了本地仓库的一个备份。

2.5.1.1 在github上创建仓库
官网:https://github.com/

注册账号->登录->新建仓库->Start a project->->点击“create repository”按钮仓库即可

Github支持两种同步方式“https”和“ssh”。

使用https很简单基本不需要配置就可以使用,但是每次提交代码和下载代码时都需要输入用户名和密码

2.5.1.1.2 ssh协议版本
SSH 为 Secure Shell的缩写,利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。

使用ssh方式就需要客户端先生成一个密钥对,即一个公钥一个私钥。然后还需要把公钥放到githib的服务器上。

Client

1
2
3
4
5
6
7
8
9
10
[root@localhost ~]# ssh-keygen -t rsa <== 建立密钥对,-t代表类型,有RSA和DSA两种
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): #密钥文件默认存放位置,按Enter即可
Created directory ‘/root/.ssh’.
Enter passphrase (empty for no passphrase): #输入密钥锁码
Enter same passphrase again:
Your identification has been saved in /root/.ssh/id_rsa. #生成的私钥
Your public key has been saved in /root/.ssh/id_rsa.pub. #生成的公钥
The key fingerprint is:
SHA256:
ssh密钥配置

密钥生成后需要在github上配置密钥本地才可以顺利访问。

在key部分将id_rsa.pub文件内容添加进去,然后点击“Add SSH key”按钮完成配置。

使用ssh协议同步到远程仓库

1
2
$ git remote add origin git@github.com:itcast888/mytest.git
$ git push -u origin master
origin就是一个名字,它是在你clone一个托管在Github上代码库时,git为你默认创建的指向这个远程代码库的标签, origin指向的是repository,master只是这个repository中默认创建的第一个分支。

2.5.2 从远程仓库克隆
克隆远程仓库也就是从远程把仓库复制一份到本地,克隆后会创建一个新的本地仓库。选择一个任意部署仓库的目录,然后克隆远程仓库。

1
$ git clone git@github.com:sublun/mytest.git
2.5.3 从远程仓库取代码
1
2
3

  1. git fetch:相当于是从远程获取最新版本到本地,不会自动merge(合并代码)

  2. git pull:相当于是从远程获取最新版本并merge到本地,上述命令其实相当于git fetch 和 git merge
    在实际使用中,git fetch更安全一些,但是不常用!

因为在merge前,我们可以查看更新情况,然后再决定是否合并。

git pull更常用,因为即得代码又可以自动合并

2.5.4 解决版本冲突
原因

同时获取同一份代码,用户A先提交,用户B再提交,即出现版本冲突

解决

先拉取(pull)远程仓库的代码到本地。

编辑冲突

合并成一个新的文件

再次推送

避免冲突

优先提交代码
提交后通知全组成员pull
2.6 分支管理
目标

在本地仓库中使用分支,可在一个项目中使用多条路径,完成版本控制。

学习路径

分支的概念

分支管理

(1)创建分支(2)合并分支(3)解决冲突(4)删除分支

2.6.1 分支的概念
在我们每次的提交,Git都把它们串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即master分支。HEAD指针严格来说不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是当前分支。

只要有本地仓库就有master分支。

1
2
3
4
5
6
$ git checkout
A “Algorithm/\347\256\227\346\263\225\345\233\276\350\247\243/.idea/$CACHE_FILE$”
M C++ project/.vscode/launch.json
M C++ project/.vscode/tasks.json
M Java/MyFirstApp.class
Your branch is up to date with ‘origin/master’.
Master是条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支

每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长。

1
2
3
Head

A–>B–>C–>D–>E(Master)
创建新的分支,如dev,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,此时,对工作区的修改和提交就针对dev分支,提交后,dev指针往前移动一步,而master指针不变

1
2
$ git checkout -b dev
Switched to a new branch “dev”
1
2
3
4
5
6
7
Master

A–>B–>C–>D–>E

dev

Head
dev上的工作完成了,就可以把dev合并到master上,删除dev分支,变回一条线

1
2
3
4
5
6
7
8
9
10
#在切换分支之前,保持好一个干净的状态
$ git add xx/*
$ git commit -m “XXX”
#切换回 master 分支
$ git checkout master
Switched to branch ‘master’
#假设提交,且无冲突,合并分支
$ git merge dev
#使用带 -d 选项的 git branch 命令来删除分支
$ git branch -d dev
2.7 IntelliJ IDEA中使用git
目标

在idea使用git完成版本控制

git操作本地仓库(自己进行版本控制)

git操作远程仓库(多人进行版本控制)

学习路径

配置Git
创建工程集成Git
推送代码到远程仓库
从远程仓库克隆
从远程仓库拉取
使用分支
2.7.1 在Idea中配置git
安装好IntelliJ IDEA后,如果Git安装在默认路径下,那么idea会自动找到git的位置,如果更改了Git的安装位置则需要手动配置下Git的路径。

选择File→Settings打开设置窗口,找到Version Control下的git选项
选择git的安装目录后可以点击“Test”按钮测试是否正确配置。
使用idea操作github远程服务器的时候,在idea中配置用户名和密码,这样使用https协议访问github的时候,不需要输入用户名和密码了。
2.7.2 创建工程集成GIT
2.7.2.1创建maven工程
创建Maven工程打包方式为jar包,创建User类

创建本地仓库

在菜单中选择“vcs”→Import into Version Control→Create Git Repository

此时样式改变,查看本地工程目录,有绿色箭头。说明创建了本地仓库。

2.7.2.2添加暂存区
项目右键——>Git—>Add

2.7.2.3 提交本地仓库
项目右键——>Git—>Commit Directory

2.7.3 远程仓库操作
2.7.3.1 推送到远程仓库
在github上创建一个远程仓库 HelloGithub 然后将本地仓库推送到远程。

注意:这里使用https的协议连接

在工程上点击右键,选择git→Repository→Remotes,添加URL

在工程上点击右键,选择git→Repository→Push

2.7.3.2 从远程仓库克隆
方式一

选择VCS—>Checkout from Version Control—>GitHub

方式二

在idea的欢迎页上有“Checkout from version control”下拉框,选择git(推荐使用https形式的url)

2.7.3.3 修改文件push到远程仓库
修改User

先commit到本地

Git–>Commit Directory

再push到远程

Git–>Repository–>Push

2.7.3.4 从远程仓库拉取
Git–>Repository–>Pull

2.7.4 在Idea中使用分支
选择VCS—>Git—>Branches

点击“New Branch”,新建一个分支

分支操作

Checkout:为切换分支
Merge:为合并分支
Delete:删除分支
小结
在idea中配置git
将工程添加至git(操作本地仓库,将工程推送到远程仓库)
从远程仓库克隆(远程仓库)
从服务端拉取代码(远程仓库)
在idea中使用分支

一.linux基础
1.1 远程连接工具使用
在实际开发中,Linux服务器都在其他的地方,我们要通过windows客户端工具远程去连接Linux并操作 它,连接Linux的windows客户端工具有很多,企业中常用的有secureCRT、Putty、xshell、SSH Secure等。

1.2 Linux目录结构
root目录:超级管理员所在的目录,用~表示
home目录:普通用户所在的目录
usr目录:安装用户文件所在的目录
etc目录:Linux系统管理和配置文件所在的目录

1.3 文件夹(目录)操作命令
查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ls

List directory contents.

  • List files one per line:
    ls -1

  • List all files, including hidden files:
    ls -a

  • Long format list (permissions, ownership, size and modification date) of all files:
    ls -la

  • Long format list with size displayed using human readable units (KB, MB, GB):
    ls -lh

  • Long format list sorted by size (descending):
    ls -lS

  • Long format list of all files, sorted by modification date (oldest first):
    ls -ltr
    跳转

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

Change the current working directory.

  • Go to the given directory:
    cd path/to/directory

  • Go to home directory of current user:
    cd

  • Go up to the parent of the current directory:
    cd ..

  • Go to the previously chosen directory:
    cd -
    创建

1
2
3
4
5
6
7
8
9
mkdir

Creates a directory.

  • Create a directory in current directory or given path:
    mkdir directory

  • Create directories recursively (useful for creating nested dirs):
    mkdir -p path/to/directory
    搜索

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
find

Find files or directories under the given directory tree, recursively.

  • Find files by extension:
    find root_path -name ‘*.ext’

  • Find files by matching multiple patterns:
    find root_path -name ‘pattern_1‘ -or -name ‘pattern_2

  • Find directories matching a given name, in case-insensitive mode:
    find root_path -type d -iname ‘lib

  • Find files matching a path pattern:
    find root_path -path ‘/lib//*.ext’

  • Find files matching a given pattern, excluding specific paths:
    find root_path -name ‘.py’ -not -path ‘/site-packages/*’

  • Find files matching a given size range:
    find root_path -size +500k -size -10M

  • Run a command for each file (use {} within the command to access the filename):
    find root_path -name ‘*.ext’ -exec wc -l {} ;

  • Find files modified in the last 7 days, and delete them:
    find root_path -mtime -7 -delete
    修改

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

mv

Move or rename files and directories.

  • Move files in arbitrary locations:
    mv source target

  • Do not prompt for confirmation before overwriting existing files:
    mv -f source target

  • Prompt for confirmation before overwriting existing files, regardless of file permissions:
    mv -i source target

  • Do not overwrite existing files at the target:
    mv -n source target

  • Move files in verbose mode, showing files after they are moved:
    mv -v source target
    剪切

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

mv

Move or rename files and directories.

  • Move files in arbitrary locations:
    mv source target

  • Do not prompt for confirmation before overwriting existing files:
    mv -f source target

  • Prompt for confirmation before overwriting existing files, regardless of file permissions:
    mv -i source target

  • Do not overwrite existing files at the target:
    mv -n source target

  • Move files in verbose mode, showing files after they are moved:
    mv -v source target
    复制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cp

Copy files and directories.

  • Copy a file to another location:
    cp path/to/file.ext path/to/copy.ext

  • Copy a file into another directory, keeping the filename:
    cp path/to/file.ext path/to/target_parent_directory

  • Recursively copy a directory’s contents to another location (if the destination exists, the directory is copied inside it):
    cp -r path/to/directory path/to/copy

  • Copy a directory recursively, in verbose mode (shows files as they are copied):
    cp -vr path/to/directory path/to/copy

  • Copy text files to another location, in interactive mode (prompts user before overwriting):
    cp -i *.txt path/to/target_directory

  • Dereference symbolic links before copying:
    cp -L link path/to/copy
    删除

1
2
3
4
5
6
7
8
9
rmdir

Removes a directory.

  • Remove directory, provided it is empty. Use rm to remove not empty directories:
    rmdir path/to/directory

  • Remove directories recursively (useful for nested dirs):
    rmdir -p path/to/directory
    1.4 文件操作命令
    创建

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

Change a file access and modification times (atime, mtime).

  • Create a new empty file(s) or change the times for existing file(s) to current time:
    touch filename

  • Set the times on a file to a specific date and time:
    touch -t YYYYMMDDHHMM.SS filename

  • Use the times from a file to set the times on a second file:
    touch -r filename filename2
    查看

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
cat

Print and concatenate files.

  • Print the contents of a file to the standard output:
    cat file

  • Concatenate several files into the target file:
    cat file1 file2 > target_file

  • Append several files into the target file:
    cat file1 file2 >> target_file

  • Number all output lines:
    cat -n file

  • Display non-printable and whitespace characters (with M- prefix if non-ASCII):
    cat -v -t -e file

tail

Display the last part of a file.

  • Show last ‘num’ lines in file:
    tail -n num file

  • Show all file since line ‘num’:
    tail -n +num file

  • Show last ‘num’ bytes in file:
    tail -c num file

  • Keep reading file until Ctrl + C:
    tail -f file

  • Keep reading file until Ctrl + C, even if the file is rotated:
    tail -F file

head

Output the first part of files.

  • Output the first few lines of a file:
    head -n count_of_lines filename

  • Output the first few bytes of a file:
    head -c number_in_bytes filename
    删除

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

Remove files or directories.

  • Remove files from arbitrary locations:
    rm path/to/file path/to/another/file

  • Recursively remove a directory and all its subdirectories:
    rm -r path/to/directory

  • Forcibly remove a directory, without prompting for confirmation or showing error messages:
    rm -rf path/to/directory

  • Interactively remove multiple files, with a prompt before every removal:
    rm -i file(s)

  • Remove files in verbose mode, printing a message for each removed file:
    rm -v path/to/directory/*
    编辑

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
vim

Vim (Vi IMproved), a command-line text editor, provides several modes for different kinds of text manipulation.
Pressing i enters edit mode. <Esc> goes back to normal mode, which doesn’t allow regular text insertion.
More information: https://www.vim.org.

  • Open a file:
    vim path/to/file

  • View Vim’s help manual:
    :help

  • Save a file:
    :write

  • Quit without saving:
    :quit!

  • Open a file at a specified line number:
    vim +line_number path/to/file

  • Undo the last operation:
    u

  • Search for a pattern in the file (press n/N to go to next/previous match):
    /search_pattern

  • Perform a regex substitution in the whole file:
    :%s/pattern/replacement/g
    1.5 文件压缩命令
    将文件打成压缩包

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
tar

Archiving utility.
Often combined with a compression method, such as gzip or bzip.
More information: https://www.gnu.org/software/tar.

  • Create an archive from files:
    tar cf target.tar file1 file2 file3

  • Create a gzipped archive:
    tar czf target.tar.gz file1 file2 file3

  • Create a gzipped archive from a directory using relative paths:
    tar czf target.tar.gz -C path/to/directory .

  • Extract a (compressed) archive into the current directory:
    tar xf source.tar[.gz|.bz2|.xz]

  • Extract an archive into a target directory:
    tar xf source.tar -C directory

  • Create a compressed archive, using archive suffix to determine the compression program:
    tar caf target.tar.xz file1 file2 file3

  • List the contents of a tar file:
    tar tvf source.tar

  • Extract files matching a pattern:
    tar xf source.tar –wildcards “*.html”

  • Extract a specific file without preserving the folder structure:
    tar xf source.tar source.tar/path/to/extract –strip-components=depth_to_strip
    解压缩包获得文件

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
tar

Archiving utility.
Often combined with a compression method, such as gzip or bzip.
More information: https://www.gnu.org/software/tar.

  • Create an archive from files:
    tar cf target.tar file1 file2 file3

  • Create a gzipped archive:
    tar czf target.tar.gz file1 file2 file3

  • Create a gzipped archive from a directory using relative paths:
    tar czf target.tar.gz -C path/to/directory .

  • Extract a (compressed) archive into the current directory:
    tar xf source.tar[.gz|.bz2|.xz]

  • Extract an archive into a target directory:
    tar xf source.tar -C directory

  • Create a compressed archive, using archive suffix to determine the compression program:
    tar caf target.tar.xz file1 file2 file3

  • List the contents of a tar file:
    tar tvf source.tar

  • Extract files matching a pattern:
    tar xf source.tar –wildcards “*.html”

  • Extract a specific file without preserving the folder structure:
    tar xf source.tar source.tar/path/to/extract –strip-components=depth_to_strip
    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
    chmod

Change the access permissions of a file or directory.

  • Give the [u]ser who owns a file the right to e[x]ecute it:
    chmod u+x file

  • Give the [u]ser rights to [r]ead and [w]rite to a file/directory:
    chmod u+rw file_or_directory

  • Remove e[x]ecutable rights from the [g]roup:
    chmod g-x file

  • Give [a]ll users rights to [r]ead and e[x]ecute:
    chmod a+rx file

  • Give [o]thers (not in the file owner’s group) the same rights as the [g]roup:
    chmod o=g file

  • Remove all rights from [o]thers:
    chmod o= file

  • Change permissions recursively giving [g]roup and [o]thers the abililty to [w]rite:
    chmod -R g+w,o+w directory
    1.7 其他命令
    显示工作目录

1
2
3
4
5
6
7
8
9
pwd

Print name of current/working directory.

  • Print the current directory:
    pwd

  • Print the current directory, and resolve all symlinks (i.e. show the “physical” path):
    pwd -P
    查看进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ps

Information about running processes.

  • List all running processes:
    ps aux

  • List all running processes including the full command string:
    ps auxww

  • Search for a process that matches a string:
    ps aux | grep string

  • List all processes of the current user in extra full format:
    ps –user $(id -u) -F

  • List all processes of the current user as a tree:
    ps –user $(id -u) f

  • Get the parent pid of a process:
    ps -o ppid= -p pid
    杀死进程

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
kill

Sends a signal to a process, usually related to stopping the process.
All signals except for SIGKILL and SIGSTOP can be intercepted by the process to perform a clean exit.

  • Terminate a program using the default SIGTERM (terminate) signal:
    kill process_id

  • List available signal names (to be used without the SIG prefix):
    kill -l

  • Terminate a background job:
    kill %job_id

  • Terminate a program using the SIGHUP (hang up) signal. Many daemons will reload instead of terminating:
    kill -1|HUP process_id

  • Terminate a program using the SIGINT (interrupt) signal. This is typically initiated by the user pressing Ctrl + C:
    kill -2|INT process_id

  • Signal the operating system to immediately terminate a program (which gets no chance to capture the signal):
    kill -9|KILL process_id

  • Signal the operating system to pause a program until a SIGCONT (“continue”) signal is received:
    kill -17|STOP process_id

  • Send a SIGUSR1 signal to all processes with the given GID (group id):
    kill -SIGUSR1 -group_id
    搜索

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
grep

Matches patterns in input text.
Supports simple patterns and regular expressions.

  • Search for an exact string:
    grep search_string path/to/file

  • Search in case-insensitive mode:
    grep -i search_string path/to/file

  • Search recursively (ignoring non-text files) in current directory for an exact string:
    grep -RI search_string .

  • Use extended regular expressions (supporting ?, +, {}, () and |):
    grep -E ^regex$ path/to/file

  • Print 3 lines of [C]ontext around, [B]efore, or [A]fter each match:
    grep -C|B|A 3 search_string path/to/file

  • Print file name with the corresponding line number for each match:
    grep -Hn search_string path/to/file

  • Use the standard input instead of a file:
    cat path/to/file | grep search_string

  • Invert match for excluding specific strings:
    grep -v search_string
    管道

1
2
3
4
| ##管道符
管道只允许正确输出通过
tee ####复制一份输出
2>&1 | ####转换错误输出为正确再通过管道
关机

1
poweroff
重启

1
2
3
4
5
6
7
8
9
reboot

Reboot the system.

  • Reboot immediately:
    sudo reboot

  • Reboot immediately without gracefully shutting down:
    sudo reboot -q
    二.Linux网络
    2.1 网络服务
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    ifconfig

Network Interface Configurator.

  • View network settings of an ethernet adapter:
    ifconfig eth0

  • Display details of all interfaces, including disabled interfaces:
    ifconfig -a

  • Disable eth0 interface:
    ifconfig eth0 down

  • Enable eth0 interface:
    ifconfig eth0 up

  • Assign IP address to eth0 interface:
    ifconfig eth0 ip_address

ip addr :CentOS7版本
修改网卡ip
vim /etc/sysconfig/network-scripts/ifcfg-ens33
概要信息
DEVICE=ens33 网卡名称
TYPE=Ethernet 网卡类型 以太网
ONBOOT=yes 是否开机就使用此网卡
BOOTPROTO=dhcp 启动网卡时指定获取IP地址的方式
IPADDR= ip地址
GATEWAY= 网关
NETMASK=255.255.255.0 子网掩码
DNS1= DNS服务器
重启网卡服务
systemctl status network 查看指定服务的状态
systemctl stop network 停止指定服务
systemctl start network 启动指定服务
systemctl restart network 重启指定服务
2.2 防火墙服务
2.2.1 防火墙设置
防火墙设置
开启防火墙 systemctl start firewalld
重启防火墙 systemctl restart firewalld
关闭防火墙 systemctl stop firewalld
设置开机启动 systemctl enable firewalld
停止并关闭开机启动 systemctl disable firewalld
查看防火墙状态 systemctl status firewalld 或者 firewall-cmd –state
查看防火墙开机时是否启动 systemctl list-unit-files | grep firewalld
2.2.2 端口设置
端口设置
添加 firewall-cmd –zone=public –add-port=80/tcp –permanent
更新防火墙规则 firewall-cmd –reload
查看 firewall-cmd –zone=public –query-port=80/tcp
firewall-cmd –zone=public –list-ports
删除 firewall-cmd –zone=public –remove-port=80/tcp –permanent
常用端口 8080 tomcat
80 http协议
443 https协议
22 ssh远程连接
3306 mysql
6379 redis
三.Nginx
3.1 介绍
Nginx是一款轻量级的 Web 服务器,由俄罗斯的程序设计师伊戈尔·西索夫所开发。 Nginx性能非常优秀, 官方测试能够支撑5万并发链接,并且 cpu、内存等资源消耗却非常低,运行非常稳定。

Nginx的功能有很多,我们主要使用它来做静态资源服务器、负载均衡服务器和反向代理服务器。

3.2 应用场景
3.2.1 静态资源服务器
部署网站的静态资源(html、css、js),可与Tomcat等实现动静分离

3.2.2 反向代理服务器
代理

给某个对象提供一个代理对象,并由代理对象控制原对象的引用

正向代理

对客户端进行代理(例如VPN)

反向代理

对服务端进行代理

反向代理,就是对服务端进行代理,作为客户端,只需要将请求发送到反向代理服务器,由反向代理服务 器去选择目标服务器获取数据后,再响应给客户端,此时反向代理服务器和目标服务器对外就是一个服 务器,暴露的是代理服务器地址,隐藏了真实服务器IP地址。

3.2.3 负载均衡服务器
负载均衡 Load Balance 意思就是将一份负载分摊到多个操作单元上进行执行

3.3 配置文件介绍(nginx-1.13.9/conf/nginx.conf)
1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 80; #port
server_name localhost; #Server

#charset koi8-r;

#access_log  logs/host.access.log  main;

location / {
    root   html; #Static resource directory
    index  index.html index.htm; #Default visit page
}

3.5 nginx与tomcat区别
存放的文件(资源)形式
Nginx是http服务器,只能解析静态文件

Tomcat是web中间件(本质上是一个servlet),能解析jsp和静态文件

用途

nginx可以作为反向代理服务器,负责均衡服务器,静态资源存放服务器

tomcat能作为jsp容器使用,静态资源存放服务器

性能

nginx支持5W+并发,tomcat的并发只能在200-400之间

总结

linux基础

Linux基本命令

目录结构

  • root

​ - 超级管理员所在的目录

  • home

​ - 普通用户所在的目录

  • usr

​ - 安装用户文件所在的目录

  • etc

​ - Linux系统管理和配置文件所在的目录

目录操作

  • 查看

​ - ll

​ - ls

  • 切换

​ - cd

​ - 绝对路径

​ - 相对路径

  • 创建

​ - mkdir -p

  • 修改/剪切

​ - mv

  • 复制

​ - cp -r

  • 删除

​ - rmdir -p

文件操作

  • 创建

​ - touch

  • 查看

​ - cat/more/less/head/tail

  • 删除

​ - rm -rf

  • 编辑

​ - vim

​ - 插入模式

​ - i

​ - 命令行模式

​ - esc

​ - 底行模式

​ - :

​ - wq!

​ - q!

压缩命令

  • 打包

​ - tar -zcvf

​ - xxxx.tar.gz

  • 解压缩

​ - tar -zxvf

​ - xxxx.tar.gz

文件权限

  • chmod -R 777

其他命令

  • 查看进程

​ - ps -ef

  • 杀死进程

​ - kill -9 PID

  • 文本搜索

​ - grep -in

  • 管道

​ - 一个命令的输出作为另一个命令的输入

Linux网络

网络

  • 查看ip

​ - ifconfig

​ - ip addr

防火墙

  • 防火墙设置

​ - 关闭

​ - systemctl stop firewalld

​ - 关闭开机自启动

​ - systemctl disable firewalld

  • 端口设置

​ - 放行端口

​ - firewall-cmd –zone=public –add-port=80/tcp –permanent

​ - 更新规则

​ - firewall-cmd –reload

Nginx

由战斗民族开发的一款高性能的 http 服务器/反向代理服务器

  • http服务器

  • 反向代理(负载均衡)

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