介绍
代码仓库地址:https://gitee.com/CandyWall/spring-boot-study 跟着黑马程序员spring boot本教程的学习笔记与视频内容的项目名称和代码略有不同。它们都是基于我自己的考虑。代码已被操作验证,仅供参考。
视频教程地址:https://www.bilibili.com/video/BV15b4y1a7yG
注:四级标题和部分五级标题以子项目名称命名,与我的代码仓库项目一一对应。
每个子项目对应的视频链接和一些重要内容的笔记
一、基础篇
1.入门案例
springboot_01_01_quickstart
P3 基础篇-03-SpringBoot入门案例(Idea联网版)18:12
springboot_01_02_quickstart
P4 基础篇-04-SpringBoot入门案例(官网创建版)06:21
springboot_01_03_quickstart
P5 基础篇-05-SpringBoot入门案例(阿里云版)06:01
springboot_01_04_quickstart
P6 基础篇-06-SpringBoot08:34入门案例(手工制作版)
P7 基本文-07-教你一招:隐藏文件或文件夹
P8 基本文-08-入门案例分析:parent
P9 基本文-09-入门案例分析:starter
P10 基础文章-10-入门案例分析:指导类
P11 基本文章-11-入门案例:辅助功能:替换:SpringBoot内嵌的默认的web容器从tomcat换成jetty
1.1 补充
springboot_01_05_rest
P12 知识加油站-01-REST风格简介
P13 知识加油站-02-RESTful入门案例
P14 知识加油站-03-RESTful快速开发
springboot_0x_02x_xxxxxxxx
2. 基础配置
P15 基本文章-12-教你一招:复制模块
springboot_02_base_configuration
P16 基本文-13-属性配置
P17 基本配置-14-基本配置
P18 配置文件类型-15-3
-
properties
-
yml(主流格式)
-
yaml
P19 配置文件加载优先级
三种格式共存,从先到后优先properties、yml、yaml
P20 基本文章-17-教你一招:属性提示消失解决方案
3. yaml配置文件
springboot_03_yaml
P21 基础篇-18-yaml数据格式
P22 基础篇-19-读取yaml单属性数据
P23 基础篇-20-yaml引用文件中的变量
P24 基础篇-21-读取yaml所有属性数据
P25 基础篇-22-读取yaml引用类型属性数据
4. 整合Junit
springboot_04_junit
P27基础篇-24-整合JUnit——classes属性
5. 整合mybatis
springboot_05_mybatis
P28基础篇-25-SpringBoot整合MyBatis
课程中使用的数据库脚本
/* Navicat MySQL Data Transfer Source Server : localhost Source Server Type : MySQL Source Server Version : 80023 Source Host : localhost:3306 Source Schema : springboot_db Target Server Type : MySQL Target Server Version : 80023 File Encoding : 65001 Date: 20/01/2022 11:50:34 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for tbl_book -- ---------------------------- DROP TABLE IF EXISTS `tbl_book`; CREATE TABLE `tbl_book` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, `type` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, `description` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of tbl_book -- ---------------------------- INSERT INTO `tbl_book` VALUES (1, '三体', '科幻', 大刘的巅峰之作,将中国科幻小说推向了世界舞台。分为三部曲:《地球往事》、《黑暗森林》、《死神永生》。分为三部曲:《地球往事》、《黑暗森林》、《死神永生》。 INSERT INTO `tbl_book` VALUES (2, 格林童话, '童话', 睡前故事。'); INSERT INTO `tbl_book` VALUES (3, 'Spring 设计模式, 计算机理论, '深入Spring源码剖析Spring源代码中包含的10种设计模式; INSERT INTO `tbl_book` VALUES (4, 'Spring MVC MyBatis从入门到项目实战开发, 计算机理论, 全面分析面向Web轻量级框架的应用,带你成为Spring MVC开发开发; INSERT INTO `tbl_book` VALUES (5, '轻量级Java Web实战企业应用, 计算机理论, 源码级分析Spring适合已掌握的框架Java基本读者'); INSERT INTO `tbl_book` VALUES (6, 'Java核心技术卷|基础知识(原书第11版), 计算机理论, 'Core Java第11版,Jolt获奖作品,针对Java SE9、10、 11全面更新; INSERT INTO `tbl_book` VALUES (7, '深入理解Java虚拟机', 计算机理论, 全面分析五维JVM,全覆盖面试知识点; INSERT INTO `tbl_book` VALUES (8, 'Java编程思想(第四版), 计算机理论, 'Java学习必读经典殿堂级作品!赢得全球程序员的广泛赞誉; INSERT INTO `tbl_book` VALUES (9, '零基础学Java (全彩版), '计算机理论', 从浅到深,详解Java语言编程思想和核心技术; INSERT INTO `tbl_book` VALUES (10, 就这样做吧:主播高效沟通实战指南, 市场营销, 书中有李子七、李佳琪、薇娅成长为网红的秘密。 INSERT INTO `tbl_book` VALUES (11, 直播销讲实战一本通, 市场营销, 与秋叶一起学习一系列网络营销书籍; INSERT INTO `tbl_book` VALUES (12, 直播带货:淘宝、天猫直播从新手到高手, 市场营销, 教你如何玩直播书, 10课轻松实现月收入310W '); INSRT INTO `tbl_book` VALUES (13, 'Spring实战第5版', '计算机理论', 'Spring入门经典教程,深入理解Spring原理技术内幕');
INSERT INTO `tbl_book` VALUES (14, 'Spring 5核心原理与30个类手写实战', '计算机理论', '十年沉淀之作,写Spring精华思想');
SET FOREIGN_KEY_CHECKS = 1;
P29 基础篇-26-SpringBoot整合MyBatis常见问题处理
6. 整合mybatis-plus
springboot_06_mybatis_plus
P30 基础篇-27-SpringBoot整合MyBatisPlus
7. 整合druid数据库连接池
springboot_07_druid
P31 基础篇-28-SpringBoot整合Druid
8. springboot基础篇综合案例
springboot_08_ssmp
P32 基础篇-29-SSMP整合案例制作分析
P33 基础篇-30-模块创建
P34 基础篇-31-实体类快速开发(lombok)
P35 基础篇-32-数据层标准开发(基础CRUD)
P36 基础篇-33-开启MP运行日志
P38 基础篇-35-数据层标准开发(条件查询)
P39 基础篇-36-业务层标准开发(基础CRUD)
P40 基础篇-37-业务层快速开发(基于MyBatisPlus构建)
P41 基础篇-38-表现层标准开发
P42 基础篇-39-表现层数据一致性处理(R对象)
P43 基础篇-40-前后端调用(axios发送异步请求)
P44 基础篇-41-列表功能
P45 基础篇-42-添加功能
P46 基础篇-43-删除功能
P47 基础篇-44修改功能(加载数据)
P48 基础篇-45-修改功能
P49 基础篇-46-异常消息处理
P50 基础篇-47-分页
P51 基础篇-48-分页功能维护(删除BUG)
P52 基础篇-49-条件查询
P53 基础篇-50-基础篇完结
二、运维实用篇
P54 运维实用篇-51-工程打包与运行
P55 运维实用篇-52-打包插件
P56 运维实用篇-53-Boot工程快速启动(Linux版)
# 后台启动springboot项目jar包
nohup java -jar springboot_08_ssmp-0.0.1-SNAPSHOT.jar > server.log 2>&1 &
# 终止程序
# 查看java -jar 命令对应的进程号
ps -ef | grep "java -jar"
# 终止对应进程
kill -9 <pid>
P57 运维实用篇-54-临时属性
P58 运维实用篇-55-临时属性(开发环境)
P59 运维实用篇-56-配置文件4级分类
9. 使用自定义配置
springboot_09_config
P60 运维实用篇-57-自定义配置文件
- 方法1:设置程序参数,指定文件名(不包含后缀),如:
--spring.config.name=ebank
- 方法2:指定配置文件的路径,如:
--spring.config.location=classpath:/ebank.yml
- 还可以指定多个配置文件,如:
--spring.config.location=classpath:/ebank.yml,classpath:/ebank-server.yml
10. 多环境配置
springboot_10_profiles
P61 运维实用篇-58-多环境开发(yaml版)
单文件版:
application.yml
文件:
# 应用环境
# 公共配置
spring:
profiles:
active: dev
---
# 生产环境
spring:
config:
activate:
on-profile: pro
server:
port: 80
---
spring:
config:
activate:
on-profile: dev
server:
port: 8080
---
# 测试环境
spring:
config:
activate:
on-profile: test
server:
port: 8888
P62 运维实用篇-59-多环境开发多文件版(yaml版)
yaml多文件版
application.yml
# 应用环境
# 公共配置
spring:
profiles:
active: test
application-dev.yml
# 开发环境
server:
port: 8080
application-pro.yml
# 生产环境
server:
port: 80
application-test.yml
# 测试环境
server:
port: 8888
P63 运维实用篇-60-多环境开发多文件版(properties版)
properties多文件版
application.properties
# 应用环境
spring.profiles.active=test
application-dev.properties
# 开发环境
server.port=80
application-pro.properties
# 生产环境
server.port=8080
application-test.properties
# 测试环境
server.port=8888
P64 运维实用篇-61-多环境分组管理
使用group属性配置同组的配置文件,active可以直接使一组配置文件同时生效
application.yml
#spring:
# profiles:
# active: dev
# include: devDB, devMVC
spring:
profiles:
active: dev
group:
"dev": devDB, devMVC
"pro": proDB, proMVC
"test": testDB, TestMVC
application-dev.yml
server:
port: 80
application-devDB.yml
server:
port: 81
application-devMVCyml
server:
servlet:
context-path: /ebank
port: 82
观察启动日志,配置文件加载的顺序为 : The following profiles are active: dev,devDB,devMVC
P65 运维实用篇-62-多环境开发控制
maven
中使用多环境,然后在springboot
中读取maven
中的配置
pom.xml
中的配置
<!--设置多环境-->
<profiles>
<profile>
<id>env_dev</id>
<properties>
<profile.active>dev</profile.active>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>env_pro</id>
<properties>
<profile.active>pro</profile.active>
</properties>
</profile>
<profile>
<id>env_test</id>
<properties>
<profile.active>test</profile.active>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
</profiles>
application.yml
中的配置
#spring:
# profiles:
# active: dev
# include: devDB, devMVC
spring:
profiles:
active: @profile.active@
group:
"dev": devDB, devMVC
"pro": proDB, proMVC
"test": testDB, TestMVC
11. 记录日志
springboot_11_log
P66 运维实用篇-63-日志基础操作
P67 运维实用篇-64-教你一招:快速创建日志对象
P68 运维实用篇-65-日志输出格式控制
P69 运维实用篇-66-文件记录日志(运维实用篇完结)
三、开发实用篇
12. 热部署
springboot_12_hot_deploy
P70 开发实用篇-67-手工启动热部署
想要拥有热部署的功能,需要在pom.xml文件中添加如下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
P71 开发实用篇-68-自动启动热部署
自动启动热部署,即,CTRL + F9
编译的操作操作由程序自动完成,需要进行如下配置:
- 在设置中勾选
- 按快捷键
CTRL + SHIFT + ALT + /
,在弹出的菜单中选择第一项,注册表,或者直接按CTRL + ALT + M
调出注册表;
- 在注册表项中勾选
compiler.automake.allow.when.app.running
- idea新版本
2021.3.1
中在注册表中找不到改选项,需要在高级设置中进行设置。
P72 开发实用篇-69-热部署范围配置
P73 开发实用篇-70-关闭热部署功能
application.yml
spring:
# 热部署范围配置
devtools:
restart:
# 设置不参与热部署的文件和文件夹(即修改后不重启)
exclude: static/**,public/**,config/application.yml
#是否可用
enabled: false
如果配置文件比较多的时候找热部署对应配置比较麻烦,可以在springboot
启动类的main方法中设置,此处设置的优先级将比配置文件高,一定会生效。
System.setProperty("spring.devtools.restart.enabled", "false");
13. 属性绑定
springboot_13_configuration
P74 实用开发篇-71-第三方bean属性绑定
-
先在要配置的类上面加
@Component
注解将该类交由spring
容器管理; -
@ConfigurationProperties(prefix="xxx")
,xxx
跟application.yml
配置文件中的属性对应; -
如果多个配置类想统一管理也可以通过
@EnableConfigurationProperties({xxx.class, yyy.class})
的方式完成配置,不过该注解会与@Component
配置发生冲突,二选一即可; -
第三方类对象想通过配置进行属性注入,可以通过创建一个方法,在方法体上加
@Bean
和@ConfigurationProperties(prefix="xxx")
注解,然后方法返回这个第三方对象的方式。 -
使用
@ConfigurationProperties(prefix="xxx")
注解后idea
工具会报一个警告Spring Boot Configuration Annotation Processor not configured
只需要在
pom.xml
中加上如下依赖刷新即可消除该警告<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> </dependency>
P75 实用开发篇-72-松散绑定
@ConfigurationProperties
绑定属性支持属性名宽松绑定,又叫松散绑定。
比如要将ServerConfig.class
作为配置类,并通过配置文件application.yml
绑定属性
ServerConfig.class
@Component
@ConfigurationProperties(prefix = "serverconfig")
@Data
public class ServerConfig {
private String ipAddress;
private int port;
private long timeout;
}
application.yml
serverConfig:
# ipAddress: 192.168.0.1 # 驼峰模式
# ipaddress: 192.168.0.1
# IPADDRESS: 192.168.0.1
ip-address: 192.168.0.1 # 主流配置方式,烤肉串模式
# ip_address: 192.168.0.1 # 下划线模式
# IP_ADDRESS: 192.168.0.1 # 常量模式
# ip_Add_rEss: 192.168.0.1
# ipaddress: 192.168.0.1
port: 8888
timeout: -1
以ipAddress
属性为例,上面的多种配置方式皆可生效,这就是松散绑定。而@Value
不支持松散绑定,必须一一对应。
@ConfigurationProperties(prefix="serverconfig")
中的prefix
的值为serverconfig
或者server-config
,如果是serverConfig
就会报错,这与松散绑定的前缀命名规范有关:仅能使用纯小写字母、数字、中划线作为合法的字符
P76 实用开发篇-73-常用计量单位应用
//@Component
@ConfigurationProperties(prefix = "server-config")
@Data
public class ServerConfig {
private String ipAddress;
private int port;
@DurationUnit(ChronoUnit.MINUTES)
private Duration timeout;
@DataSizeUnit(DataUnit.MEGABYTES)
private DataSize dataSize;
}
P77 实用开发篇-74-bean属性校验
引入Bean
属性校验框架的步骤:
-
在
javax.validation validation-api org.hibernate.validator hibernate-validatorpom.xml
中添加JSR303
规范和hibernate
校验框架的依赖: -
在要校验的类上加
@Validated
注解 -
设置具体的校验规则,如:
@Max(value=8888, message="最大值不能超过8888")
@ConfigurationProperties(prefix = “server-config”) @Data // 2.开启对当前bean的属性注入校验 @Validated public class ServerConfig { private String ipAddress; // 设置具体的规则 @Max(value = 8888, message = “最大值不能超过8888”) @Min(value = 1000, message = “最小值不能低于1000”) private int port; @DurationUnit(ChronoUnit.MINUTES) private Duration timeout;
@DataSizeUnit(DataUnit.MEGABYTES) private DataSize dataSize;
}
P78 实用开发篇-75-进制数据转换规则
进制转换中的一些问题:
如application.yml
文件中对数据库有如下配置:
datasource:
driverClassName: com.mysql.cj.jdbc.Driver123
# 不加引号读取的时候默认解析为了8进制数,转成十进制就是87
# 所以想让这里正确识别,需要加上引号
# password: 0127
password: "0127"
14. 测试相关
P79 实用开发篇-76-加载测试专用属性
springboot_14_test
@SpringBootTest
注解中可以设置properties
和args
属性,这里的args
属性的作用跟idea
工具中自带的程序参数类似,只不过这里的配置是源码级别的,会随着源码的移动而跟随,而idea中的程序参数的配置会丢失。并且这里的args
属性的配置的作用范围比较小,仅在当前测试类生效。
application.yml
test:
prop: testValue
PropertiesAndArgsTest.java
// properties属性可以为当前测试用例添加临时的属性配置
//@SpringBootTest(properties = {"test.prop=testValue1"})
// args属性可以为当前测试用例添加临时的命令行参数
//@SpringBootTest(args = {"--test.prop=testValue2"})
// 优先级排序: args > properties > 配置文件
@SpringBootTest(args = {"--test.prop=testValue2"}, properties = {"test.prop=testValue1"})
class PropertiesAndArgsTest {
@Value("${test.prop}")
private String prop;
@Test
public void testProperties() {
System.out.println("prop = " + prop);
}
}
P80 实用开发篇-77-加载测试专用配置
某些测试类中需要用到第三方的类,而其他测试类则不需要用到,这里可以在类上加载@Import({xxx.class, yyy.class})
P81 实用开发篇-78-测试类中启动web环境
P82 实用开发篇-79-发送虚拟请求
P83 实用开发篇-80-匹配响应执行状态
P84 实用开发篇-81-匹配响应体
P85 实用开发篇-82-匹配响应体(json)
P86 实用开发篇-83-匹配响应头
表现层BookController.java
@RestController
@RequestMapping("/books")
public class BookController {
/*@GetMapping("/{id}")
public String getById(@PathVariable int id) {
System.out.println("id = " + id);
return "getById...";
}*/
@GetMapping("/{id}")
public Book getById(@PathVariable int id) {
System.out.println("id = " + id);
Book book = new Book();
book.setId(5);
book.setName("神秘岛");
book.setType("科幻");
book.setDescription("《神秘岛》是法国科幻小说家儒勒·凡尔纳创作的长篇小说,是他写的三部曲之一。");
return book;
}
}
对应的测试类WebTest.java
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
// 开启虚拟mvc调用
@AutoConfigureMockMvc
public class WebTest {
@Test
public void testRandomPort() {
}
@Test
public void testWeb(@Autowired MockMvc mvc) throws Exception {
// 创建虚拟请求,当前访问 /books
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books/5");
mvc.perform(builder);
}
@Test
public void testStatus(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books1/6");
ResultActions action = mvc.perform(builder);
// 设定预期值,与真实值进行比较,成功测试通过,失败测试不通过
// 定义本次调用的预期值
StatusResultMatchers srm = MockMvcResultMatchers.status();
// 预计本次调用成功的状态码:200
ResultMatcher ok = srm.isOk();
// 添加预计值到本次调用过程中进行匹配
action.andExpect(ok);
}
@Test
public void testBody(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books/6");
ResultActions action = mvc.perform(builder);
// 设定预期值,与真实值进行比较,成功测试通过,失败测试不通过
// 定义本次调用的预期值
ContentResultMatchers crm = MockMvcResultMatchers.content();
// 预计本次调用成功的状态码:200
ResultMatcher rm = crm.string("getById...");
// 添加预计值到本次调用过程中进行匹配
action.andExpect(rm);
}
@Test
public void testJson(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books/7");
ResultActions action = mvc.perform(builder);
// 设定预期值,与真实值进行比较,成功测试通过,失败测试不通过
// 定义本次调用的预期值
ContentResultMatchers jsonMatcher = MockMvcResultMatchers.content();
ResultMatcher rm = jsonMatcher.json("{"id":5,"name":"神秘岛","type":"科幻","description":"《神秘岛》是法国科幻小说家儒勒·凡尔纳创作的长篇小说,是他写的三部曲之一。1"}");
action.andExpect(rm);
}
@Test
public void testContentType(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books/7");
ResultActions action = mvc.perform(builder);
// 设定预期值,与真实值进行比较,成功测试通过,失败测试不通过
// 定义本次调用的预期值
HeaderResultMatchers hrm = MockMvcResultMatchers.header();
ResultMatcher rm = hrm.string("Content-Type", "application/json");
action.andExpect(rm);
}
@Test
// 完整测试
public void testGetById(@Autowired MockMvc mvc) throws Exception {
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books/8");
ResultActions action = mvc.perform(builder);
// 1、比较状态码
StatusResultMatchers statusResultMatchers = MockMvcResultMatchers.status();
ResultMatcher statusResultMatcher = statusResultMatchers.isOk();
action.andExpect(statusResultMatcher);
// 2、比较返回值类型
HeaderResultMatchers headerResultMatchers = MockMvcResultMatchers.header();
ResultMatcher headerResultMatcher = headerResultMatchers.string("Content-Type", "application/json");
action.andExpect(headerResultMatcher);
/// 3、比较json返回值
ContentResultMatchers contentResultMatchers = MockMvcResultMatchers.content();
ResultMatcher jsonResultMatcher = contentResultMatchers.json("{"id":5,"name":"神秘岛","type":"科幻","description":"《神秘岛》是法国科幻小说家儒勒·凡尔纳创作的长篇小说,是他写的三部曲之一。"}");
action.andExpect(jsonResultMatcher);
}
}
P87 实用开发篇-84-业务层测试事务回滚
测试过程中对数据库的增删改操作的影响是否回滚,由下面两个注解控制,需要在测试类上加:
@Transactional
,@Rollback(value=true)
:回滚,@Rollback(value=true)
为默认值,也可以省略;
@Transactional
,@Rollback(value=false)
:不回滚,跟什么注解都不加的效果一样。
P88 实用开发篇-85-测试用例设置随机数据
可以把测试用例中的属性值都按照一定规则设置成随机值,可以让测试结果更具有普适性。并且可以把测试用例的属性的随机规则写在配置文件中,方便更改。
application.yml
testcase:
randomBook:
id: ${random.int}
id2: ${random.int(10)} # 生成10以内的整数
type: ${random.int(5, 10)} # 生成5-10之间的整数
name: 糖果墙${random.value}
uuid: ${random.uuid}
publicTime: ${random.long}
BookCase.java
@Data
@Component
@ConfigurationProperties(prefix = "test-case.random-book")
public class BookCase {
private Integer id;
private Integer id2;
private String type;
private String name;
private String uuid;
private Long publishTime;
}
15. 数据层解决方案
springboot_15_01_datasource
P89 实用开发篇-86-内置数据源
在springboot
项目中使用Druid
数据源,需要先在pom.xml
中加上Druid
的依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
然后在application.yml
中有两种配置方法,两种方法实现的效果一样
# 配法1:
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC
username: root
password: 123
# 配法2:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC
username: root
password: 123
type: com.alibaba.druid.pool.DruidDataSource # 去掉type属性,Druid数据源依然会启用
配法2去掉type
属性后,再启动项目,发现Druid
数据源依然启用了,这是由于引入了Druid
数据源的依赖后,springboot
会自动配置Druid
。
如果不引入Druid
数据源的依赖,springboot
默认的数据源是Hikari
数据源
# 默认为Hikari数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC
username: root
password: 123
# Hikari数据源详细配置,这里需要注意url需要和Hikari属性并列,而Hikari下的jdbc-url无效
spring:
datasource:
url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC
hikari:
# jdbc-url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC # 此项无效
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123
maximum-pool-size: 50
minimum-idle: 30
idle-timeout: 30000
springboot_15_02_jdbc_template
P90 实用开发篇-87-JdbcTemplate
使用JdbcTemplate
-
在
pom.xml
中加入相关依赖 -
application.yml
中配置数据源,另外还可以对JdbcTemplate
进行一些简单的配置# 配置数据库和连接池 spring: datasource: url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC hikari: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: TGQ@candywall123 maximum-pool-size: 50 minimum-idle: 30 # idle-timeout: 30000 # JdbcTemplate的一些配置 jdbc: template: query-timeout: 30s # 指定查询超时时间 max-rows: 500 # 最大查询条数 fetch-size: 500 # 数据条数比较多的时候,一次拿多少条数据
-
测试类
@SpringBootTest class JdbcTemplateApplicationTests { @Autowired private JdbcTemplate jdbcTemplate; @Test void testJdbcTemplateSelect() { String sql = "select * from tbl_book"; // List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql); // System.out.println(maps); RowMapper<Book> rm = new RowMapper<Book>() { @Override public Book mapRow(ResultSet rs, int rowNum) throws SQLException { Book book = new Book(); book.setId(rs.getInt("id")); book.setName(rs.getString("name")); book.setType(rs.getString("type")); book.setDescription(rs.getString("description")); return book; } }; List<Book> bookList = jdbcTemplate.query(sql, rm); bookList.forEach(System.out::println); } @Test void testJdbcTemplateInsert() { String sql = "insert into tbl_book values(null, ?, ?, ?)"; jdbcTemplate.update(sql, "springboot1", "springboot2", "springboot3"); } }
springboot_15_03_h2
P91 实用开发篇-88-H2数据库
使用H2
数据库
-
在
pom.xml
中加入相关依赖 -
在
application.yml
中配置数据源并且启用H2
数据库的控制台server: port: 80 spring: # 配置数据库和连接池 datasource: url: jdbc:h2:~/test hikari: driver-class-name: org.h2.Driver username: sa password: 123456 # 启用H2的控制台 h2: console: enabled: true path: /h2
-
在浏览器中输入访问
H2
控制台地址 -
输入默认密码:
123456
,然后点连接,会跳转到控制台主页 -
可以写
sql
语句建表,并插入几条数据create table tbl_book( id int, type varchar, name varchar, description varchar ); insert into tbl_book values(1, 'springboot1', 'springboot2', 'springboot3'); insert into tbl_book values(2, 'springboot4', 'springboot5', 'springboot6'); insert into tbl_book values(3, 'springboot7', 'springboot8', 'springboot9'); insert into tbl_book values(4, 'springboot10', 'springboot11', 'springboot12');
查询
tbl_book
表中的数据select * from tbl_book;
-
写代码连接
H2
数据库,需要注意启动测试类连接H2数据库的时候需要将之前的H2控制台的springboot
程序先停止,否则会造成端口占用,测试类报错。测试类:
@SpringBootTest class H2ApplicationTests { @Autowired private JdbcTemplate jdbcTemplate; @Test public void testH2Select() { String sql = "select * from tbl_book"; RowMapper<Book> rm = new RowMapper<Book>() { @Override public Book mapRow(ResultSet rs, int rowNum) throws SQLException { Book book = new Book(); book.setId(rs.getInt("id")); book.setName(rs.getString("name")); book.setType(rs.getString("type")); book.setDescription(rs.getString("description")); return book; } }; List<Book> bookList = jdbcTemplate.query(sql, rm); bookList.forEach(System.out::println); } @Test public void testH2Save() { String sql = "insert into tbl_book values(?, ? ,? ,?)"; jdbcTemplate.update(sql, 5, "啊哈算法", "计算机", "数据结构和算法"); } }
-
H2
数据库线上运行时请务必关闭。bind 0.0.0.0 protected-mode no port 6379 timeout 0 save 900 1 # 900s内至少一次写操作则执行bgsave进行RDB持久化 save 300 10 save 60 10000 rdbcompression yes dbfilename dump.rdb dir /data appendonly yes appendfsync everysec requirepass 12345678
springboot_15_04_redis
P92 实用开发篇-89-redis下载安装与基本使用
-
windows
版下载地址:https://github.com/tporadowski/redis/releases -
linux
版下载地址:https://redis.io/ -
在
linux
上安装redis除了最原始的方法外,推荐使用docker-compose
一键启动redis
,非常方便
redis.conf
bind 0.0.0.0
protected-mode no
port 6379
timeout 0
save 900 1 # 900s内至少一次写操作则执行bgsave进行RDB持久化
save 300 10
save 60 10000
rdbcompression yes
dbfilename dump.rdb
dir /data
appendonly yes
appendfsync everysec
requirepass 123456
docker-compose.yml
version: '3'
services:
redis:
image: redis:latest
container_name: redis
restart: always
ports:
- 6379:6379
volumes:
- ./redis.conf:/etc/redis/redis.conf:rw
- ./data:/data:rw
command: ["redis-server","/etc/redis/redis.conf"]
我的redis
安装在虚拟机中的linux系统上,可以通过以下两种方式测试连通性:
-
通过
windows
版redis
自带的redis-cli
来远程连接linux
上的redis服务,指令如下:redis-cli.exe -h 192.168.0.110 -p 6379 -a “123456” # 123456是密码
-
还可以使用
AnotherRedisDesktopManager
:这是一款非常稳定并且拥有美观的图形界面的redis客户端,操作起来也是相当简单,一看就会用,下载地址:https://github.com/qishibo/AnotherRedisDesktopManager/releases
P93 实用开发篇-90-SpringBoot整合Redis
-
在
pom.xml
中加入spring
整合redis
的依赖<!--springboot整合redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
-
application.yml
spring: # redis配置 redis: host: 192.168.0.110 port: 6379 password: 123456
-
测试类
@SpringBootTest class RedisApplicationTests { @Autowired private StringRedisTemplate redisTemplate; @Test public void testSet() { ValueOperations valueOperations = redisTemplate.opsForValue(); valueOperations.set("age", "41"); } @Test public void testGet() { ValueOperations valueOperations = redisTemplate.opsForValue(); System.out.println("age = " + valueOperations.get("age")); System.out.println("username = " + valueOperations.get("username")); } }
注:这里如果使用
RedisTemplate
而不使用StringRedisTemplate
,去redis
客户端里面查看会发现键值包含?ít特殊字符,这是由于RedisTemplate<K, V>
模板类在操作redis
时默认使用JdkSerializationRedisSerializer
来进行序列化。而存取序列化的方式从org.springframework.data.redis.serializer.JdkSerializationRedisSerializer
将序列化的方式改为
org.springframework.data.redis.serializer.StringRedisSerializer
会自动去掉?ít
前缀,所以有两种解决方法:-
直接使用
StringRedisTemplate
; -
方案2 修改默认的序列化方式:
private RedisTemplate redisTemplate; @Autowired(required = false) public void setRedisTemplate(RedisTemplate redisTemplate) { RedisSerializer stringSerializer = new StringRedisSerializer(); redisTemplate.setKeySerializer(stringSerializer); redisTemplate.setValueSerializer(stringSerializer); redisTemplate.setHashKeySerializer(stringSerializer); redisTemplate.setHashValueSerializer(stringSerializer); this.redisTemplate = redisTemplate; }
-
P94 实用开发篇-91-SpringBoot读写Redis的客户端
P95 实用开发篇-92-SpringBoot操作Redis客户端实现技术切换(jedis)
java
操作redis
底层有两种实现分别为lettuce和jedis,其中lettuce
为springboot
的RedisTemplate
默认使用的技术。如果想要切换到jedis:
-
引入
jedis
的jar包 -
在
application.yml
中加入配置spring: # redis配置 redis: host: 192.168.0.110 port: 6379 password: 123456 client-type: jedis # 默认为lettuce # 还可以进一步配置 jedis: pool: enabled: true max-active: 16 min-idle: 8
springboot_15_05_mongodb
P96 实用开发篇-93-Mongodb简介
P97 实用开发篇-94-Mongodb下载与安装
解压mongodb安装包,然后在软件根目录下新建datadb,进入到bin目录下启动黑窗口,输入如下命令,启动mongodb数据库,并指定数据保存到datadb目录下。
.mongod.exe --dbpath=..datadb
在bin目录下再开一个黑窗口,然后输入
.mongo.exe
会默认连接ip
为localhost
,port
为27017
的mongodb
服务,连接成功会输出mongodb的版本等信息。
P98 实用开发篇-95-Mongodb基础操作
由于黑窗口操作较为繁琐,这里推荐使用带图形化界面的客户端robo3t
,启动robo3t
,创建一个连接
打开这个连接
右击左侧连接名,在右键菜单中选择Create Database
,新建一个数据库
填写数据库名称
展开数据库名,右击Collections
再新建一个Collection
填写collection
名称
右击新建的Collection book
,会弹出一个查询界面,可以在文本框中填写指令对Collection book
进行操作
// 查询所有
db.getCollection('book').find({})
// 可以简写为
db.book.find();
// 条件查询
db.book.find({type: "springboot"})
// 保存文档
db.book.save({"name": "springboot", type: "springboot"})
// 删除操作
db.book.remove({type: "springboot"});
// 修改操作
// 修改满足条件的第一条数据
db.book.update({name: "springboot"}, {$set:{name: "springboot2"}});
// 修改满足条件的所有数据
db.book.updateMany({name: "springboot"}, {$set:{name: "springboot2"}});
P99 实用开发篇-96-SpringBoot整合Mongodb
在pom.xml
中加入springboot
整合MongoDB
的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
在application.yml
中进行MongoDB
的配置
spring:
data:
mongodb:
uri: mongodb://localhost/springboot_mongodb
测试代码
@SpringBootTest
class MongodbApplicationTests {
@Autowired
private MongoTemplate mongoTemplate;
@Test
void testSave() {
Book book = new Book();
book.setId(2);
book.setName("springboot2");
book.setType("springboot2");
book.setDescription("springboot2");
mongoTemplate.save(book);
}
@Test
public void testFindAll() {
List<Book> books = mongoTemplate.findAll(Book.class);
books.forEach(System.out::println);
}
}
springboot_15_06_elasticsearch
P100 实用开发篇-97-ES简介
P101 实用开发篇-98-ES下载与安装
-
下载地址:https://www.elastic.co/cn/start
-
安装
解压
es
的安装包,然后去bin目录下双击elasticsearch.bat
启动es
服务器,然后就可以去浏览器输入http://localhost:9200/
如果有正常的json
返回值,那么说明es
启动正常
P102 实用开发篇-99-ES索引操作
-
安装IK分词器插件
我们希望
es
再新建索引的时候应用分词效果,所以需要先给es
安装IK
分词器插件下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases
在
es
的plugin
目录下新建一个ik文件夹(建文件夹是为了方便管理),然后把下载好的ik
分词器压缩包中的内容解压到ik目录下
关闭当前es
启动的黑窗口,去es
的bin目录下,双击elasticsearch.bat
,重新启动es
-
新建索引
打开
postman
,发送一个PUT
请求,新建一个books
索引请求参数
{ "mappings": { "properties": { "id": { "type": "keyword" }, "name": { "type": "text", "analyzer": "ik_max_word", "copy_to": "all" }, "type": { "type": "keyword" }, "description": { "type": "text", "analyzer": "ik_max_word", "copy_to": "all" }, "all": { "type": "text", "analyzer": "ik_max_word" } } } }
返回如下提示就表示新建索引成功
- 查询索引
-
删除索引
P103 实用开发篇-100-ES文档操作
-
新建文档
有3种请求方式:
方式1:
http://localhost:9200/books/_doc
方式2:http://localhost:9200/books/_doc/221432414
,其中221432414
是文档中的_id
属性,如果不指定,则随机生成
方式3:http://localhost:9200/books/_create/221432414
,其中221432414
是文档中的_id
属性,这里不指定会报错
注:如果在新建的过程中出现[TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark, index has read-only-allow-delete block]
的问题,可以发送下面的请求解决
如果上面的方法也不能解决,检查一下自己的es
安装目录所在磁盘的可用空间是否太小,默认必须大于5%
才可以,比如磁盘空间500G, 需要至少25G
的可用空间才可以。后来清了磁盘大于5%
也不行,后来用这个方法解决了:Elasticsearch flood stage disk watermark exceeded
-
查询文档
- 查询全部文档
- 查询单个文档
-
按条件查询
-
删除文档
-
修改文档
将
_id
为221432414
的文档的name
修改的值修改为springboot 非常好
先查询一下
发起请求执行修改操作,这里请求体里面只填写要修改的属性
再查询一下
发现修改后的文档,另外两个没有修改的属性没有了,这不是期望的效果,这种请求的修改方式是全覆盖方式的修改。
如果要想只修改文档中
name
属性的值,需要使用新的请求方式(操作之前先将_id
为221432414
的文档数据恢复一下)注意:这里发送的是
POST
请求,而上面的全量修改发送的是PUT
请求修改之后再查询一下
P104 实用开发篇-101-SpringBoot整合ES客户端操作
P105 实用开发篇-102-添加文档
P106 实用开发篇-103-查询文档
参考整合h2
、redis
、mongodb
的方式,整合es的时候应该先在pom.xml
中加入spring
整合es
的依赖,spring-boot-starter-data-elasticsearch
,然后再去application.yml
中编写es的配置,最后再去测试类里面注入es的template
对象,进行相关操作。可是需要注意的是,springboot
整合es有两套整合方案,一个是整合低级别的es
客户端,另一个是整合高级别的es
客户端。而开头所说的是springboot
整合低级别的方式,这里不采用这种方式。直接整合高级别的es
客户端,分为以下几个步骤:
-
在
pom.xml
中加入es
的依赖,由于测试的过程中还要用到对象转json
字符串,所以这里把json
解析的依赖也一同加上<dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency>
-
整合高级别的
es
客户端,意味着springboot
没有提供默认的配置,所以就不能在application.yml
中配置有关访问es
客户端的url
等参数了,这里直接编写测试类,采用硬编码的方式指定这些参数。@SpringBootTest class ElasticsearchApplicationTests { // @Autowired // private ElasticsearchRestTemplate esTemplate; @Autowired private BookMapper bookMapper; private RestHighLevelClient client; // json转换工具 private static ObjectMapper objectMapper; @Test public void testConnect() throws IOException { HttpHost host = HttpHost.create("http://localhost:9200"); RestClientBuilder builder = RestClient.builder(host); client = new RestHighLevelClient(builder); client.close(); } @Test // 创建索引 public void testCreateIndex() throws IOException { CreateIndexRequest request = new CreateIndexRequest("books"); client.indices().create(request, RequestOptions.DEFAULT); } @Test // 使用IK分词器创建索引 public void testCreateIndexByIK() throws IOException { CreateIndexRequest request = new CreateIndexRequest("books"); // 设置请求参数 String jsonParam = "{ " + " "mappings": { " + " "properties": { " + " "id": { " + " "type": "keyword" " + " }, " + " "name": { " + " "type": "text", " + " "analyzer": "ik_max_word", " + " "copy_to": "all" " + " }, " + " "type": { " + " "type": "keyword" " + " }, " + " "description": { " + " "type": "text", " + " "analyzer": "ik_max_word", " + " "copy_to": "all" " + " }, " + " "all": { " + " "type": "text", " + " "analyzer": "ik_max_word" " + " } " + " } " + " } " + "}"; request.source(jsonParam, XContentType.JSON); client.indices().create(request, RequestOptions.DEFAULT); } @Test public void testCreateDoc() throws Exception { Book book = bookMapper.selectById(1); IndexRequest request = new IndexRequest("books").id(book.getId() + ""); String jsonParams = objectMapper.writeValueAsString(book); request.source(jsonParams, XContentType.JSON); client.index(request, RequestOptions.DEFAULT); } @Test // 将mysql数据库tbl_book表中的数据都存到es中 public void testCreateDocAllFromMySQL() throws Exception { List<Book> books = bookMapper.selectList(null); // 批处理请求,相当于一个request容器,可以把单个请求加进来 BulkRequest requests = new BulkRequest(); for (Book book : books) { IndexRequest request = new IndexRequest("books").id(book.getId() + ""); String jsonParams = objectMapper.writeValueAsString(book); request.source(jsonParams, XContentType.JSON); requests.add(request); } client.bulk(requests, RequestOptions.DEFAULT); } @Test // 按ID查询 public void testGet() throws IOException { GetRequest request = new GetRequest("books", "1"); GetResponse response = client.get(request, RequestOptions.DEFAULT); System.out.println("book:" + response.getSourceAsString()); } @Test public void testSearch() throws IOException { SearchRequest request = new SearchRequest("books"); SearchSourceBuilder builder = new SearchSourceBuilder(); builder.query(QueryBuilders.termQuery("all", "spring")); request.source(builder); SearchResponse response = client.search(request, RequestOptions.DEFAULT); SearchHits hits = response.getHits(); for (SearchHit hit : hits) { System.out.println(hit.getSourceAsString()); } } @BeforeEach void setUp() { objectMapper = new ObjectMapper(); HttpHost host = HttpHost.create("http://localhost:9200"); RestClientBuilder builder = RestClient.builder(host); client = new RestHighLevelClient(builder); } @AfterEach void tearDown() throws IOException { client.close(); } }
16. 整合第三方技术
16.1 缓存
springboot_16_01_01_cache_book_quickstart
P107 实用开发篇-104-缓存的作用
P108 实用开发篇-105-Spring缓存使用方式
先自己用java
的HashMap
模拟一个缓存
BookController.java
@GetMapping("{id}")
// 模拟缓存
public R getById(@PathVariable Integer id) {
return new R(true, bookService.getById(id));
}
BookServiceImpl.java
@Override
// 模拟缓存
private HashMap<Serializable, Book> cache = new HashMap<>();
public Book getById(Serializable id) {
// 如果当前缓存中没有本次要查询的数据,则进行查询,否则直接从缓存中获取数据返回
Book book = cache.get(id);
if (book == null) {
book = super.getById(id);
cache.put(id, book);
}
return book;
}
springboot_16_01_02_cache_book_simple
使用spring
中自带的缓存技术
-
在
pom.xml
中添加如下依赖<!--cache--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
-
在
springboot
启动类上加@EnableCaching
注解 -
在业务层要使用缓存的方法上加上
@Cacheable(value = "cacheSpace", key = "#id")
注解,如下所示@Cacheable(value = "cacheSpace", key = "#id") public Book getById(Serializable id) { System.out.println("id = " + id); return super.getById(id); }
其中
value
表示缓存空间,key=“#id”
,表示将方法参数id
的值作为缓存中的一个key
。
springboot_16_01_03_cache_smscode_simple
P109 实用开发篇-106-手机验证码案例-生成验证码
-
手机验证码案例基础代码准备
-
存储手机号和验证码的实体类
SMSCode.java
@Data public class SMSCode { private String phone; private String code; }
-
生成验证码的工具类
CodeUtils.java
@Component public class CodeUtils { private final String padding = "000000"; // 生成验证码(位数少于6位左边填充0,填充方法1) public String generateCode(String phone) { int hash = phone.hashCode(); int encryption = 20228888; long result = hash ^ encryption; long nowTime = System.nanoTime(); result = (result ^ nowTime) % 1000000; String code = result + ""; // code = phone; // padding.substring(code.length()) code.length() // 6 // 0 5 // 00 4 // 000 3 // 000 3 // 0000 2 // 00000 1 // 000000 0 code = padding.substring(code.length()) + code; // System.out.println(code); return code; } private final String[] patch = {"000000", "00000", "0000", "000", "00", "0", ""}; // 生成验证码(位数少于6位左边填充0,填充方法2) public String generateCode1(String phone) { int hash = phone.hashCode(); int encryption = 20228888; long result = hash ^ encryption; long nowTime = System.nanoTime(); result = (result ^ nowTime) % 1000000; String code = result + ""; // code = phone; // patch[code.length] code.length() // 000000 0 // 00000 1 // 0000 2 // 000 3 // 00 4 // 0 5 // 6 code = patch[code.length()] + code; // System.out.println(code); return code; } // 根据手机号从缓存中获取验证码,缓存中有的话返回缓存中的值,没有的话就返回null @Cacheable(value = "smsCode", key = "#phone") public String getCodeByPhoneFromCache(String phone) { return null; } }
-
业务层接口
SMSCodeService.java
public interface SMSCodeService { String sendCodeToSMS(String phone); boolean checkCode(SMSCode smsCode); }
业务层接口实现类
SMSCodeServiceImpl.java
@Servic
-