- 第 5 章 SpringBoot 与 Docker
- 5.1 Docker 简介
- 5.2 核心概念
- 5.3 安装Docker
- 安装linux虚拟机
- 在linux虚拟机上安装docker
- 5.4 Docker常用命令&操作
-
- 镜像操作
-
- 容器操作
-
- 安装 MySQL 示例
-
- 第 6 章 SpringBoot 与数据访问
- 6.1 数据源初始化与 JDBC
-
- 配置 MySQL
-
- 数据源自动配置原理
-
- 数据表自动初始化
-
- 使用 JdbcTemplate 查询数据
-
- 数据库自动初始化原理
-
- 6.2 使用外部数据源
- 6.3 自定义数据源原理
- 6.4 配置 Druid 数据源
- 6.5 整合 MyBatis
-
- 注解版
-
- Mybatis 常见配置
-
- xml 版
-
- 6.6 整合 SpringData JPA
-
- Spring Data 简介
-
- 整合 SpringData JPA
-
- 6.1 数据源初始化与 JDBC
- 第 7 章 SpringBoot 启动配置原理
- 7.1 启动流程
-
- 创建SpringApplication对象
-
- 运行run方法
-
- 7.2 事件监听机制
- 7.1 启动流程
- 第 8 章 SpringBoot 自定义 starter
- 8.1 starter 原理
- 8.2 自定义 starter
-
- SpringBoot 与开发热部署
- 进阶学习
- 待补充
- 推荐阅读
- 参考文档
第 5 章 SpringBoot 与 Docker
5.1 Docker 简介
Docker是一个开源的应用容器引擎,轻量级容器技术。Docker支持将软件编译成一个镜像,然后在镜像中各种软件做好配置,将镜像发布出去,其他使用者可以直接使用这个镜像;运行中的这个镜像称为容器,容器启动是非常快速的。
参考 我的 Docker 入门笔记
[图片上传失败...(image-82059e-1606212213489)]
5.2 核心概念
- 主机(Host):安装了Docker程序的机器(Docker直接安装在操作系统之上)
- 客户端(Client):连接docker主机进行操作
- 仓库(Registry):用来保存各种打包好的软件镜像
- 镜像(Images):软件打包好的镜像;放在docker仓库中
- 容器(Container):镜像启动后的实例称为一个容器,容器是独立运行的一个或一组应用
使用Docker的步骤:
- 安装Docker
- 去Docker仓库找到这个软件对应的镜像
- 使用Docker运行这个镜像,这个镜像就会生成一个Docker容器
- 对容器的启动停止就是对软件的启动停止
5.3 安装Docker
安装linux虚拟机
VMWare、VirtualBox(安装)
导入虚拟机文件centos7-atguigu.ova
双击启动linux虚拟机;使用 root/ 123456登陆
使用客户端连接linux服务器进行命令操作
设置虚拟机网络;桥接网络===选好网卡====接入网线;
-
设置好网络以后使用命令重启虚拟机的网络
service network restart
-
查看linux的ip地址
ip addr
使用客户端连接linux
在linux虚拟机上安装docker
参考 Docker 安装与启动
步骤:
# 1、检查内核版本,必须是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 关键字 eg:docker search redis | 我们经常去docker hub上检索镜像的详细信息,如镜像的TAG。 |
拉取 | docker pull 镜像名:tag | :tag是可选的,tag表示标签,多为软件的版本,默认是latest |
列表 | docker images | 查看所有本地镜像 |
删除 | docker rmi image-id | 删除指定的本地镜像 |
https://hub.docker.com/
2. 容器操作
软件镜像(QQ安装程序)----运行镜像----产生一个容器(正在运行的软件,运行的QQ);
步骤:
# 1、搜索镜像
[root@localhost ~]# docker search tomcat
# 2、拉取镜像
[root@localhost ~]# docker pull tomcat
# 3、根据镜像启动容器
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: 将主机的端口映射到容器的一个端口 主机端口:容器内部的端口
# 10、为了演示简单关闭了linux的防火墙
service firewalld status ;查看防火墙状态
service firewalld stop:关闭防火墙
# 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 镜像文档,可以看到启动命令中需要设置 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
# 查看容器,发现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;这个三个参数必须指定一个
# 正确的启动,设置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.
# 显示所有数据库
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)
安装完成后,可以使用 Navicat 连接 Docker 中的 MySQL,连接端口为容器映射到主机的端口,但是发生错误:
Client does not support authentication protocol
经过查找资料,发现是 Navicat 不支持 MySQL 8.0 的原因,于是我关闭了 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的一些配置参数
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 与数据访问
对于数据访问层,无论是 SQL 还是 NOSQL,Spring Boot 默认采用整合 Spring Data 的方式进行统一处理,添加大量自动配置,屏蔽了很多设置。引入各种 xxxTemplate,xxxRepository 来简化我们对数据访问层的操作。对我们来说只需要进行简单的设置即可。我们将在数据访问章节测试使用SQL相关、NOSQL在缓存、消息、检索等章节测试。
– JDBC
– MyBatis
– JPA
6.1 数据源初始化与 JDBC
1. 配置 MySQL
- 引入 Mysql 驱动 和 jdbc-starter:
默认MySQL 驱动是 8.0
org.springframework.boot
spring-boot-starter-jdbc
mysql
mysql-connector-java
runtime
Mysql驱动scope设置为runtime?
- 在
application.yml
中配置数据源,新版驱动变为了 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("数据源:" + dataSource.getClass());
Connection connection = dataSource.getConnection();
System.out.println("Connection: " + connection);
}
经过测试,说明配置 MySQL 成功,SpringBoot 2.x 默认使用 Hikari 数据源
数据源:class com.zaxxer.hikari.HikariDataSource
Connection: HikariProxyConnection@356519935 wrapping com.mysql.cj.jdbc.ConnectionImpl@18d910b3
什么是数据源?
数据源是对数据库操作的抽象,封装了目标源的位置信息,验证信息和建立与关闭连接的操作。不同数据库可以实现接口提供不同策略。常见数据源包括:DriverManagerDataSource(不提供连接池),C3P0,Dbcp2,Hikari等
数据源更多的属性配置参考:DataSourceProperties
- @ConfigurationProperties 从配置文件根据
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. 数据源自动配置原理
数据源自动配置原理: DataSourceConfiguration
- SpringBoot 2.x 默认数据源为 hikari
- 可以使用
spring.datasource.type
修改数据源类型 - @Configuration 底层是 @Component,标注这个类会被扫描
- @ConfigurationProperties 从配置文件根据
prefix
读取属性绑定 - @Bean 将方法返回的对象加入到 Spring 容器,该方法只会被调用一次
- @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. 数据表自动初始化
- 创建建表 SQL
schema-*.sql
,插入数据 SQLdata-*.sql
,放在 /resource 目录下 - 配置中加入
spring.datasource.initialization-mode=always
表示启动自动建表的操作 (SpringBoot 2.x) - 启动项目就可以自动运行建表,插入数据了
注意:每次项目启动都会执行 SQL,即重新创建表和插入数据
默认建表 SQL名称为schema-*.sql
,插入数据 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 开启自动初始化数据库
initialization-mode: always
# 指定自动执行的建表SQL,List需要用 - 表示
schema:
- classpath:department.sql # - 后面有空格,classpath:后无空格
- classpath:employee.sql
data:
- classpath:init-dept.sql # 插入数据SQL
4. 使用 JdbcTemplate 查询数据
JdbcTemplateConfiguration 会创建 JdbcTemplate Bean 并加入到容器,使用 @Autowired 自动装配即可查询数据
@RestController
public class HelloController {
// SpringBoot自动配置了 JdbcTemplate组件,并加入到容器
@Autowired
JdbcTemplate jdbcTemplate;
@GetMapping("/query")
public Map getDept() {
List
5. 数据库自动初始化原理
数据库自动初始化原理: DataSourceInitializerInvoker,DataSourceInitializer
- 数据源初始化完成(被创建)时,帮我们运行 schema-.sql 建表
- 监听建表完成事件,完成后执行 data-.sql 插入数据
DataSourceInitializerInvoker:
class DataSourceInitializerInvoker implements ApplicationListener, InitializingBean {
// 数据源 DataSource Bean初始化完成后调用
@Override
public void afterPropertiesSet() {
DataSourceInitializer initializer = getDataSourceInitializer();
if (initializer != null) {
// 获取建表 schema-*.sql 并执行,见下文
boolean schemaCreated = this.dataSourceInitializer.createSchema();
if (schemaCreated) {
// 表已经创建完成,触发DataSourceSchemaCreatedEvent事件
initialize(initializer);
}
}
}
// 监听 DataSourceSchemaCreatedEvent事件,执行插入数据SQL
@Override
public void onApplicationEvent(DataSourceSchemaCreatedEvent event) {
DataSourceInitializer initializer = getDataSourceInitializer();
if (!this.initialized && initializer != null) {
// 获取data-*.sql 并执行,见下文
initializer.initSchema();
this.initialized = true;
}
}
上面执行建表SQL 与 插入数据 SQL 方法内容如下:
DataSourceInitializer:
// 执行建表SQL schema-*.sql
boolean createSchema() {
// 获取需要执行的建表schema-*.sql
List 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();
}
// 执行插入数据SQL data-*.sql
void initSchema() {
// 获取插入数据SQL data-*.sql
List 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 使用外部数据源
前面提到可以使用spring.datasource.type
来指定数据源,SpringBoot 支持的数据源包括
- Hikari
- Tomcat
- Dbcp2
自定义数据源 Druid 步骤:
-
引入 Druid 依赖
com.alibaba druid 1.1.8 -
配置自定义数据源 application.yml
spring: datasource: # 自定义数据源 type: com.alibaba.druid.pool.DruidDataSource
-
测试数据源 Druid 配置
@Test void testJdbc() throws SQLException { System.out.println("数据源:" + dataSource.getClass()); Connection connection = dataSource.getConnection(); System.out.println("Connection: " + connection); }
输出:
数据源:class com.alibaba.druid.pool.DruidDataSource Connection: com.mysql.cj.jdbc.ConnectionImpl@5c080ef3
6.3 自定义数据源原理
SpringBoot 中自定义数据源自动配置功能,是通过配置属性 spring.datasource.type
获取数据源的全类名,然后通过反射创建该类的实例对象,然后绑定数据源的相关属性配置,最后返回实例对象,加入到 Spring 容器。源码如下:
DataSourceConfiguration:
@Configuration(proxyBeanMethods = false)
// 如果容器中不存在DataSource Bean,才执行该方法将自定义数据源Bean加入容器
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {
// 将创建的数据源加入到容器,使用时使用 @AutoWired
@Bean
DataSource dataSource(DataSourceProperties properties) {
// 使用 DataSourceBuilder 创建数据源,build下下文
return properties.initializeDataSourceBuilder().build();
}
}
DataSourceBuilder:
public T build() {
// 获取 spring.datasource.type 指定的数据源类名称
Class extends DataSource> type = getType();
// 根据数据源类名称,反射创建数据源示例
DataSource result = BeanUtils.instantiateClass(type);
maybeGetDriverClassName();
// 绑定数据源的属性配置,如url, username, password
bind(result);
return (T) result;
}
这种方式无法配置自定义数据源的特有属性,所以一般使用 《6.4 配置 Druid 数据源》 自定义配置类的方式引入外部数据源
6.4 配置 Druid 数据源
上一节中使用了外部数据源 Druid,但是不支持配置 Druid 的相关属性,所以我们来自定义 Druid 配置类来创建数据源Bean DataSource 加入到 Spring 容器。
- 配置 Druid 相关属性
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3316/sp-jdbc
driver-class-name: com.mysql.cj.jdbc.Driver
# 2.x 开启自动初始化数据库
initialization-mode: always
# 自定义数据源
type: com.alibaba.druid.pool.DruidDataSource
# 自动执行的建表SQL,默认为schema-*.sql data-*.sql
# schema:
# - classpath:department.sql # - 后面有空格,classpath:后无空格
# - classpath:employee.sql
# druid 数据源其他配置
# 初始化连接个数
initialSize: 5
minIdle: 5
# 连接池的最大数据库连接数。设为0表示无限制
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
- 将 Druid 数据源加入 Spring 容器
将上面配置的 Druid 数据源的属性与 DruidDataSource 进行绑定,并将其加入到 Spring 容器
- @ConfigurationProperties 将配置文件中的相关属性绑定到 DruidDataSource 对象
- @Bean 将方法返回的 DataSource 对象加入到 Spring 容器,使用 @Autowired 自动装配
- 不会与默认的数据源 Hikari 冲突,因为 Hikari 数据源加入容器的条件是容器中没有 DataSource Bean
- Hikari Bean 上的注解 @ConditionalOnMissingBean(DataSource.class) 表示没有 DataSource Bean 时才加入容器
`
@Configuration
public class DruidConfig {
/**
* 读取配置文件中的 spring.datasource 属性绑定到DruidDataSource,返回一个 DataSource 数据源对象
* 并将其加入容器,替换了默认的 DataSource Bean(Hikari)
* {@link org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration.Hikari.dataSource}
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druid() {
return new DruidDataSource();
}
}
-
使用 Druid 数据源
测试 Druid 数据源的属性配置,输出相关属性,与配置文件中一致,说明配置成功
// 这里装配的数据源已经从默认的 Hrki 变为了 Druid @Autowired private DataSource dataSource; @Test void testDruid() throws SQLException { System.out.println("数据源:" + dataSource.getClass()); // 获取Druid 数据源的initialSize属性,配置文件中为5 int initialSize = ((DruidDataSource)dataSource).getInitialSize(); System.out.println("initialSize: " + initialSize); // 获取Druid 数据源的maxActive属性,配置文件中为20 int maxActive = ((DruidDataSource)dataSource).getMaxActive(); System.out.println("maxActive: " + maxActive); }
-
配置 Druid Web 监控
@Configuration public class DruidConfig { /** * 读取配置文件中的 spring.datasource 属性绑定到DruidDataSource,返回一个 DataSource 数据源对象 * 并将其加入容器,替换了默认的 DataSource Bean(Hikari) * {@link org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration.Hikari.dataSource} */ @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druid() { return new DruidDataSource(); } // 配置Druid的监控 // 1. 注册一个Druid管理后台的Servlet @Bean public ServletRegistrationBean statViewServlet() { // 注册Servlet到容器,处理/druid/*下的所有请求 // 这里的 urlMappings 类似Controller,访问时前面需要加上项目路径 context-path ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*"); Map
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监控的filter @Bean public FilterRegistrationBean webStatFilter() { FilterRegistrationBean bean = new FilterRegistrationBean<>(); bean.setFilter(new WebStatFilter()); Map 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 监控,可以查看数据源的配置,调用过的SQL,访问过URI等监控信息。
6.5 整合 MyBatis
1. 注解版
整合 Mybatis 的前提预备步骤:
- 已经配置好了 MySQL驱动 和数据源
- 数据库建表
- 创建了和表对应的实体类
- 可以使用 JDBC 单元测试一下前三步是否准备完成
做好预备步骤后,接下来整合 SprintBoot 与 Mybatis:
- 引入 MyBatis-starter
org.mybatis.spring.boot mybatis-spring-boot-starter 2.1.3
[图片上传失败...(image-2717e4-1606212213489)]
- 创建注解版Mapper,实现增删改查 SQL
// 指定这是一个操作数据库的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);
// 标注使用自增主键,并且将生成的主键绑定到入参 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已经设置了自动生成的id
return dept;
}
}
2. Mybatis 常见配置
-
获取自增主键:插入数据后,通常需要将数据返回,而自增主键一般在数据库中生成。我们可以使用 @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 会生成代理类来执行SQL,代理类会加入到Spring 容器。@ComponentScan 扫描的是所有 @Component注解,而 @MapperScan 扫描的是指定包下的所有接口。
-
松散绑定:查询到的结果需要自动绑定到对象上返回,但是默认是不支持松散绑定的,如数据库
dept_name
字段无法绑定到对象的deptName
属性上,需要开启松散绑定。mybatis: configuration: map-underscore-to-camel-case: true
Mybatis 自动配置类为 MybatisAutoConfiguration,根据@EnableConfigurationProperties 指定的配置类找到 MybatisProperties,其属性 configuration 拥有属性 mapUnderscoreToCamelCase,正是用来设置松散绑定的。
-
自定义配置:设置 Mybatis 属性,除了配置文件,还可以使用自定义 Mybatis 配置类,下面就使用自定义 Mybatis 配置类设置松散绑定:
@org.springframework.context.annotation.Configuration public class MybatisConfig { // 这里的ConfigurationCustomizer是mybatis的自定义配置 @Bean public ConfigurationCustomizer configurationCustomizer() { return new ConfigurationCustomizer() { @Override public void customize(Configuration configuration) { // 设置松散绑定 configuration.setMapUnderscoreToCamelCase(true); } }; } }
-
开启 SQL 打印:
mybatis configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
-
关闭缓存:Mybatis 中一级缓存默认开启,查询操作会优先从缓存中查询,增删改操作会使缓存失效。
但是在调试过程中,如果手动修改了数据库数据,Mybatis 仍会使用一级缓存中的数据,导致需要重启项目才能生效,所以开发过程中,建议关闭一级缓存
3. xml 版
- 指定 Mybatis 的全局配置文件与所有 Mapper 文件的路径
mybatis:
config-location: classpath:mybatis/mybatis-config.xml # mybatis全局配置文件
mapper-locations: classpath:mybatis/mapper/*.xml # 需要扫描的mapper文件
- 创建 Mybatis的全局配置文件
根据 Mybatis 官方文档 创建全局配置文件 mybatis-config.xml
- 创建Mapper 接口类与 xml 配置文件
public interface EmployeeMapper {
// 查询
public Employee getEmpById(Integer id);
// 插入
public void insertEmp(Employee employee);
}
insert into employee(lastName, email, gender, d_id) values(
#{lastName},
#{email},
#{gender},
#{dId}
)
// 补充:对于Mybatis的xml写法还是不熟,如果以后用到了可以专门学一下MyBatis实战教程 - 10小时
6.6 整合 SpringData JPA
1. Spring Data 简介
Spring Data 是为了简化数据访问,提供统一 API 对数据访问层进行操作,包括关系型数据库,非关系型数据库,Map-Reduce框架,云数据服务等,让我们在使用多种数据访问技术时,都使用 Spring 提供的统一标准,包括 增删改查,排序,分页等操作。
Spring Data 包含多个子项目:
- Spring Data Commons
- Spring Data JPA
- Spring Data MongoDB
- Spring Data Redis
- Spring Data Elasticsearch
2. 整合 SpringData JPA
// 补充:这里只是复制了笔记,等学懂了JPA,再来补充理解
- 编写一个实体类(bean)和数据表进行映射,并且配置好映射关系;
//使用JPA注解配置映射关系
@Entity //告诉JPA这是一个实体类(和数据表映射的类)
@Table(name = "tbl_user") //@Table来指定和哪个数据表对应;如果省略默认表名就是user;
public class User {
@Id //这是一个主键
@GeneratedValue(strategy = GenerationType.IDENTITY)//自增主键
private Integer id;
@Column(name = "last_name",length = 50) //这是和数据表对应的一个列
private String lastName;
@Column //省略默认列名就是属性名
private String email;
- 编写一个Dao接口来操作实体类对应的数据表(Repository)
//继承JpaRepository来完成对数据库的操作
public interface UserRepository extends JpaRepository {
}
- 基本的配置JpaProperties
spring:
jpa:
hibernate:
# 更新或者创建数据表结构
ddl-auto: update
# 控制台显示SQL
show-sql: true
第 7 章 SpringBoot 启动配置原理
// 补充:这一章其实并没有听懂,只是复制了笔记,后面进行补充吧
下面的启动配置原理源码解析使用的是 SpringBoot 1.5.10 版本,
几个重要的事件回调机制
配置在META-INF/spring.factories
ApplicationContextInitializer
SpringApplicationRunListener
只需要放在ioc容器中
ApplicationRunner
CommandLineRunner
7.1 启动流程
1. 创建SpringApplication对象
进入 SpringBoot 主类的主方法,是调用 run()
方法,其中创建了 SpringApplication 对象,该类的构造方法调用了initialize()
,如下所示:
initialize(sources);
private void initialize(Object[] sources) {
//保存主配置类,即启动类
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
//判断当前是否一个web应用,通过查看是否存在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 初始化完成后,调用其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);
//回调所有的获取SpringApplicationRunListener.starting()方法
listeners.starting();
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//准备环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
//创建环境完成后回调SpringApplicationRunListener.environmentPrepared();表示环境准备完成
// 控制台输出Spring Logo
Banner printedBanner = printBanner(environment);
//创建ApplicationContext;决定创建web的ioc还是普通的ioc
context = createApplicationContext();
analyzers = new FailureAnalyzers(context);
//准备上下文环境;将environment保存到ioc中;而且applyInitializers();
//applyInitializers():回调之前保存的所有的ApplicationContextInitializer的initialize方法
//回调所有的SpringApplicationRunListener的contextPrepared();
//
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded();
// 这个方法进入到了Spring中,创建所有 bean 对象,
// 刷新容器;ioc容器初始化(如果是web应用还会创建嵌入式的Tomcat);Spring注解版
// 扫描,创建,加载所有组件的地方;(配置类,组件,自动配置)
refreshContext(context);
//从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调
//ApplicationRunner先回调,CommandLineRunner再回调
afterRefresh(context, applicationArguments);
//所有的SpringApplicationRunListener回调finished方法
listeners.finished(context, null);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
//整个SpringBoot应用启动完成以后返回启动的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 事件监听机制
// 补充
第 8 章 SpringBoot 自定义 starter
8.1 starter 原理
starter:
1、这个场景需要使用到的依赖是什么?
2、如何编写自动配置类
@Configuration //指定这个类是一个配置类
@ConditionalOnXXX //在指定条件成立的情况下自动配置类生效
@AutoConfigureAfter //指定自动配置类的顺序
@Bean //给容器中添加组件
@ConfigurationPropertie // 指定相关xxxProperties类来绑定相关的配置
@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 与开发热部署
在实际开发中我们修改一个文件后想看到效果必须重启应用,这导致大量时间
被浪费,我们希望不重启应用的情况下,程序可以自动部署(热部署)。有以下四
种方式可以实现热部署:
-
模板引擎
- 在 Spring Boot 中开发情况下禁用模板引擎的缓存
spring.thymeleaf.cache=false
- 页面模板改变
ctrl+F9
可以重新编译当前页面并生效
- 在 Spring Boot 中开发情况下禁用模板引擎的缓存
-
Spring Loaded
- Spring官方提供的热部署程序,实现修改类文件的热部署
- 下载Spring Loaded(项目地址https://github.com/spring�projects/spring-loaded)
- 添加运行时参数
-javaagent:C:/springloaded-1.2.5.RELEASE.jar -noverify
-
Spring Boot Devtools
- 引入依赖
org.springframework.boot spring-boot-devtools -
ctrl + f9
重新构建一次项目即可生效
- 引入依赖
-
JRebel (推荐)
- 收费插件,安装后使用即可
- 激活方式: https://juejin.im/post/5d8340976fb9a06b1777ee35
- https://www.cnblogs.com/lideqiang0909/p/11635932.html
- 安装后使用
JRebel 方式启动
项目,修改代码后,ctrl + f9
编译项目后自动将修改过的类热加载,并打印热加载日志 - 可以设置 自动构建项目,省去
ctrl + f9
编译项目步骤2020-07-29 07:15:48 JRebel: Reloading class 'com.imooc.pojo.bo.center.UserInfoBO'.
Spring Boot Devtools 热部署的本质还是重启,效率很低。IDEA 中自带插件 HotSwap,例如在修改 Controller 层的代码后ctrl + f9
能够立即生效,但是修改 Controller 引用的其他模块代码,并不会立即生效,而 JRebel 能够实现动态类加载,所以推荐使用 JRebel 进行热加载。
进阶学习
-
SpringBoot 整合教程 - 尚硅谷
7小时,主要讲了 SpringBoot 与常用技术的整合,包括 Redis,RabbitMQ,ElasticSearch,定时任务,SpringSecurity,Zookeeper/dubbo,SpringCloud等,等熟悉了这些技术后再学习与 SpringBoot 整合
-
Spring注解驱动教程 - 尚硅谷
11 小时,学习当前这门课,许多源码部分都涉及到了 Spring 原生注解,很多实际开发用不到的注解,在看源码时很有用,需要查缺补漏
-
SpringCloud 教程 - 尚硅谷
25 小时,分布式微服务必备,必须具有 SpringBoot 基础
-
图解+仿写深入解析SpringBoot源码课 - 慕课网
13小时,当前笔记中许多 SpringBoot 源码和原理的地方没有完全搞清楚,借助这门课搞清
-
Spring Boot企业微信点餐系统 - 慕课网
学了SpringBoot,需要使用项目巩固一下,否则很容易忘记,也无法检测学习成果。这门课并不算全面,缺少密码加密,购物车,图片上传等功能,参考从0开始Java电商网站开发
SSM 教程
待补充
- 补充笔记中写了
// 补充
的部分,这些部分大多都是重难点。包括自动配置原理,@Conditional,SpringMVC自动配置原理,嵌入式 Servlet 容器自动配置原理,SpringBoot启动配置原理,监听事件机制,自定义 starter - 分析SpringMVC工作流程,根据LoginController debug和3y mvc笔记,核心控制器,映射器,适配器,视图解析器等等,非常简单(反射调用方法,传参)
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作为单例的,是不可能使用成员变量来进行接收的【因为会有多个用户访问,就会出现数据不合理性】!
那么SpringMVC作为单例的,他只能通过方法的参数来进行接收对应的参数!只有方法才能保证不同的用户对应不同的数据!
- 看一下过滤器和监听器,搞明白CharacterEncodingFilter,HiddenHttpMethodFilter,ContextLoaderListener的原理
推荐阅读
- @RequestParam注解详解
- 15个经典的Spring面试题 - Guide
- Spring 面试题及解析 - 程序员诸葛
- Spring Boot热部署 - 慕课网
- Spring MVC中的Controller是Serlvet吗?
- 关于 @EnableConfigurationProperties 注解
- Spring Boot启动流程源码详解
- Spring/SpringBoot常用注解总结 - Guide
- 15个经典的Spring面试常见问题解析 - Guide
- Spring Boot 热部署入门 - 芋道源码
参考文档
SpringBoot 核心技术 - 尚硅谷
Spring 注解驱动教程 - 尚硅谷
Spring 和 SpringBoot 比较
SpringBoot 2.3.1 官方文档
SpringBoot 66个常用Demo - Github
SpringBoot 整合示例
Srping 易混淆注解对比 - Hollis
thymeleaf3.0 官方文档
Docker MySQL 镜像 官方文档
Docker 入门笔记