- 第 5 章 SpringBoot 與 Docker
- 5.1 Docker 簡介
- 5.2 核心概念
- 5.3 安裝Docker
- 安裝linux虛擬機
- 在linux虛擬機上安裝docker
- 5.4 Docker常用命令&操作
- 鏡像操作
- 容器操作
- 安裝 MySQL 示例
- 第 6 章 SpringBoot 與數(shù)據(jù)訪問
- 6.1 數(shù)據(jù)源初始化與 JDBC
- 配置 MySQL
- 數(shù)據(jù)源自動配置原理
- 數(shù)據(jù)表自動初始化
- 使用 JdbcTemplate 查詢數(shù)據(jù)
- 數(shù)據(jù)庫自動初始化原理
- 6.2 使用外部數(shù)據(jù)源
- 6.3 自定義數(shù)據(jù)源原理
- 6.4 配置 Druid 數(shù)據(jù)源
- 6.5 整合 MyBatis
- 注解版
- Mybatis 常見配置
- xml 版
- 6.6 整合 SpringData JPA
- Spring Data 簡介
- 整合 SpringData JPA
- 6.1 數(shù)據(jù)源初始化與 JDBC
- 第 7 章 SpringBoot 啟動配置原理
- 7.1 啟動流程
- 創(chuàng)建SpringApplication對象
- 運行run方法
- 7.2 事件監(jiān)聽機制
- 7.1 啟動流程
- 第 8 章 SpringBoot 自定義 starter
- 8.1 starter 原理
- 8.2 自定義 starter
- SpringBoot 與開發(fā)熱部署
- 進階學(xué)習(xí)
- 待補充
- 推薦閱讀
- 參考文檔
第 5 章 SpringBoot 與 Docker
5.1 Docker 簡介
Docker是一個開源的應(yīng)用容器引擎,輕量級容器技術(shù)。Docker支持將軟件編譯成一個鏡像,然后在鏡像中各種軟件做好配置,將鏡像發(fā)布出去,其他使用者可以直接使用這個鏡像;運行中的這個鏡像稱為容器,容器啟動是非常快速的。
參考 我的 Docker 入門筆記
[圖片上傳失敗...(image-82059e-1606212213489)]
5.2 核心概念
- 主機(Host):安裝了Docker程序的機器(Docker直接安裝在操作系統(tǒng)之上)
- 客戶端(Client):連接docker主機進行操作
- 倉庫(Registry):用來保存各種打包好的軟件鏡像
- 鏡像(Images):軟件打包好的鏡像;放在docker倉庫中
- 容器(Container):鏡像啟動后的實例稱為一個容器,容器是獨立運行的一個或一組應(yīng)用
使用Docker的步驟:
- 安裝Docker
- 去Docker倉庫找到這個軟件對應(yīng)的鏡像
- 使用Docker運行這個鏡像,這個鏡像就會生成一個Docker容器
- 對容器的啟動停止就是對軟件的啟動停止
5.3 安裝Docker
安裝linux虛擬機
VMWare、VirtualBox(安裝)
導(dǎo)入虛擬機文件centos7-atguigu.ova
雙擊啟動linux虛擬機;使用 root/ 123456登陸
使用客戶端連接linux服務(wù)器進行命令操作
設(shè)置虛擬機網(wǎng)絡(luò);橋接網(wǎng)絡(luò)===選好網(wǎng)卡====接入網(wǎng)線;
-
設(shè)置好網(wǎng)絡(luò)以后使用命令重啟虛擬機的網(wǎng)絡(luò)
service network restart
-
查看linux的ip地址
ip addr
使用客戶端連接linux
在linux虛擬機上安裝docker
參考 Docker 安裝與啟動
步驟:
# 1、檢查內(nèi)核版本,必須是3.10及以上
uname -r
# 2、安裝docker
yum install docker
# 3、輸入y確認安裝
# 4、啟動docker
[root@localhost ~]# systemctl start docker
[root@localhost ~]# docker -v
Docker version 1.12.6, build 3e8e77d/1.12.6
# 5、開機啟動docker
[root@localhost ~]# systemctl enable docker
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.
# 6、停止docker
systemctl stop docker
5.4 Docker常用命令&操作
1. 鏡像操作
操作 | 命令 | 說明 |
---|---|---|
檢索 | docker search 關(guān)鍵字 eg:docker search redis | 我們經(jīng)常去docker hub上檢索鏡像的詳細信息,如鏡像的TAG。 |
拉取 | docker pull 鏡像名:tag | :tag是可選的,tag表示標簽,多為軟件的版本,默認是latest |
列表 | docker images | 查看所有本地鏡像 |
刪除 | docker rmi image-id | 刪除指定的本地鏡像 |
2. 容器操作
軟件鏡像(QQ安裝程序)----運行鏡像----產(chǎn)生一個容器(正在運行的軟件,運行的QQ);
步驟:
# 1、搜索鏡像
[root@localhost ~]# docker search tomcat
# 2、拉取鏡像
[root@localhost ~]# docker pull tomcat
# 3、根據(jù)鏡像啟動容器
docker run --name mytomcat -d tomcat:latest
# 4、查看運行中的容器
docker ps
# 5、 停止運行中的容器
docker stop 容器的id
# 6、查看所有的容器
docker ps -a
# 7、啟動容器
docker start 容器id
# 8、刪除一個容器
docker rm 容器id
# 9、啟動一個做了端口映射的tomcat
[root@localhost ~]# docker run -d -p 8888:8080 tomcat
-d:后臺運行
-p: 將主機的端口映射到容器的一個端口 主機端口:容器內(nèi)部的端口
# 10、為了演示簡單關(guān)閉了linux的防火墻
service firewalld status ;查看防火墻狀態(tài)
service firewalld stop:關(guān)閉防火墻
# 11、查看容器的日志
docker logs container-name/container-id
更多命令參考 Docker 官方文檔
3. 安裝 MySQL 示例
拉取 MySQL鏡像:
docker pull mysql:5.6
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 5.6 8de95e6026c3 4 weeks ago 302MB
啟動 MySQL:
啟動 MySQL的命令比較特殊,以后遇到不熟悉的鏡像可以查看官方文檔,如 啟動MySQL 可以查看 MySQL 鏡像文檔,可以看到啟動命令中需要設(shè)置 MySQL 的初始密碼:
Start a mysql server instance
Starting a MySQL instance is simple:
$ docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
[root@localhost ~]# docker run --name mysql01 -d mysql:5.6
42f09819908bb72dd99ae19e792e0a5d03c48638421fa64cce5f8ba0f40f5846
# 查看容器,發(fā)現(xiàn)mysql并沒有啟動成功
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
42f09819908b mysql "docker-entrypoint.sh" 34 seconds ago Exited (1) 33 seconds ago mysql01
538bde63e500 tomcat "catalina.sh run" About an hour ago Exited (143) About an hour ago compassionate_
goldstine
c4f1ac60b3fc tomcat "catalina.sh run" About an hour ago Exited (143) About an hour ago lonely_fermi
81ec743a5271 tomcat "catalina.sh run" About an hour ago Exited (143) About an hour ago sick_ramanujan
# 查看錯誤日志
[root@localhost ~]# docker logs 42f09819908b
error: database is uninitialized and password option is not specified
You need to specify one of MYSQL_ROOT_PASSWORD, MYSQL_ALLOW_EMPTY_PASSWORD and MYSQL_RANDOM_ROOT_PASSWORD;這個三個參數(shù)必須指定一個
# 正確的啟動,設(shè)置MySQL密碼
[root@localhost ~]# docker run --name mysql01 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.6
b874c56bec49fb43024b3805ab51e9097da779f2f572c22c695305dedd684c5f
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b874c56bec49 mysql "docker-entrypoint.sh" 4 seconds ago Up 3 seconds 3306/tcp mysql01
# 做端口映射
[root@localhost ~]# docker run -p 3306:3306 --name mysql02 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.6
ad10e4bc5c6a0f61cbad43898de71d366117d120e39db651844c0e73863b9434
# 可以看到容器的端口映射 0.0.0.0:3306->3306/tcp
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ad10e4bc5c6a mysql "docker-entrypoint.sh" 4 seconds ago Up 2 seconds 0.0.0.0:3306->3306/tcp mysql02
操作 MySQL 命令行:
啟動 MySQL 成功后,可以進入 MySQL 命令行操作 MySQL
# 啟動MySQL
> docker run --name mysql02 -p 3316:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.6
6a356cfef74fd3c0c81b298bb04913ed1faa2c676cd30b4c57efa220d691d6b2
# 查看mysql容器信息
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6a356cfef74f mysql:5.6 "docker-entrypoint.s…" 5 seconds ago Up 4 seconds 0.0.0.0:3316->3306/tcp mysql02
# 進入mysql命令行,可以看到Mysql的版本號等信息
> docker exec -it mysql02 bash
root@6a356cfef74f:/# mysql -uroot -p123456
Warning: Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 2
Server version: 5.6.48 MySQL Community Server (GPL)
Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
# 顯示所有數(shù)據(jù)庫
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)
安裝完成后,可以使用 Navicat 連接 Docker 中的 MySQL,連接端口為容器映射到主機的端口,但是發(fā)生錯誤:
Client does not support authentication protocol
經(jīng)過查找資料,發(fā)現(xiàn)是 Navicat 不支持 MySQL 8.0 的原因,于是我關(guān)閉了 MySQL:8.0 容器,重新拉取啟動了 MySQL:5.6,使用 Navicat 連接成功。
高級操作:
# 把主機的/conf/mysql文件夾掛載到 mysqldocker容器的/etc/mysql/conf.d文件夾里面
# 改mysql的配置文件就只需要把mysql配置文件放在自定義的文件夾下(/conf/mysql)
docker run --name mysql03 -v /conf/mysql:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
# 指定mysql的一些配置參數(shù)
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
第 6 章 SpringBoot 與數(shù)據(jù)訪問
對于數(shù)據(jù)訪問層,無論是 SQL 還是 NOSQL,Spring Boot 默認采用整合 Spring Data 的方式進行統(tǒng)一處理,添加大量自動配置,屏蔽了很多設(shè)置。引入各種 xxxTemplate,xxxRepository 來簡化我們對數(shù)據(jù)訪問層的操作。對我們來說只需要進行簡單的設(shè)置即可。我們將在數(shù)據(jù)訪問章節(jié)測試使用SQL相關(guān)、NOSQL在緩存、消息、檢索等章節(jié)測試。
– JDBC
– MyBatis
– JPA
6.1 數(shù)據(jù)源初始化與 JDBC
1. 配置 MySQL
- 引入 Mysql 驅(qū)動 和 jdbc-starter:
默認MySQL 驅(qū)動是 8.0
<!-- jdbc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- mysql 驅(qū)動,這里默認是8.0.20版本,測試兼容 MySQL5.6 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
Mysql驅(qū)動scope設(shè)置為runtime?
- 在
application.yml
中配置數(shù)據(jù)源,新版驅(qū)動變?yōu)榱?com.mysql.cj.jdbc.Driver
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3316/sp-jdbc
driver-class-name: com.mysql.cj.jdbc.Driver
- 單元測試 JDBC 配置
@Autowired
private DataSource dataSource;
@Test
void testJdbc() throws SQLException {
System.out.println("數(shù)據(jù)源:" + dataSource.getClass());
Connection connection = dataSource.getConnection();
System.out.println("Connection: " + connection);
}
經(jīng)過測試,說明配置 MySQL 成功,SpringBoot 2.x 默認使用 Hikari 數(shù)據(jù)源
數(shù)據(jù)源:class com.zaxxer.hikari.HikariDataSource
Connection: HikariProxyConnection@356519935 wrapping com.mysql.cj.jdbc.ConnectionImpl@18d910b3
什么是數(shù)據(jù)源?
數(shù)據(jù)源是對數(shù)據(jù)庫操作的抽象,封裝了目標源的位置信息,驗證信息和建立與關(guān)閉連接的操作。不同數(shù)據(jù)庫可以實現(xiàn)接口提供不同策略。常見數(shù)據(jù)源包括:DriverManagerDataSource(不提供連接池),C3P0,Dbcp2,Hikari等
數(shù)據(jù)源更多的屬性配置參考:DataSourceProperties
- @ConfigurationProperties 從配置文件根據(jù)
prefix
讀取屬性綁定
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties {
private String username;
private String password;
private String url;
private String driverClassName;
private Class<? extends DataSource> type;
}
2. 數(shù)據(jù)源自動配置原理
數(shù)據(jù)源自動配置原理: DataSourceConfiguration
- SpringBoot 2.x 默認數(shù)據(jù)源為 hikari
- 可以使用
spring.datasource.type
修改數(shù)據(jù)源類型 - @Configuration 底層是 @Component,標注這個類會被掃描
- @ConfigurationProperties 從配置文件根據(jù)
prefix
讀取屬性綁定 - @Bean 將方法返回的對象加入到 Spring 容器,該方法只會被調(diào)用一次
- @ConditionalOnProperty 表示配置中
name
=havingValue
時則生效,matchIfMissing 表示沒有這項配置時也生效
DataSourceConfiguration:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
static class Hikari {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
}
3. 數(shù)據(jù)表自動初始化
- 創(chuàng)建建表 SQL
schema-*.sql
,插入數(shù)據(jù) SQLdata-*.sql
,放在 /resource 目錄下 - 配置中加入
spring.datasource.initialization-mode=always
表示啟動自動建表的操作 (SpringBoot 2.x) - 啟動項目就可以自動運行建表,插入數(shù)據(jù)了
注意:每次項目啟動都會執(zhí)行 SQL,即重新創(chuàng)建表和插入數(shù)據(jù)
默認建表 SQL名稱為schema-*.sql
,插入數(shù)據(jù) SQL 名稱為:data-*.sql
。也可以在配置application.yml 中指定建表 SQL 文件:spring.datasource.schema: - classpath:department.sql
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3316/sp-jdbc
driver-class-name: com.mysql.cj.jdbc.Driver
# 2.x 開啟自動初始化數(shù)據(jù)庫
initialization-mode: always
# 指定自動執(zhí)行的建表SQL,List需要用 - 表示
schema:
- classpath:department.sql # - 后面有空格,classpath:后無空格
- classpath:employee.sql
data:
- classpath:init-dept.sql # 插入數(shù)據(jù)SQL
4. 使用 JdbcTemplate 查詢數(shù)據(jù)
JdbcTemplateConfiguration 會創(chuàng)建 JdbcTemplate Bean 并加入到容器,使用 @Autowired 自動裝配即可查詢數(shù)據(jù)
@RestController
public class HelloController {
// SpringBoot自動配置了 JdbcTemplate組件,并加入到容器
@Autowired
JdbcTemplate jdbcTemplate;
@GetMapping("/query")
public Map<String, Object> getDept() {
List<Map<String, Object>> depts = jdbcTemplate.queryForList("SELECT * FROM department");
return depts.get(0);
}
}
5. 數(shù)據(jù)庫自動初始化原理
數(shù)據(jù)庫自動初始化原理: DataSourceInitializerInvoker,DataSourceInitializer
- 數(shù)據(jù)源初始化完成(被創(chuàng)建)時,幫我們運行 schema-.sql 建表
- 監(jiān)聽建表完成事件,完成后執(zhí)行 data-.sql 插入數(shù)據(jù)
DataSourceInitializerInvoker:
class DataSourceInitializerInvoker implements ApplicationListener<DataSourceSchemaCreatedEvent>, InitializingBean {
// 數(shù)據(jù)源 DataSource Bean初始化完成后調(diào)用
@Override
public void afterPropertiesSet() {
DataSourceInitializer initializer = getDataSourceInitializer();
if (initializer != null) {
// 獲取建表 schema-*.sql 并執(zhí)行,見下文
boolean schemaCreated = this.dataSourceInitializer.createSchema();
if (schemaCreated) {
// 表已經(jīng)創(chuàng)建完成,觸發(fā)DataSourceSchemaCreatedEvent事件
initialize(initializer);
}
}
}
// 監(jiān)聽 DataSourceSchemaCreatedEvent事件,執(zhí)行插入數(shù)據(jù)SQL
@Override
public void onApplicationEvent(DataSourceSchemaCreatedEvent event) {
DataSourceInitializer initializer = getDataSourceInitializer();
if (!this.initialized && initializer != null) {
// 獲取data-*.sql 并執(zhí)行,見下文
initializer.initSchema();
this.initialized = true;
}
}
上面執(zhí)行建表SQL 與 插入數(shù)據(jù) SQL 方法內(nèi)容如下:
DataSourceInitializer:
// 執(zhí)行建表SQL schema-*.sql
boolean createSchema() {
// 獲取需要執(zhí)行的建表schema-*.sql
List<Resource> scripts = getScripts("spring.datasource.schema", this.properties.getSchema(), "schema");
if (!scripts.isEmpty()) {
if (!isEnabled()) {
logger.debug("Initialization disabled (not running DDL scripts)");
return false;
}
String username = this.properties.getSchemaUsername();
String password = this.properties.getSchemaPassword();
// 運行sql
runScripts(scripts, username, password);
}
return !scripts.isEmpty();
}
// 執(zhí)行插入數(shù)據(jù)SQL data-*.sql
void initSchema() {
// 獲取插入數(shù)據(jù)SQL data-*.sql
List<Resource> scripts = getScripts("spring.datasource.data", this.properties.getData(), "data");
if (!scripts.isEmpty()) {
if (!isEnabled()) {
logger.debug("Initialization disabled (not running data scripts)");
return;
}
String username = this.properties.getDataUsername();
String password = this.properties.getDataPassword();
// 運行SQL
runScripts(scripts, username, password);
}
}
6.2 使用外部數(shù)據(jù)源
前面提到可以使用spring.datasource.type
來指定數(shù)據(jù)源,SpringBoot 支持的數(shù)據(jù)源包括
- Hikari
- Tomcat
- Dbcp2
自定義數(shù)據(jù)源 Druid 步驟:
-
引入 Druid 依賴
<!-- 引入自定義數(shù)據(jù)源 druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.8</version> </dependency>
-
配置自定義數(shù)據(jù)源 application.yml
spring: datasource: # 自定義數(shù)據(jù)源 type: com.alibaba.druid.pool.DruidDataSource
-
測試數(shù)據(jù)源 Druid 配置
@Test void testJdbc() throws SQLException { System.out.println("數(shù)據(jù)源:" + dataSource.getClass()); Connection connection = dataSource.getConnection(); System.out.println("Connection: " + connection); }
輸出:
數(shù)據(jù)源:class com.alibaba.druid.pool.DruidDataSource Connection: com.mysql.cj.jdbc.ConnectionImpl@5c080ef3
6.3 自定義數(shù)據(jù)源原理
SpringBoot 中自定義數(shù)據(jù)源自動配置功能,是通過配置屬性 spring.datasource.type
獲取數(shù)據(jù)源的全類名,然后通過反射創(chuàng)建該類的實例對象,然后綁定數(shù)據(jù)源的相關(guān)屬性配置,最后返回實例對象,加入到 Spring 容器。源碼如下:
DataSourceConfiguration:
@Configuration(proxyBeanMethods = false)
// 如果容器中不存在DataSource Bean,才執(zhí)行該方法將自定義數(shù)據(jù)源Bean加入容器
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {
// 將創(chuàng)建的數(shù)據(jù)源加入到容器,使用時使用 @AutoWired
@Bean
DataSource dataSource(DataSourceProperties properties) {
// 使用 DataSourceBuilder 創(chuàng)建數(shù)據(jù)源,build下下文
return properties.initializeDataSourceBuilder().build();
}
}
DataSourceBuilder:
public T build() {
// 獲取 spring.datasource.type 指定的數(shù)據(jù)源類名稱
Class<? extends DataSource> type = getType();
// 根據(jù)數(shù)據(jù)源類名稱,反射創(chuàng)建數(shù)據(jù)源示例
DataSource result = BeanUtils.instantiateClass(type);
maybeGetDriverClassName();
// 綁定數(shù)據(jù)源的屬性配置,如url, username, password
bind(result);
return (T) result;
}
這種方式無法配置自定義數(shù)據(jù)源的特有屬性,所以一般使用 《6.4 配置 Druid 數(shù)據(jù)源》 自定義配置類的方式引入外部數(shù)據(jù)源
6.4 配置 Druid 數(shù)據(jù)源
上一節(jié)中使用了外部數(shù)據(jù)源 Druid,但是不支持配置 Druid 的相關(guān)屬性,所以我們來自定義 Druid 配置類來創(chuàng)建數(shù)據(jù)源Bean DataSource 加入到 Spring 容器。
- 配置 Druid 相關(guān)屬性
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3316/sp-jdbc
driver-class-name: com.mysql.cj.jdbc.Driver
# 2.x 開啟自動初始化數(shù)據(jù)庫
initialization-mode: always
# 自定義數(shù)據(jù)源
type: com.alibaba.druid.pool.DruidDataSource
# 自動執(zhí)行的建表SQL,默認為schema-*.sql data-*.sql
# schema:
# - classpath:department.sql # - 后面有空格,classpath:后無空格
# - classpath:employee.sql
# druid 數(shù)據(jù)源其他配置
# 初始化連接個數(shù)
initialSize: 5
minIdle: 5
# 連接池的最大數(shù)據(jù)庫連接數(shù)。設(shè)為0表示無限制
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置監(jiān)控統(tǒng)計攔截的filters,去掉后監(jiān)控界面sql無法統(tǒng)計,'wall'用于防火墻
filters: stat,wall
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
- 將 Druid 數(shù)據(jù)源加入 Spring 容器
將上面配置的 Druid 數(shù)據(jù)源的屬性與 DruidDataSource 進行綁定,并將其加入到 Spring 容器
- @ConfigurationProperties 將配置文件中的相關(guān)屬性綁定到 DruidDataSource 對象
- @Bean 將方法返回的 DataSource 對象加入到 Spring 容器,使用 @Autowired 自動裝配
- 不會與默認的數(shù)據(jù)源 Hikari 沖突,因為 Hikari 數(shù)據(jù)源加入容器的條件是容器中沒有 DataSource Bean
- Hikari Bean 上的注解 @ConditionalOnMissingBean(DataSource.class) 表示沒有 DataSource Bean 時才加入容器
`
@Configuration
public class DruidConfig {
/**
* 讀取配置文件中的 spring.datasource 屬性綁定到DruidDataSource,返回一個 DataSource 數(shù)據(jù)源對象
* 并將其加入容器,替換了默認的 DataSource Bean(Hikari)
* {@link org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration.Hikari.dataSource}
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druid() {
return new DruidDataSource();
}
}
-
使用 Druid 數(shù)據(jù)源
測試 Druid 數(shù)據(jù)源的屬性配置,輸出相關(guān)屬性,與配置文件中一致,說明配置成功
// 這里裝配的數(shù)據(jù)源已經(jīng)從默認的 Hrki 變?yōu)榱?Druid @Autowired private DataSource dataSource; @Test void testDruid() throws SQLException { System.out.println("數(shù)據(jù)源:" + dataSource.getClass()); // 獲取Druid 數(shù)據(jù)源的initialSize屬性,配置文件中為5 int initialSize = ((DruidDataSource)dataSource).getInitialSize(); System.out.println("initialSize: " + initialSize); // 獲取Druid 數(shù)據(jù)源的maxActive屬性,配置文件中為20 int maxActive = ((DruidDataSource)dataSource).getMaxActive(); System.out.println("maxActive: " + maxActive); }
-
配置 Druid Web 監(jiān)控
@Configuration public class DruidConfig { /** * 讀取配置文件中的 spring.datasource 屬性綁定到DruidDataSource,返回一個 DataSource 數(shù)據(jù)源對象 * 并將其加入容器,替換了默認的 DataSource Bean(Hikari) * {@link org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration.Hikari.dataSource} */ @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druid() { return new DruidDataSource(); } // 配置Druid的監(jiān)控 // 1. 注冊一個Druid管理后臺的Servlet @Bean public ServletRegistrationBean statViewServlet() { // 注冊Servlet到容器,處理/druid/*下的所有請求 // 這里的 urlMappings 類似Controller,訪問時前面需要加上項目路徑 context-path ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); Map<String, String> initParams = new HashMap<>(); initParams.put("loginUsername", "admin"); initParams.put("loginPassword", "admin"); initParams.put("allow", ""); // 允許所有人訪問 initParams.put("deny", "192.168.1.1"); // 拒絕這個IP訪問 bean.setInitParameters(initParams); return bean; } // 2. 注冊一個 Web監(jiān)控的filter @Bean public FilterRegistrationBean webStatFilter() { FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>(); bean.setFilter(new WebStatFilter()); Map<String, String> initParams = new HashMap<>(); initParams.put("exclusions", "*.js, *.css, /druid/*"); bean.setInitParameters(initParams); bean.setUrlPatterns(Arrays.asList("/*")); return bean; } }
訪問localhost:8080/context-path/druid ,就可以查看 Druid 的 Web 監(jiān)控,可以查看數(shù)據(jù)源的配置,調(diào)用過的SQL,訪問過URI等監(jiān)控信息。
6.5 整合 MyBatis
1. 注解版
整合 Mybatis 的前提預(yù)備步驟:
- 已經(jīng)配置好了 MySQL驅(qū)動 和數(shù)據(jù)源
- 數(shù)據(jù)庫建表
- 創(chuàng)建了和表對應(yīng)的實體類
- 可以使用 JDBC 單元測試一下前三步是否準備完成
做好預(yù)備步驟后,接下來整合 SprintBoot 與 Mybatis:
- 引入 MyBatis-starter
<!-- 引入 mybatis-starter,不是Spring官方出點 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.3</version> </dependency>
[圖片上傳失敗...(image-2717e4-1606212213489)]
- 創(chuàng)建注解版Mapper,實現(xiàn)增刪改查 SQL
// 指定這是一個操作數(shù)據(jù)庫的mapper,將接口掃描裝配到容器中,接口名不需要與表名一致
@Mapper
public interface DepartmentMapper {
// 注解版,直接寫sql語句,不需要xml
@Select("select * from department where id=#{id}")
public Department getDeptById(Integer id);
@Delete("delete from department where id=#{id}")
public int deleteDeptById(Integer id);
// 標注使用自增主鍵,并且將生成的主鍵綁定到入?yún)?dept.id 屬性上
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into department(departmentName) values(#{departmentName})")
public int insertDept(Department dept);
@Update("update dapartment set departmentName=#{departmentName} where id=#{id}")
public int updateDept(Department dept);
}
- 使用 Mapper
@RestController
public class DeptController {
@Autowired
private DepartmentMapper departmentMapper;
@GetMapping("/dept/{id}")
public Department getDepartment(@PathVariable("id") Integer id) {
return departmentMapper.getDeptById(id);
}
@GetMapping("/dept")
public Department getDepartment(Department dept) {
int i = departmentMapper.insertDept(dept);
// 這里返回的dept已經(jīng)設(shè)置了自動生成的id
return dept;
}
}
2. Mybatis 常見配置
-
獲取自增主鍵:插入數(shù)據(jù)后,通常需要將數(shù)據(jù)返回,而自增主鍵一般在數(shù)據(jù)庫中生成。我們可以使用 @Options 注解,Mybatis 會自己將主鍵綁定到插入對象的屬性上,useGeneratedKeys 表示使用了自增逐漸,keyProperty 指定主鍵需要綁定的對象屬性
@Options(useGeneratedKeys = true, keyProperty = "id")
-
掃描配置:在SpringBoot 主類上加@MappserScan 指定掃描的包,不需要每個Mapper接口上標注Mapper注解
// 掃描指定包下的 mapper,不需要每個Mapper類上標注Mapper注解 @MapperScan(value = "com.atguigu.springboot.mapper") @SpringBootApplication public class SpringBoot06DataMybatisApplication {
@Mapper 注解針對的是一個個的接口,相當于是一個個 Mapper.xml 文件,可以將 SQL 寫到接口中,Mybatis 會生成代理類來執(zhí)行SQL,代理類會加入到Spring 容器。@ComponentScan 掃描的是所有 @Component注解,而 @MapperScan 掃描的是指定包下的所有接口。
-
松散綁定:查詢到的結(jié)果需要自動綁定到對象上返回,但是默認是不支持松散綁定的,如數(shù)據(jù)庫
dept_name
字段無法綁定到對象的deptName
屬性上,需要開啟松散綁定。mybatis: configuration: map-underscore-to-camel-case: true
Mybatis 自動配置類為 MybatisAutoConfiguration,根據(jù)@EnableConfigurationProperties 指定的配置類找到 MybatisProperties,其屬性 configuration 擁有屬性 mapUnderscoreToCamelCase,正是用來設(shè)置松散綁定的。
-
自定義配置:設(shè)置 Mybatis 屬性,除了配置文件,還可以使用自定義 Mybatis 配置類,下面就使用自定義 Mybatis 配置類設(shè)置松散綁定:
@org.springframework.context.annotation.Configuration public class MybatisConfig { // 這里的ConfigurationCustomizer是mybatis的自定義配置 @Bean public ConfigurationCustomizer configurationCustomizer() { return new ConfigurationCustomizer() { @Override public void customize(Configuration configuration) { // 設(shè)置松散綁定 configuration.setMapUnderscoreToCamelCase(true); } }; } }
-
開啟 SQL 打印:
mybatis configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
<settings> <setting name="logImpl" value="STDOUT_LOGGING" /> </settings>
-
關(guān)閉緩存:Mybatis 中一級緩存默認開啟,查詢操作會優(yōu)先從緩存中查詢,增刪改操作會使緩存失效。
但是在調(diào)試過程中,如果手動修改了數(shù)據(jù)庫數(shù)據(jù),Mybatis 仍會使用一級緩存中的數(shù)據(jù),導(dǎo)致需要重啟項目才能生效,所以開發(fā)過程中,建議關(guān)閉一級緩存<settings> <setting name="cacheEnabled" value="false"/> </settings>
3. xml 版
- 指定 Mybatis 的全局配置文件與所有 Mapper 文件的路徑
mybatis:
config-location: classpath:mybatis/mybatis-config.xml # mybatis全局配置文件
mapper-locations: classpath:mybatis/mapper/*.xml # 需要掃描的mapper文件
- 創(chuàng)建 Mybatis的全局配置文件
根據(jù) Mybatis 官方文檔 創(chuàng)建全局配置文件 mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 設(shè)置松散綁定,下劃線轉(zhuǎn)駝峰 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
- 創(chuàng)建Mapper 接口類與 xml 配置文件
public interface EmployeeMapper {
// 查詢
public Employee getEmpById(Integer id);
// 插入
public void insertEmp(Employee employee);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 與指定的Mapper接口類綁定 -->
<mapper namespace="com.atguigu.springboot.mapper.EmployeeMapper">
<!-- id對應(yīng)Mapper方法名,resultType是返回類型 -->
<select id="getEmpById" resultType="com.atguigu.springboot.bean.Employee">
select * from employee where id=#{id}
</select>
<!-- 自增ID綁定 -->
<insert id="insertEmp" useGeneratedKeys="true" keyProperty="id" >
insert into employee(lastName, email, gender, d_id) values(
#{lastName},
#{email},
#{gender},
#{dId}
)
</insert>
</mapper>
// 補充:對于Mybatis的xml寫法還是不熟,如果以后用到了可以專門學(xué)一下MyBatis實戰(zhàn)教程 - 10小時
6.6 整合 SpringData JPA
1. Spring Data 簡介
Spring Data 是為了簡化數(shù)據(jù)訪問,提供統(tǒng)一 API 對數(shù)據(jù)訪問層進行操作,包括關(guān)系型數(shù)據(jù)庫,非關(guān)系型數(shù)據(jù)庫,Map-Reduce框架,云數(shù)據(jù)服務(wù)等,讓我們在使用多種數(shù)據(jù)訪問技術(shù)時,都使用 Spring 提供的統(tǒng)一標準,包括 增刪改查,排序,分頁等操作。
Spring Data 包含多個子項目:
- Spring Data Commons
- Spring Data JPA
- Spring Data MongoDB
- Spring Data Redis
- Spring Data Elasticsearch
2. 整合 SpringData JPA
// 補充:這里只是復(fù)制了筆記,等學(xué)懂了JPA,再來補充理解
- 編寫一個實體類(bean)和數(shù)據(jù)表進行映射,并且配置好映射關(guān)系;
//使用JPA注解配置映射關(guān)系
@Entity //告訴JPA這是一個實體類(和數(shù)據(jù)表映射的類)
@Table(name = "tbl_user") //@Table來指定和哪個數(shù)據(jù)表對應(yīng);如果省略默認表名就是user;
public class User {
@Id //這是一個主鍵
@GeneratedValue(strategy = GenerationType.IDENTITY)//自增主鍵
private Integer id;
@Column(name = "last_name",length = 50) //這是和數(shù)據(jù)表對應(yīng)的一個列
private String lastName;
@Column //省略默認列名就是屬性名
private String email;
- 編寫一個Dao接口來操作實體類對應(yīng)的數(shù)據(jù)表(Repository)
//繼承JpaRepository來完成對數(shù)據(jù)庫的操作
public interface UserRepository extends JpaRepository<User,Integer> {
}
- 基本的配置JpaProperties
spring:
jpa:
hibernate:
# 更新或者創(chuàng)建數(shù)據(jù)表結(jié)構(gòu)
ddl-auto: update
# 控制臺顯示SQL
show-sql: true
第 7 章 SpringBoot 啟動配置原理
// 補充:這一章其實并沒有聽懂,只是復(fù)制了筆記,后面進行補充吧
下面的啟動配置原理源碼解析使用的是 SpringBoot 1.5.10 版本,
幾個重要的事件回調(diào)機制
配置在META-INF/spring.factories
ApplicationContextInitializer
SpringApplicationRunListener
只需要放在ioc容器中
ApplicationRunner
CommandLineRunner
7.1 啟動流程
1. 創(chuàng)建SpringApplication對象
進入 SpringBoot 主類的主方法,是調(diào)用 run()
方法,其中創(chuàng)建了 SpringApplication 對象,該類的構(gòu)造方法調(diào)用了initialize()
,如下所示:
initialize(sources);
private void initialize(Object[] sources) {
//保存主配置類,即啟動類
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//判斷當前是否一個web應(yīng)用,通過查看是否存在javax.servlet.Servlet類
this.webEnvironment = deduceWebEnvironment();
//從類路徑下找到META-INF/spring.factories配置的所有ApplicationContextInitializer;然后保存起來
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//從類路徑下找到ETA-INF/spring.factories配置的所有ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//從多個配置類中找到有main方法的主配置類
this.mainApplicationClass = deduceMainApplicationClass();
}
2. 運行run方法
SpringApplication 初始化完成后,調(diào)用其run()
方法:
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
FailureAnalyzers analyzers = null;
configureHeadlessProperty();
//獲取SpringApplicationRunListeners;從類路徑下META-INF/spring.factories
SpringApplicationRunListeners listeners = getRunListeners(args);
//回調(diào)所有的獲取SpringApplicationRunListener.starting()方法
listeners.starting();
try {
//封裝命令行參數(shù)
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//準備環(huán)境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//創(chuàng)建環(huán)境完成后回調(diào)SpringApplicationRunListener.environmentPrepared();表示環(huán)境準備完成
// 控制臺輸出Spring Logo
Banner printedBanner = printBanner(environment);
//創(chuàng)建ApplicationContext;決定創(chuàng)建web的ioc還是普通的ioc
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
//準備上下文環(huán)境;將environment保存到ioc中;而且applyInitializers();
//applyInitializers():回調(diào)之前保存的所有的ApplicationContextInitializer的initialize方法
//回調(diào)所有的SpringApplicationRunListener的contextPrepared();
//
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//prepareContext運行完成以后回調(diào)所有的SpringApplicationRunListener的contextLoaded();
// 這個方法進入到了Spring中,創(chuàng)建所有 bean 對象,
// 刷新容器;ioc容器初始化(如果是web應(yīng)用還會創(chuàng)建嵌入式的Tomcat);Spring注解版
// 掃描,創(chuàng)建,加載所有組件的地方;(配置類,組件,自動配置)
refreshContext(context);
//從ioc容器中獲取所有的ApplicationRunner和CommandLineRunner進行回調(diào)
//ApplicationRunner先回調(diào),CommandLineRunner再回調(diào)
afterRefresh(context, applicationArguments);
//所有的SpringApplicationRunListener回調(diào)finished方法
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//整個SpringBoot應(yīng)用啟動完成以后返回啟動的ioc容器;
return context;
}
catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex);
throw new IllegalStateException(ex);
}
}
AbstractApplicationContext: spring 容器啟動初始化Bean工廠
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// 對所有Bean進行初始化后保存到beanFactory,Bean是單例
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) { }
finally { }
}
}
7.2 事件監(jiān)聽機制
// 補充
第 8 章 SpringBoot 自定義 starter
8.1 starter 原理
starter:
1、這個場景需要使用到的依賴是什么?
2、如何編寫自動配置類
@Configuration //指定這個類是一個配置類
@ConditionalOnXXX //在指定條件成立的情況下自動配置類生效
@AutoConfigureAfter //指定自動配置類的順序
@Bean //給容器中添加組件
@ConfigurationPropertie // 指定相關(guān)xxxProperties類來綁定相關(guān)的配置
@EnableConfigurationProperties // 讓指定的xxxProperties生效加入到容器中
參考 DataSourceAutoConfiguration,WebMvcAutoConfiguration 等自動配置類
而自動配置類要能加載,將需要啟動就加載的自動配置類,配置在META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
8.2 自定義 starter
// 補充:很簡單,但是心靜不下來,后面再補充,23分鐘的視頻 p71
9. SpringBoot 與開發(fā)熱部署
在實際開發(fā)中我們修改一個文件后想看到效果必須重啟應(yīng)用,這導(dǎo)致大量時間
被浪費,我們希望不重啟應(yīng)用的情況下,程序可以自動部署(熱部署)。有以下四
種方式可以實現(xiàn)熱部署:
-
模板引擎
- 在 Spring Boot 中開發(fā)情況下禁用模板引擎的緩存
spring.thymeleaf.cache=false
- 頁面模板改變
ctrl+F9
可以重新編譯當前頁面并生效
- 在 Spring Boot 中開發(fā)情況下禁用模板引擎的緩存
-
Spring Loaded
- Spring官方提供的熱部署程序,實現(xiàn)修改類文件的熱部署
- 下載Spring Loaded(項目地址https://github.com/spring?projects/spring-loaded)
- 添加運行時參數(shù)
-javaagent:C:/springloaded-1.2.5.RELEASE.jar -noverify
-
Spring Boot Devtools
- 引入依賴
<!-- 引入熱部署依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency>
-
ctrl + f9
重新構(gòu)建一次項目即可生效
- 引入依賴
-
JRebel (推薦)
- 收費插件,安裝后使用即可
- 激活方式: https://juejin.im/post/5d8340976fb9a06b1777ee35
- https://www.cnblogs.com/lideqiang0909/p/11635932.html
- 安裝后使用
JRebel 方式啟動
項目,修改代碼后,ctrl + f9
編譯項目后自動將修改過的類熱加載,并打印熱加載日志 - 可以設(shè)置 自動構(gòu)建項目,省去
ctrl + f9
編譯項目步驟2020-07-29 07:15:48 JRebel: Reloading class 'com.imooc.pojo.bo.center.UserInfoBO'.
Spring Boot Devtools 熱部署的本質(zhì)還是重啟,效率很低。IDEA 中自帶插件 HotSwap,例如在修改 Controller 層的代碼后ctrl + f9
能夠立即生效,但是修改 Controller 引用的其他模塊代碼,并不會立即生效,而 JRebel 能夠?qū)崿F(xiàn)動態(tài)類加載,所以推薦使用 JRebel 進行熱加載。
進階學(xué)習(xí)
-
7小時,主要講了 SpringBoot 與常用技術(shù)的整合,包括 Redis,RabbitMQ,ElasticSearch,定時任務(wù),SpringSecurity,Zookeeper/dubbo,SpringCloud等,等熟悉了這些技術(shù)后再學(xué)習(xí)與 SpringBoot 整合
-
11 小時,學(xué)習(xí)當前這門課,許多源碼部分都涉及到了 Spring 原生注解,很多實際開發(fā)用不到的注解,在看源碼時很有用,需要查缺補漏
-
25 小時,分布式微服務(wù)必備,必須具有 SpringBoot 基礎(chǔ)
-
圖解+仿寫深入解析SpringBoot源碼課 - 慕課網(wǎng)
13小時,當前筆記中許多 SpringBoot 源碼和原理的地方?jīng)]有完全搞清楚,借助這門課搞清
-
Spring Boot企業(yè)微信點餐系統(tǒng) - 慕課網(wǎng)
學(xué)了SpringBoot,需要使用項目鞏固一下,否則很容易忘記,也無法檢測學(xué)習(xí)成果。這門課并不算全面,缺少密碼加密,購物車,圖片上傳等功能,參考從0開始Java電商網(wǎng)站開發(fā)
待補充
- 補充筆記中寫了
// 補充
的部分,這些部分大多都是重難點。包括自動配置原理,@Conditional,SpringMVC自動配置原理,嵌入式 Servlet 容器自動配置原理,SpringBoot啟動配置原理,監(jiān)聽事件機制,自定義 starter - 分析SpringMVC工作流程,根據(jù)LoginController debug和3y mvc筆記,核心控制器,映射器,適配器,視圖解析器等等,非常簡單(反射調(diào)用方法,傳參)
SpringMVC - 運行流程圖及原理分析
https://blog.csdn.net/j080624/article/details/58041191
https://blog.csdn.net/j080624/article/details/57411870
[圖片上傳失敗...(image-fbd23b-1606212213489)]
[圖片上傳失敗...(image-e93db3-1606212213489)]
-
Bean 的生命周期,Spring IOC 的初始化,參考文章Bean的生命周期 - Guide 推薦
https://blog.csdn.net/fageweiketang/article/details/80994433
https://mp.weixin.qq.com/s/IKrWnD_O4_L7tbhmTFG_pg
https://mp.weixin.qq.com/s/0tWgaYxavixiDCppvOfd-w
在講解該控制器之前,首先我們要明白SpringMVC控制器一個與Struts2不同的地方:SpringMVC的控制器是單例的,Struts2的控制器是多例的!
也就是說:Struts2收集變量是定義成員變量來進行接收,而SpringMVC作為單例的,是不可能使用成員變量來進行接收的【因為會有多個用戶訪問,就會出現(xiàn)數(shù)據(jù)不合理性】!
那么SpringMVC作為單例的,他只能通過方法的參數(shù)來進行接收對應(yīng)的參數(shù)!只有方法才能保證不同的用戶對應(yīng)不同的數(shù)據(jù)!
- 看一下過濾器和監(jiān)聽器,搞明白CharacterEncodingFilter,HiddenHttpMethodFilter,ContextLoaderListener的原理
推薦閱讀
- @RequestParam注解詳解
- 15個經(jīng)典的Spring面試題 - Guide
- Spring 面試題及解析 - 程序員諸葛
- Spring Boot熱部署 - 慕課網(wǎng)
- Spring MVC中的Controller是Serlvet嗎?
- 關(guān)于 @EnableConfigurationProperties 注解
- Spring Boot啟動流程源碼詳解
- Spring/SpringBoot常用注解總結(jié) - Guide
- 15個經(jīng)典的Spring面試常見問題解析 - Guide
- Spring Boot 熱部署入門 - 芋道源碼