2023年6月20日发(作者:)

⿊马程序员SpringBoot2全套教程学习笔记介绍注:四级标题和部分5级标题是使⽤⼦项⽬名称命名的,和我代码仓库的项⽬是⼀⼀对应的。每个⼦项⽬对应的视频链接以及⼀些重要内容的笔记⼀、基础篇1.⼊门案例springboot_01_01_quickstartspringboot_01_02_quickstartspringboot_01_03_quickstartspringboot_01_04_quickstart:更换SpringBoot内嵌的默认的web容器从tomcat换成jetty1.1 补充springboot_01_05_restspringboot_0x_02x_xxxxxxxx2. 基础配置springboot_02_base_configuration1. properties2. yml(主流格式)3. yaml三种格式共存,优先级从先到后为properties、yml、yaml3. yaml配置⽂件springboot_03_yaml4. 整合Junitspringboot_04_junit5. 整合mybatisspringboot_05_mybatis课程中使⽤到的数据库脚本829363738394647484950/* 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 (2, '格林童话', '童话', '睡前故事。');INSERT INTO `tbl_book` VALUES (3, 'Spring 5设计模式', '计算机理论', '深⼊Spring源码剖析Spring源码中蕴含的10⼤设计模式');INSERT INTO `tbl_book` VALUES (5, '轻量级Java Web企业应⽤实战', '计算机理论', '源码级剖析Spring框架,适合已掌握Java基础的读者');INSERT INTO `tbl_book` VALUES (7, '深⼊理解Java虚拟机', '计算机理论', '5个维度全⾯剖析JVM,⾯试知识点全覆盖');INSERT INTO `tbl_book` VALUES (8, 'Java编程思想(第4版)', '计算机理论', '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堂课轻松实现带货⽉⼊3W+'INSERT INTO `tbl_book` VALUES (13, 'Spring实战第5版', '计算机理论', 'Spring⼊门经典教程,深⼊理解Spring原理技术内幕');INSERT INTO `tbl_book` VALUES (14, 'Spring 5核⼼原理与30个类⼿写实战', '计算机理论', '⼗年沉淀之作,写Spring精华思想');SET FOREIGN_KEY_CHECKS = 1;INSERT INTO `tbl_book` VALUES (1, '三体', '科幻', '⼤刘的巅峰之作,将中国科幻推向世界舞台。总共分为三部曲:《地球往事》、《⿊暗森林》、《死神永⽣INSERT INTO `tbl_book` VALUES (4, 'Spring MVC+ MyBatis开发从⼊门到项⽬实战', '计算机理论', '全⽅位解析⾯向Web应⽤的轻量级框架,带你成为Spring MVINSERT INTO `tbl_book` VALUES (6, 'Java核⼼技术卷|基础知识(原书第11版)', '计算机理论', 'Core Java第11版,Jolt⼤奖获奖作品,针对Java SE9、10、 116. 整合mybatis-plusspringboot_06_mybatis_plus7. 整合druid数据库连接池springboot_07_druid8. springboot基础篇综合案例springboot_08_ssmp⼆、运维实⽤篇1234567#

后台启动springboot项⽬jar包nohup java -jar springboot_08_ > 2>&1 &#

终⽌程序#

查看java -jar

命令对应的进程号ps -ef | grep "java -jar"#

终⽌对应进程kill -9 9. 使⽤⾃定义配置springboot_09_config⽅法1:设置程序参数,指定⽂件名(不包含后缀),如:--=ebank⽅法2:指定配置⽂件的路径,如:--on=classpath:/还可以指定多个配置⽂件,如:--on=classpath:/,classpath:/10. 多环境配置springboot_10_profiles单⽂件版:⽂件:8#

应⽤环境#

公共配置spring: profiles: active: dev---#

⽣产环境spring: config: activate: on-profile: proserver: port: 80---spring: config: activate: on-profile: devserver: port: 8080---#

测试环境spring: config: activate: on-profile: testserver: port: 8888yaml多⽂件版12345#

应⽤环境#

公共配置spring: profiles: active: 123#

开发环境server: port: 123#

⽣产环境server: port: 123#

测试环境server: port: 8888properties多⽂件版ties12# 应⽤环境=ties12# 开发环境=ties12# ⽣产环境=ties12# 测试环境=8888使⽤group属性配置同组的配置⽂件,active可以直接使⼀组配置⽂件同时⽣效1112#spring:# profiles:# active: dev# include: devDB, devMVCspring: profiles: active: dev group: "dev": devDB, devMVC "pro": proDB, proMVC "test": testDB, 12server: port: 12server: port: 81application-devMVCyml1234server: servlet: context-path: /ebank port: 82观察启动⽇志,配置⽂件加载的顺序为 : The following profiles are active: dev,devDB,devMVCmaven中使⽤多环境,然后在springboot中读取maven中的配置中的配置8 env_dev <>dev true env_pro <>pro env_test <>test true 中的配置1112#spring:# profiles:# active: dev# include: devDB, devMVCspring: profiles: active: @@ group: "dev": devDB, devMVC "pro": proDB, proMVC "test": testDB, TestMVC11. 记录⽇志springboot_11_log三、开发实⽤篇12. 热部署springboot_12_hot_deploy想要拥有热部署的功能,需要在⽂件中添加如下依赖:12345 spring-boot-devtools true⾃动启动热部署,即,CTRL + F9编译的操作操作由程序⾃动完成,需要进⾏如下配置:1. 在设置中勾选2. 按快捷键CTRL + SHIFT + ALT + /,在弹出的菜单中选择第⼀项,注册表,或者直接按CTRL + ALT + M调出注册表;3. 在注册表项中勾选g4. idea新版本2021.3.1中在注册表中找不到改选项,需要在⾼级设置中进⾏设置。[外链图⽚转存失败,源站可能有防盗链机制,建议将图⽚保存下来直接上传(img-PA8pixRs-19)(C:UsersCandyWa)]12345678spring: #

热部署范围配置 devtools: restart: #

设置不参与热部署的⽂件和⽂件夹(即修改后不重启) exclude: static/**,public/**,config/ #是否可⽤ enabled: false如果配置⽂件⽐较多的时候找热部署对应配置⽐较⿇烦,可以在springboot启动类的main⽅法中设置,此处设置的优先级将⽐配置⽂件⾼,⼀定会⽣效。perty("d", "false");13. 属性绑定springboot_13_configuration1. 先在要配置的类上⾯加@Component注解将该类交由spring容器管理;2.

@ConfigurationProperties(prefix="xxx"),xxx跟配置⽂件中的属性对应;3. 如果多个配置类想统⼀管理也可以通过@EnableConfigurationProperties({, })的⽅式完成配置,不过该注解会与@Component配置发⽣冲突,⼆选⼀即可;4. 第三⽅类对象想通过配置进⾏属性注⼊,可以通过创建⼀个⽅法,在⽅法体上加@Bean和@ConfigurationProperties(prefix="xxx")注解,然后⽅法返回这个第三⽅对象的⽅式。5. 使⽤@ConfigurationProperties(prefix="xxx")注解后idea⼯具会报⼀个警告Spring Boot Configuration Annotation Processor not configured只需要在中加上如下依赖刷新即可消除该警告1234 spring-boot-configuration-processor@ConfigurationProperties绑定属性⽀持属性名宽松绑定,⼜叫松散绑定。⽐如要将作为配置类,并通过配置⽂件绑定属性12345678@Component@ConfigurationProperties(prefix = "serverconfig")@Datapublic class ServerConfig { private String ipAddress; private int port; private long timeout;}11serverConfig: # 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就会报错,这与松散绑定的前缀命名规范有关:仅能使⽤纯⼩写字母、数字、中划线作为合法的字符1112//@Component@ConfigurationProperties(prefix = "server-config")@Datapublic class ServerConfig { private String ipAddress; private int port; @DurationUnit(S) private Duration timeout; @DataSizeUnit(TES) private DataSize dataSize;}引⼊Bean属性校验框架的步骤:1. 在中添加JSR303规范和hibernate校验框架的依赖: tion validation-api tor hibernate-validator2. 在要校验的类上加@Validated注解3. 设置具体的校验规则,如:@Max(value=8888, message="最⼤值不能超过8888")@ConfigurationProperties(prefix = "server-config")@Data// 2.开启对当前bean的属性注⼊校验@Validatedpublic class ServerConfig { private String ipAddress; //

设置具体的规则 @Max(value = 8888, message = "最⼤值不能超过8888") @Min(value = 1000, message = "最⼩值不能低于1000") private int port; @DurationUnit(S) private Duration timeout; @DataSizeUnit(TES) private DataSize dataSize;}进制转换中的⼀些问题:如⽂件中对数据库有如下配置:123456datasource: driverClassName: 123 #

不加引号读取的时候默认解析为了8进制数,转成⼗进制就是87 #

所以想让这⾥正确识别,需要加上引号 # password: 0127 password: "0127"14. 测试相关springboot_14_test@SpringBootTest注解中可以设置properties和args属性,这⾥的args属性的作⽤跟idea⼯具中⾃带的程序参数类似,只不过这⾥的配置是源码级别的,会随着源码的移动⽽跟随,⽽idea中的程序参数的配置会丢失。并且这⾥的args属性的配置的作⽤范围⽐较⼩,仅在当前测试类⽣效。12test: prop: 11121314// properties属性可以为当前测试⽤例添加临时的属性配置//@SpringBootTest(properties = {"=testValue1"})// args属性可以为当前测试⽤例添加临时的命令⾏参数//@SpringBootTest(args = {"--=testValue2"})//

优先级排序: args > properties >

配置⽂件@SpringBootTest(args = {"--=testValue2"}, properties = {"=testValue1"})class PropertiesAndArgsTest { @Value("${}") private String prop; @Test public void testProperties() { n("prop = " + prop); }}某些测试类中需要⽤到第三⽅的类,⽽其他测试类则不需要⽤到,这⾥可以在类上加载@Import({, })表现层1718192021@RestController@RequestMapping("/books")public class BookController { /*@GetMapping("/{id}") public String getById(@PathVariable int id) { n("id = " + id); return ""; }*/ @GetMapping("/{id}") public Book getById(@PathVariable int id) { n("id = " + id); Book book = new Book(); (5); e("神秘岛"); e("科幻"); cription("《神秘岛》是法国科幻⼩说家儒勒·凡尔纳创作的长篇⼩说,是他写的三部曲之⼀。"); return book; }}对应的测试类8293637@SpringBootTest(webEnvironment = _PORT)//

开启虚拟mvc调⽤@AutoConfigureMockMvcpublic class WebTest { @Test public void testRandomPort() { } @Test public void testWeb(@Autowired MockMvc mvc) throws Exception { //

创建虚拟请求,当前访问 /books MockHttpServletRequestBuilder builder = ("/books/5"); m(builder); } @Test public void testStatus(@Autowired MockMvc mvc) throws Exception { MockHttpServletRequestBuilder builder = ("/books1/6"); ResultActions action = m(builder); //

设定预期值,与真实值进⾏⽐较,成功测试通过,失败测试不通过 //

定义本次调⽤的预期值 StatusResultMatchers srm = (); //

预计本次调⽤成功的状态码:200 ResultMatcher ok = (); //

添加预计值到本次调⽤过程中进⾏匹配 ect(ok); } @Test public void testBody(@Autowired MockMvc mvc) throws Exception { MockHttpServletRequestBuilder builder = ("/books/6"); ResultActions action = m(builder); //

设定预期值,与真实值进⾏⽐较,成功测试通过,失败测试不通过 //

定义本次调⽤的预期值 ContentResultMatchers crm = t(); //

预计本次调⽤成功的状态码:200 ResultMatcher rm = ("");373839464748495657585966676869767778798 ResultMatcher rm = (""); //

添加预计值到本次调⽤过程中进⾏匹配 ect(rm); } @Test public void testJson(@Autowired MockMvc mvc) throws Exception { MockHttpServletRequestBuilder builder = ("/books/7"); ResultActions action = m(builder); //

设定预期值,与真实值进⾏⽐较,成功测试通过,失败测试不通过 //

定义本次调⽤的预期值 ContentResultMatchers jsonMatcher = t(); ect(rm); } @Test public void testContentType(@Autowired MockMvc mvc) throws Exception { MockHttpServletRequestBuilder builder = ("/books/7"); ResultActions action = m(builder); //

设定预期值,与真实值进⾏⽐较,成功测试通过,失败测试不通过 //

定义本次调⽤的预期值 HeaderResultMatchers hrm = (); ResultMatcher rm = ("Content-Type", "application/json"); ect(rm); } @Test //

完整测试 public void testGetById(@Autowired MockMvc mvc) throws Exception { MockHttpServletRequestBuilder builder = ("/books/8"); ResultActions action = m(builder); // 1、⽐较状态码 StatusResultMatchers statusResultMatchers = (); ResultMatcher statusResultMatcher = (); ect(statusResultMatcher); // 2、⽐较返回值类型 HeaderResultMatchers headerResultMatchers = (); ResultMatcher headerResultMatcher = ("Content-Type", "application/json"); ect(headerResultMatcher); /// 3、⽐较json返回值 ContentResultMatchers contentResultMatchers = t(); ect(jsonResultMatcher); }} ResultMatcher rm = ("{"id":5,"name":"神秘岛","type":"科幻","description":"《神秘岛》是法国科幻⼩说家儒勒·凡尔纳创作的长篇⼩ ResultMatcher jsonResultMatcher = ("{"id":5,"name":"神秘岛","type":"科幻","description":"《神秘岛》是法国科幻⼩说测试过程中对数据库的增删改操作的影响是否回滚,由下⾯两个注解控制,需要在测试类上加:@Transactional,@Rollback(value=true):回滚,@Rollback(value=true)为默认值,也可以省略;@Transactional,@Rollback(value=false):不回滚,跟什么注解都不加的效果⼀样。可以把测试⽤例中的属性值都按照⼀定规则设置成随机值,可以让测试结果更具有普适性。并且可以把测试⽤例的属性的随机规则写在配置⽂件中,⽅便更改。12345678testcase: randomBook: id: ${} id2: ${(10)} #

⽣成10以内的整数 type: ${(5, 10)} #

⽣成5-10之间的整数 name: 糖果墙${} uuid: ${} publicTime: ${}11@Data@Component@ConfigurationProperties(prefix = "-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在springboot项⽬中使⽤Druid数据源,需要先在中加上Druid的依赖:12345 a druid-spring-boot-starter 1.2.8然后在中有两种配置⽅法,两种⽅法实现的效果⼀样17#

配法1:spring: datasource: druid: driver-class-name: url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC username: root password: 123#

配法2:spring: datasource: driver-class-name: url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC username: root password: 123 type: ataSource #

去掉type属性,Druid数据源依然会启⽤配法2去掉type属性后,再启动项⽬,发现Druid数据源依然启⽤了,这是由于引⼊了Druid数据源的依赖后,springboot会⾃动配置Druid。如果不引⼊Druid数据源的依赖,springboot默认的数据源是Hikari数据源17181920#

默认为Hikari数据源spring: datasource: driver-class-name: 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: username: root password: 123 maximum-pool-size: 50 minimum-idle: 30 idle-timeout: 30000springboot_15_02_jdbc_template使⽤JdbcTemplate1. 在中加⼊相关依赖2.

中配置数据源,另外还可以对JdbcTemplate进⾏⼀些简单的配置1718#

配置数据库和连接池spring: datasource: url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC hikari: driver-class-name: 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 #

数据条数⽐较多的时候,⼀次拿多少条数据3. 测试类82930@SpringBootTestclass JdbcTemplateApplicationTests { @Autowired private JdbcTemplate jdbcTemplate; @Test void testJdbcTemplateSelect() { String sql = "select * from tbl_book"; // List> maps = orList(sql); // n(maps); RowMapper rm = new RowMapper() { @Override public Book mapRow(ResultSet rs, int rowNum) throws SQLException { Book book = new Book(); (("id")); e(ing("name")); e(ing("type")); cription(ing("description")); return book; } }; List bookList = (sql, rm); h(::println); }

@Test void testJdbcTemplateInsert() { String sql = "insert into tbl_book values(null, ?, ?, ?)"; (sql, "springboot1", "springboot2", "springboot3"); }}springboot_15_03_h2使⽤H2数据库1. 在中加⼊相关依赖2. 在中配置数据源并且启⽤H2数据库的控制台17server: port: 80

spring: #

配置数据库和连接池 datasource: url: jdbc:h2:~/test hikari: driver-class-name: username: sa password: 123456 #

启⽤H2的控制台 h2: console: enabled: true path: /h23. 在浏览器中输⼊访问H2控制台地址4. 输⼊默认密码:123456,然后点连接,会跳转到控制台主页5. 可以写sql语句建表,并插⼊⼏条数据11create 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表中的数据1select * from tbl_book;6. 写代码连接H2数据库,需要注意启动测试类连接H2数据库的时候需要将之前的H2控制台的springboot程序先停⽌,否则会造成端⼝占⽤,测试类报错。测试类:8@SpringBootTestclass H2ApplicationTests { @Autowired private JdbcTemplate jdbcTemplate; @Test public void testH2Select() { String sql = "select * from tbl_book"; RowMapper rm = new RowMapper() { @Override public Book mapRow(ResultSet rs, int rowNum) throws SQLException { Book book = new Book(); (("id")); e(ing("name")); e(ing("type")); cription(ing("description")); return book; } }; List bookList = (sql, rm); h(::println); } @Test public void testH2Save() { String sql = "insert into tbl_book values(?, ? ,? ,?)"; (sql, 5, "啊哈算法", "计算机", "数据结构和算法");282930 (sql, 5, "啊哈算法", "计算机", "数据结构和算法"); }}7.

H2数据库线上运⾏时请务必关闭。111213bind 0.0.0.0protected-mode noport 6379timeout 0save 900 1 # 900s内⾄少⼀次写操作则执⾏bgsave进⾏RDB持久化save 300 10save 60 10000rdbcompression yesdbfilename /dataappendonly yesappendfsync everysecrequirepass 12345678springboot_15_04_redis在linux上安装redis除了最原始的⽅法外,推荐使⽤docker-compose⼀键启动redis,⾮常⽅便111213bind 0.0.0.0protected-mode noport 6379timeout 0save 900 1 # 900s内⾄少⼀次写操作则执⾏bgsave进⾏RDB持久化save 300 10save 60 10000rdbcompression yesdbfilename /dataappendonly yesappendfsync everysecrequirepass 111213version: '3'services: redis: image: redis:latest container_name: redis restart: always ports: - 6379:6379 volumes: - ./:/etc/redis/:rw - ./data:/data:rw command: ["redis-server","/etc/redis/"]我的redis安装在虚拟机中的linux系统上,可以通过以下两种⽅式测试连通性:1. 通过windows版redis⾃带的redis-cli来远程连接linux上的redis服务,指令如下: -h 192.168.0.110 -p 6379 -a "123456" # 123456是密码2. 还可以使⽤AnotherRedisDesktopManager:这是⼀款⾮常稳定并且拥有美观的图形界⾯的redis客户端,操作起来也是相当简单,⼀看就会⽤,下载地址:1. 在中加⼊spring整合redis的依赖12345 spring-boot-starter-data-redis2.

1234563. 测试类1718@SpringBootTestclass RedisApplicationTests { @Autowired private StringRedisTemplate redisTemplate; @Test public void testSet() { ValueOperations valueOperations = Value(); ("age", "41"); } @Test public void testGet() { ValueOperations valueOperations = Value(); n("age = " + ("age")); n("username = " + ("username")); }}spring: # redis配置 redis: host: 192.168.0.110 port: 6379 password: 123456注:这⾥如果使⽤RedisTemplate⽽不使⽤StringRedisTemplate,去redis客户端⾥⾯查看会发现键值包含xacxedx00x05tx00特殊字符,这是由于RedisTemplate模板类在操作redis时默认使⽤JdkSerializationRedisSerializer来进⾏序列化。⽽存取序列化的⽅式从ializationRedisSerializer将序列化的⽅式改为

RedisSerializer 会⾃动去掉xacxedx00x05tx00前缀,所以有两种解决⽅法:1. 直接使⽤StringRedisTemplate;2. ⽅案2 修改默认的序列化⽅式:private RedisTemplate redisTemplate;@Autowired(required = false)public void setRedisTemplate(RedisTemplate redisTemplate) { RedisSerializer stringSerializer = new StringRedisSerializer(); Serializer(stringSerializer); ueSerializer(stringSerializer); hKeySerializer(stringSerializer); hValueSerializer(stringSerializer); emplate = redisTemplate;}java操作redis底层有两种实现分别为lettuce和jedis,其中lettuce为springboot的RedisTemplate默认使⽤的技术。如果想要切换到jedis:1. 引⼊jedis的jar包2. 在中加⼊配置111213spring: # redis配置 redis: host: 192.168.0.110 port: 6379 password: 123456 client-type: jedis #

默认为lettuce #

还可以进⼀步配置 jedis: pool: enabled: true max-active: 16 min-idle: 8springboot_15_05_mongodb解压mongodb安装包,然后在软件根⽬录下新建datadb,进⼊到bin⽬录下启动⿊窗⼝,输⼊如下命令,启动mongodb数据库,并指定数据保存到datadb⽬录下。 --dbpath=..datadb在bin⽬录下再开⼀个⿊窗⼝,然后输⼊会默认连接ip为localhost,port为27017的mongodb服务,连接成功会输出mongodb的版本等信息。由于⿊窗⼝操作较为繁琐,这⾥推荐使⽤带图形化界⾯的客户端robo3t,启动robo3t,创建⼀个连接打开这个连接右击左侧连接名,在右键菜单中选择Create Database,新建⼀个数据库填写数据库名称展开数据库名,右击Collections再新建⼀个Collection填写collection名称右击新建的Collection book,会弹出⼀个查询界⾯,可以在⽂本框中填写指令对Collection book进⾏操作1718//

查询所有lection('book').find({})//

可以简写为();//

条件查询({type: "springboot"})//

保存⽂档({"name": "springboot", type: "springboot"})//

删除操作({type: "springboot"});//

修改操作//

修改满⾜条件的第⼀条数据({name: "springboot"}, {$set:{name: "springboot2"}});//

修改满⾜条件的所有数据Many({name: "springboot"}, {$set:{name: "springboot2"}});在中加⼊springboot整合MongoDB的依赖1234 spring-boot-starter-data-mongodb在中进⾏MongoDB的配置1234spring: data: mongodb: uri: mongodb://localhost/springboot_mongodb测试代码1718192021@SpringBootTestclass MongodbApplicationTests { @Autowired private MongoTemplate mongoTemplate; @Test void testSave() { Book book = new Book(); (2); e("springboot2"); e("springboot2"); cription("springboot2"); (book); } @Test public void testFindAll() { List books = l(); h(::println); }}springboot_15_06_elasticsearch1.

2. 安装 如果有正常的json返回值,那么说明es启动正常1. 安装IK分词器插件我们希望es再新建索引的时候应⽤分词效果,所以需要先给es安装IK分词器插件在es的plugin⽬录下新建⼀个ik⽂件夹(建⽂件夹是为了⽅便管理),然后把下载好的ik分词器压缩包中的内容解压到ik⽬录下 关闭当前es启动的⿊窗⼝,去es的bin⽬录下,双击,重新启动es2. 新建索引打开postman,发送⼀个PUT请求,新建⼀个books索引请求参数17181926{ "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" } } }} 返回如下提⽰就表⽰新建索引成功3. 查询索引4. 删除索引1. 新建⽂档有3种请求⽅式: 注:如果在新建的过程中出现[TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark, index has read-only-allow-delete block]的问题,可以发送下⾯的请求解决如果上⾯的⽅法也不能解决,检查⼀下⾃⼰的es安装⽬录所在磁盘的可⽤空间是否太⼩,默认必须⼤于5%才可以,⽐如磁盘空间500G,需要⾄少25G的可⽤空间才可以。后来清了磁盘⼤于5%也不⾏,后来⽤这个⽅法解决了:2. 查询⽂档1. 查询全部⽂档1. 查询单个⽂档1. 按条件查询3. 删除⽂档4. 修改⽂档将_id为221432414的⽂档的name修改的值修改为springboot ⾮常好先查询⼀下发起请求执⾏修改操作,这⾥请求体⾥⾯只填写要修改的属性再查询⼀下发现修改后的⽂档,另外两个没有修改的属性没有了,这不是期望的效果,这种请求的修改⽅式是全覆盖⽅式的修改。如果要想只修改⽂档中name属性的值,需要使⽤新的请求⽅式(操作之前先将_id为221432414的⽂档数据恢复⼀下)注意:这⾥发送的是POST请求,⽽上⾯的全量修改发送的是PUT请求修改之后再查询⼀下参考整合h2、redis、mongodb的⽅式,整合es的时候应该先在中加⼊spring整合es的依赖,spring-boot-starter-data-elasticsearch,然后再去中编写es的配置,最后再去测试类⾥⾯注⼊es的template对象,进⾏相关操作。可是需要注意的是,springboot整合es有两套整合⽅案,⼀个是整合低级别的es客户端,另⼀个是整合⾼级别的es客户端。⽽开头所说的是springboot整合低级别的⽅式,这⾥不采⽤这种⽅式。直接整合⾼级别的es客户端,分为以下⼏个步骤:1. 在中加⼊es的依赖,由于测试的过程中还要⽤到对象转json字符串,所以这⾥把json解析的依赖也⼀同加上12345678 elasticsearch-rest-high-level-client jackson-databind2. 整合⾼级别的es客户端,意味着springboot没有提供默认的配置,所以就不能在中配置有关访问es客户端的url等参数了,这⾥直接编写测试类,采⽤硬编码的⽅式指定这些参数。1234567891@SpringBootTestclass 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 = ("localhost:9200"); RestClientBuilder builder = r(host); client = new RestHighLevelClient(builder);6272829363738394647484956575859666768697677787980 client = new RestHighLevelClient(builder); (); } @Test //

创建索引 public void testCreateIndex() throws IOException { CreateIndexRequest request = new CreateIndexRequest("books"); s().create(request, T); } @Test //

使⽤IK分词器创建索引 public void testCreateIndexByIK() throws IOException { CreateIndexRequest request = new CreateIndexRequest("books"); //

设置请求参数 String jsonParam = "{n" + " "mappings": {n" + " "properties": {n" + " "id": {n" + " "type": "keyword"n" + " },n" + " "name": {n" + " "type": "text",n" + " "analyzer": "ik_max_word",n" + " "copy_to": "all"n" + " },n" + " "type": {n" + " "type": "keyword"n" + " },n" + " "description": {n" + " "type": "text",n" + " "analyzer": "ik_max_word",n" + " "copy_to": "all"n" + " },n" + " "all": {n" + " "type": "text",n" + " "analyzer": "ik_max_word"n" + " }n" + " }n" + " }n" + "}"; (jsonParam, ); s().create(request, T); } @Test public void testCreateDoc() throws Exception { Book book = ById(1); IndexRequest request = new IndexRequest("books").id(() + ""); String jsonParams = alueAsString(book); (jsonParams, ); (request, T); } @Test //

将mysql数据库tbl_book表中的数据都存到es中 public void testCreateDocAllFromMySQL() throws Exception { List books = List(null); //

批处理请求,相当于⼀个request容器,可以把单个请求加进来 BulkRequest requests = new BulkRequest(); for (Book book : books) { IndexRequest request = new IndexRequest("books").id(() + ""); String jsonParams = alueAsString(book);868788899697989959120121122 String jsonParams = alueAsString(book); (jsonParams, ); (request); } (requests, T); } @Test //

按ID查询 public void testGet() throws IOException { GetRequest request = new GetRequest("books", "1"); GetResponse response = (request, T); n("book:" + rceAsString()); } @Test public void testSearch() throws IOException { SearchRequest request = new SearchRequest("books"); SearchSourceBuilder builder = new SearchSourceBuilder(); (ery("all", "spring")); (builder); SearchResponse response = (request, T); SearchHits hits = s(); for (SearchHit hit : hits) { n(rceAsString()); } } @BeforeEach void setUp() { objectMapper = new ObjectMapper(); HttpHost host = ("localhost:9200"); RestClientBuilder builder = r(host); client = new RestHighLevelClient(builder); } @AfterEach void tearDown() throws IOException { (); }}16. 整合第三⽅技术16.1 缓存springboot_16_01_01_cache_book_quickstart先⾃⼰⽤java的HashMap模拟⼀个缓存12345@GetMapping("{id}")//

模拟缓存public R getById(@PathVariable Integer id) { return new R(true, d(id));}111213@Override//

模拟缓存private HashMap cache = new HashMap<>();public Book getById(Serializable id) { //

如果当前缓存中没有本次要查询的数据,则进⾏查询,否则直接从缓存中获取数据返回 Book book = (id); if (book == null) { book = d(id); (id, book); } return book;}springboot_16_01_02_cache_book_simple使⽤spring中⾃带的缓存技术在中添加如下依赖12345 spring-boot-starter-cache在springboot启动类上加@EnableCaching注解在业务层要使⽤缓存的⽅法上加上@Cacheable(value = "cacheSpace", key = "#id")注解,如下所⽰12345@Cacheable(value = "cacheSpace", key = "#id")public Book getById(Serializable id) { n("id = " + id); return d(id);}其中value表⽰缓存空间,key=“#id”,表⽰将⽅法参数id的值作为缓存中的⼀个key。springboot_16_01_03_cache_smscode_simple1. ⼿机验证码案例基础代码准备1. 存储⼿机号和验证码的实体类12345@Datapublic class SMSCode { private String phone; private String code;}2. ⽣成验证码的⼯具类123456789@Componentpublic class CodeUtils { private final String padding = "000000"; //

⽣成验证码(位数少于6位左边填充0,填充⽅法1) public String generateCode(String phone) { int hash = de(); int encryption = 20228888; long result = hash ^ encryption; long nowTime = me();972829363738394647484956 long nowTime = me(); result = (result ^ nowTime) % 1000000; String code = result + ""; // code = phone; // ing(()) () // 6 // 0 5 // 00 4 // 000 3 // 000 3 // 0000 2 // 00000 1 // 000000 0 code = ing(()) + code; // n(code); return code; } private final String[] patch = {"000000", "00000", "0000", "000", "00", "0", ""}; //

⽣成验证码(位数少于6位左边填充0,填充⽅法2) public String generateCode1(String phone) { int hash = de(); int encryption = 20228888; long result = hash ^ encryption; long nowTime = me(); result = (result ^ nowTime) % 1000000; String code = result + ""; // code = phone; // patch[] () // 000000 0 // 00000 1 // 0000 2 // 000 3 // 00 4 // 0 5 // 6 code = patch[()] + code; // n(code); return code; } //

根据⼿机号从缓存中获取验证码,缓存中有的话返回缓存中的值,没有的话就返回null @Cacheable(value = "smsCode", key = "#phone") public String getCodeByPhoneFromCache(String phone) { return null; }}3. 业务层接⼝12345public interface SMSCodeService { String sendCodeToSMS(String phone); boolean checkCode(SMSCode smsCode);}业务层接⼝实现类1234@Servicepublic class SMSCodeServiceImpl implements SMSCodeService { @Autowired private CodeUtils codeUtils;4567891 private CodeUtils codeUtils; @Override public String sendCodeToSMS(String phone) { return teCode(phone); } @Override public boolean checkCode(SMSCode smsCode) { return false; }}2. 加⼊spring默认的缓存功能1. 在中添加缓存依赖12345 spring-boot-starter-cache2. 在SMSCodeServiceImpl的sendCodeToSMS()⽅法上添加@CachePut(value = "smsCode", key = "#phone"),如下所⽰@Override// @Cacheable(value = "smsCode", key = "#phone")//

这⾥@Cacheable注解不适⽤,因为@Cacheable注解的功能是:如果缓存中没有值就去执⾏⼀次⽅法,然后将值存到缓存中,//

如果有值就直接从缓存中取值并返回,并不会执⾏⽅法,因⽽缓存中值不会发⽣改变。//

⽽⽤户点击界⾯发送⼀次验证码就调⽤了⼀次该⽅法,需要获取到新的验证码。//

使⽤新的缓存注解@CachePut可以解决这个问题,每次调⽤都会执⾏⽅法,向缓存中存新的值并返回@CachePut(value = "smsCode", key = "#phone")public String sendCodeToSMS(String phone) { return teCode(phone);}3. 编写checkCode()⽅法:校验验证码是否正确错误的写法:1234567891@Overridepublic boolean checkCode(SMSCode smsCode) { //

取出缓存中的验证码并与传递过来的验证码⽐对,如果相同,返回true,否则,返回false //

⽤户填写的验证码 String code = e(); //

缓存中存的验证码 String cacheCode = getCodeByPhoneFromCache(ne()); return (code);}//

根据⼿机号从缓存中获取验证码,缓存中有的话返回缓存中的值,没有的话就返回null@Cacheable(value = "cacheSpace", key = "#phone")public String getCodeByPhoneFromCache(String phone) { return null;}在getCodeByPhoneFromCache()⽅法上加了@Cacheable(value = "cacheSpace", key = "#phone"),然后在checkCode()⽅法中调⽤getCodeByPhoneFromCache()⽅法,这种⽅式看似是正确的,实际上@Cacheable注解不会⽣效,导致getCodeByPhoneFromCache()的返回值始终是null。这是由于被spring管理的类内的⽅法互调注解不会被解析。由此可以想到解决办法,将getCodeByPhoneFromCache()放到另外⼀个类(这⾥为了⽅便起见,直接放到CodeUtils类中),并将这个类交由spring管理(在类上⾯加@Component注解),然后再⽤eByPhoneFromCache(ne())即这个类交由spring管理(在类上⾯加@Component注解),然后再⽤eByPhoneFromCache(ne())即可正常从缓存中拿到值。代码如下:123456789@Overridepublic boolean checkCode(SMSCode smsCode) { //

取出缓存中的验证码并与传递过来的验证码⽐对,如果相同,返回true,否则,返回false //

⽤户填写的验证码 String code = e(); //

缓存中存的验证码 String cacheCode = eByPhoneFromCache(ne()); return (code);}springboot_16_01_04_cache_smscode_ehcache基于验证码案例的代码和配置,使⽤ehcache替换spring默认的simple缓存1. 在中加⼊ehcache的依赖12345 e ehcache2. 在中加⼊如下配置123456spring: cache: type: ehcache #

如果配置⽂件改名了,可以打开下⾯的配置指定配置⽂件路径 # ehcache: # config: 3. 在resources⽬录下新建⼀个配置⽂件,内容如下:8293031323334 直接启动项⽬,并且验证码获取和验证的过程不报错,说明缓存替换成成功。LRU: Least recently used, 淘汰最近最少被使⽤的数据LFU: Least frequently used,淘汰使⽤频率最低的数据springboot_16_01_05_cache_smscode_redis基于验证码案例的代码和配置,使⽤redis替换spring默认的simple缓存1. 在中加⼊redis的依赖12345 spring-boot-starter-data-redis2. 在中加⼊如下配置111213spring: cache: type: redis redis: #

是否使⽤缓存命名空间作为前缀,如:smsCode::,默认为true use-key-prefix: true cache-null-values: true #

是否缓存空值 key-prefix: aa # use-key-prefix为false的时候该项不⽣效 time-to-live: 10s #

缓存失效时间 redis: host: 192.168.0.110 port: 6379 password: 123456直接启动项⽬,并且验证码获取和验证的过程不报错,说明缓存替换成成功。springboot_16_01_06_cache_smscode_memcached基于验证码案例的代码和配置,使⽤memcached替换spring默认的simple缓存,memcached最新的客户端技术是xmemcached1. 在中加⼊xmemcached的依赖123456 ched xmemcached 2.4.72. 由于springboot并未收录memcached,所以只能通过硬编码的⽅式完成相关配置123456789@Componentpublic class XMemcachedConfig { @Bean public MemcachedClient getMemcachedClient() throws IOException { MemcachedClientBuilder memcachedClientBuilder = new XMemcachedClientBuilder("192.168.0.102:11211"); MemcachedClient memcachedClient = (); return memcachedClient; }}82930@Servicepublic class SMSCodeServiceImpl implements SMSCodeService { @Autowired private CodeUtils codeUtils; @Autowired private MemcachedClient memcachedClient; @Override public String sendCodeToSMS(String phone) { String code = teCode(phone); try { (phone, 10, code); } catch (Exception e) { tackTrace(); } return code; } @Override public boolean checkCode(SMSCode smsCode) { String code = null; try { code = (ne()).toString(); } catch (Exception e) { tackTrace(); } return e().equals(code); }}直接启动项⽬,并且验证码获取和验证的过程不报错,说明缓存替换成成功。

2023年6月20日发(作者:)

⿊马程序员SpringBoot2全套教程学习笔记介绍注:四级标题和部分5级标题是使⽤⼦项⽬名称命名的,和我代码仓库的项⽬是⼀⼀对应的。每个⼦项⽬对应的视频链接以及⼀些重要内容的笔记⼀、基础篇1.⼊门案例springboot_01_01_quickstartspringboot_01_02_quickstartspringboot_01_03_quickstartspringboot_01_04_quickstart:更换SpringBoot内嵌的默认的web容器从tomcat换成jetty1.1 补充springboot_01_05_restspringboot_0x_02x_xxxxxxxx2. 基础配置springboot_02_base_configuration1. properties2. yml(主流格式)3. yaml三种格式共存,优先级从先到后为properties、yml、yaml3. yaml配置⽂件springboot_03_yaml4. 整合Junitspringboot_04_junit5. 整合mybatisspringboot_05_mybatis课程中使⽤到的数据库脚本829363738394647484950/* 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 (2, '格林童话', '童话', '睡前故事。');INSERT INTO `tbl_book` VALUES (3, 'Spring 5设计模式', '计算机理论', '深⼊Spring源码剖析Spring源码中蕴含的10⼤设计模式');INSERT INTO `tbl_book` VALUES (5, '轻量级Java Web企业应⽤实战', '计算机理论', '源码级剖析Spring框架,适合已掌握Java基础的读者');INSERT INTO `tbl_book` VALUES (7, '深⼊理解Java虚拟机', '计算机理论', '5个维度全⾯剖析JVM,⾯试知识点全覆盖');INSERT INTO `tbl_book` VALUES (8, 'Java编程思想(第4版)', '计算机理论', '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堂课轻松实现带货⽉⼊3W+'INSERT INTO `tbl_book` VALUES (13, 'Spring实战第5版', '计算机理论', 'Spring⼊门经典教程,深⼊理解Spring原理技术内幕');INSERT INTO `tbl_book` VALUES (14, 'Spring 5核⼼原理与30个类⼿写实战', '计算机理论', '⼗年沉淀之作,写Spring精华思想');SET FOREIGN_KEY_CHECKS = 1;INSERT INTO `tbl_book` VALUES (1, '三体', '科幻', '⼤刘的巅峰之作,将中国科幻推向世界舞台。总共分为三部曲:《地球往事》、《⿊暗森林》、《死神永⽣INSERT INTO `tbl_book` VALUES (4, 'Spring MVC+ MyBatis开发从⼊门到项⽬实战', '计算机理论', '全⽅位解析⾯向Web应⽤的轻量级框架,带你成为Spring MVINSERT INTO `tbl_book` VALUES (6, 'Java核⼼技术卷|基础知识(原书第11版)', '计算机理论', 'Core Java第11版,Jolt⼤奖获奖作品,针对Java SE9、10、 116. 整合mybatis-plusspringboot_06_mybatis_plus7. 整合druid数据库连接池springboot_07_druid8. springboot基础篇综合案例springboot_08_ssmp⼆、运维实⽤篇1234567#

后台启动springboot项⽬jar包nohup java -jar springboot_08_ > 2>&1 &#

终⽌程序#

查看java -jar

命令对应的进程号ps -ef | grep "java -jar"#

终⽌对应进程kill -9 9. 使⽤⾃定义配置springboot_09_config⽅法1:设置程序参数,指定⽂件名(不包含后缀),如:--=ebank⽅法2:指定配置⽂件的路径,如:--on=classpath:/还可以指定多个配置⽂件,如:--on=classpath:/,classpath:/10. 多环境配置springboot_10_profiles单⽂件版:⽂件:8#

应⽤环境#

公共配置spring: profiles: active: dev---#

⽣产环境spring: config: activate: on-profile: proserver: port: 80---spring: config: activate: on-profile: devserver: port: 8080---#

测试环境spring: config: activate: on-profile: testserver: port: 8888yaml多⽂件版12345#

应⽤环境#

公共配置spring: profiles: active: 123#

开发环境server: port: 123#

⽣产环境server: port: 123#

测试环境server: port: 8888properties多⽂件版ties12# 应⽤环境=ties12# 开发环境=ties12# ⽣产环境=ties12# 测试环境=8888使⽤group属性配置同组的配置⽂件,active可以直接使⼀组配置⽂件同时⽣效1112#spring:# profiles:# active: dev# include: devDB, devMVCspring: profiles: active: dev group: "dev": devDB, devMVC "pro": proDB, proMVC "test": testDB, 12server: port: 12server: port: 81application-devMVCyml1234server: servlet: context-path: /ebank port: 82观察启动⽇志,配置⽂件加载的顺序为 : The following profiles are active: dev,devDB,devMVCmaven中使⽤多环境,然后在springboot中读取maven中的配置中的配置8 env_dev <>dev true env_pro <>pro env_test <>test true 中的配置1112#spring:# profiles:# active: dev# include: devDB, devMVCspring: profiles: active: @@ group: "dev": devDB, devMVC "pro": proDB, proMVC "test": testDB, TestMVC11. 记录⽇志springboot_11_log三、开发实⽤篇12. 热部署springboot_12_hot_deploy想要拥有热部署的功能,需要在⽂件中添加如下依赖:12345 spring-boot-devtools true⾃动启动热部署,即,CTRL + F9编译的操作操作由程序⾃动完成,需要进⾏如下配置:1. 在设置中勾选2. 按快捷键CTRL + SHIFT + ALT + /,在弹出的菜单中选择第⼀项,注册表,或者直接按CTRL + ALT + M调出注册表;3. 在注册表项中勾选g4. idea新版本2021.3.1中在注册表中找不到改选项,需要在⾼级设置中进⾏设置。[外链图⽚转存失败,源站可能有防盗链机制,建议将图⽚保存下来直接上传(img-PA8pixRs-19)(C:UsersCandyWa)]12345678spring: #

热部署范围配置 devtools: restart: #

设置不参与热部署的⽂件和⽂件夹(即修改后不重启) exclude: static/**,public/**,config/ #是否可⽤ enabled: false如果配置⽂件⽐较多的时候找热部署对应配置⽐较⿇烦,可以在springboot启动类的main⽅法中设置,此处设置的优先级将⽐配置⽂件⾼,⼀定会⽣效。perty("d", "false");13. 属性绑定springboot_13_configuration1. 先在要配置的类上⾯加@Component注解将该类交由spring容器管理;2.

@ConfigurationProperties(prefix="xxx"),xxx跟配置⽂件中的属性对应;3. 如果多个配置类想统⼀管理也可以通过@EnableConfigurationProperties({, })的⽅式完成配置,不过该注解会与@Component配置发⽣冲突,⼆选⼀即可;4. 第三⽅类对象想通过配置进⾏属性注⼊,可以通过创建⼀个⽅法,在⽅法体上加@Bean和@ConfigurationProperties(prefix="xxx")注解,然后⽅法返回这个第三⽅对象的⽅式。5. 使⽤@ConfigurationProperties(prefix="xxx")注解后idea⼯具会报⼀个警告Spring Boot Configuration Annotation Processor not configured只需要在中加上如下依赖刷新即可消除该警告1234 spring-boot-configuration-processor@ConfigurationProperties绑定属性⽀持属性名宽松绑定,⼜叫松散绑定。⽐如要将作为配置类,并通过配置⽂件绑定属性12345678@Component@ConfigurationProperties(prefix = "serverconfig")@Datapublic class ServerConfig { private String ipAddress; private int port; private long timeout;}11serverConfig: # 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就会报错,这与松散绑定的前缀命名规范有关:仅能使⽤纯⼩写字母、数字、中划线作为合法的字符1112//@Component@ConfigurationProperties(prefix = "server-config")@Datapublic class ServerConfig { private String ipAddress; private int port; @DurationUnit(S) private Duration timeout; @DataSizeUnit(TES) private DataSize dataSize;}引⼊Bean属性校验框架的步骤:1. 在中添加JSR303规范和hibernate校验框架的依赖: tion validation-api tor hibernate-validator2. 在要校验的类上加@Validated注解3. 设置具体的校验规则,如:@Max(value=8888, message="最⼤值不能超过8888")@ConfigurationProperties(prefix = "server-config")@Data// 2.开启对当前bean的属性注⼊校验@Validatedpublic class ServerConfig { private String ipAddress; //

设置具体的规则 @Max(value = 8888, message = "最⼤值不能超过8888") @Min(value = 1000, message = "最⼩值不能低于1000") private int port; @DurationUnit(S) private Duration timeout; @DataSizeUnit(TES) private DataSize dataSize;}进制转换中的⼀些问题:如⽂件中对数据库有如下配置:123456datasource: driverClassName: 123 #

不加引号读取的时候默认解析为了8进制数,转成⼗进制就是87 #

所以想让这⾥正确识别,需要加上引号 # password: 0127 password: "0127"14. 测试相关springboot_14_test@SpringBootTest注解中可以设置properties和args属性,这⾥的args属性的作⽤跟idea⼯具中⾃带的程序参数类似,只不过这⾥的配置是源码级别的,会随着源码的移动⽽跟随,⽽idea中的程序参数的配置会丢失。并且这⾥的args属性的配置的作⽤范围⽐较⼩,仅在当前测试类⽣效。12test: prop: 11121314// properties属性可以为当前测试⽤例添加临时的属性配置//@SpringBootTest(properties = {"=testValue1"})// args属性可以为当前测试⽤例添加临时的命令⾏参数//@SpringBootTest(args = {"--=testValue2"})//

优先级排序: args > properties >

配置⽂件@SpringBootTest(args = {"--=testValue2"}, properties = {"=testValue1"})class PropertiesAndArgsTest { @Value("${}") private String prop; @Test public void testProperties() { n("prop = " + prop); }}某些测试类中需要⽤到第三⽅的类,⽽其他测试类则不需要⽤到,这⾥可以在类上加载@Import({, })表现层1718192021@RestController@RequestMapping("/books")public class BookController { /*@GetMapping("/{id}") public String getById(@PathVariable int id) { n("id = " + id); return ""; }*/ @GetMapping("/{id}") public Book getById(@PathVariable int id) { n("id = " + id); Book book = new Book(); (5); e("神秘岛"); e("科幻"); cription("《神秘岛》是法国科幻⼩说家儒勒·凡尔纳创作的长篇⼩说,是他写的三部曲之⼀。"); return book; }}对应的测试类8293637@SpringBootTest(webEnvironment = _PORT)//

开启虚拟mvc调⽤@AutoConfigureMockMvcpublic class WebTest { @Test public void testRandomPort() { } @Test public void testWeb(@Autowired MockMvc mvc) throws Exception { //

创建虚拟请求,当前访问 /books MockHttpServletRequestBuilder builder = ("/books/5"); m(builder); } @Test public void testStatus(@Autowired MockMvc mvc) throws Exception { MockHttpServletRequestBuilder builder = ("/books1/6"); ResultActions action = m(builder); //

设定预期值,与真实值进⾏⽐较,成功测试通过,失败测试不通过 //

定义本次调⽤的预期值 StatusResultMatchers srm = (); //

预计本次调⽤成功的状态码:200 ResultMatcher ok = (); //

添加预计值到本次调⽤过程中进⾏匹配 ect(ok); } @Test public void testBody(@Autowired MockMvc mvc) throws Exception { MockHttpServletRequestBuilder builder = ("/books/6"); ResultActions action = m(builder); //

设定预期值,与真实值进⾏⽐较,成功测试通过,失败测试不通过 //

定义本次调⽤的预期值 ContentResultMatchers crm = t(); //

预计本次调⽤成功的状态码:200 ResultMatcher rm = ("");373839464748495657585966676869767778798 ResultMatcher rm = (""); //

添加预计值到本次调⽤过程中进⾏匹配 ect(rm); } @Test public void testJson(@Autowired MockMvc mvc) throws Exception { MockHttpServletRequestBuilder builder = ("/books/7"); ResultActions action = m(builder); //

设定预期值,与真实值进⾏⽐较,成功测试通过,失败测试不通过 //

定义本次调⽤的预期值 ContentResultMatchers jsonMatcher = t(); ect(rm); } @Test public void testContentType(@Autowired MockMvc mvc) throws Exception { MockHttpServletRequestBuilder builder = ("/books/7"); ResultActions action = m(builder); //

设定预期值,与真实值进⾏⽐较,成功测试通过,失败测试不通过 //

定义本次调⽤的预期值 HeaderResultMatchers hrm = (); ResultMatcher rm = ("Content-Type", "application/json"); ect(rm); } @Test //

完整测试 public void testGetById(@Autowired MockMvc mvc) throws Exception { MockHttpServletRequestBuilder builder = ("/books/8"); ResultActions action = m(builder); // 1、⽐较状态码 StatusResultMatchers statusResultMatchers = (); ResultMatcher statusResultMatcher = (); ect(statusResultMatcher); // 2、⽐较返回值类型 HeaderResultMatchers headerResultMatchers = (); ResultMatcher headerResultMatcher = ("Content-Type", "application/json"); ect(headerResultMatcher); /// 3、⽐较json返回值 ContentResultMatchers contentResultMatchers = t(); ect(jsonResultMatcher); }} ResultMatcher rm = ("{"id":5,"name":"神秘岛","type":"科幻","description":"《神秘岛》是法国科幻⼩说家儒勒·凡尔纳创作的长篇⼩ ResultMatcher jsonResultMatcher = ("{"id":5,"name":"神秘岛","type":"科幻","description":"《神秘岛》是法国科幻⼩说测试过程中对数据库的增删改操作的影响是否回滚,由下⾯两个注解控制,需要在测试类上加:@Transactional,@Rollback(value=true):回滚,@Rollback(value=true)为默认值,也可以省略;@Transactional,@Rollback(value=false):不回滚,跟什么注解都不加的效果⼀样。可以把测试⽤例中的属性值都按照⼀定规则设置成随机值,可以让测试结果更具有普适性。并且可以把测试⽤例的属性的随机规则写在配置⽂件中,⽅便更改。12345678testcase: randomBook: id: ${} id2: ${(10)} #

⽣成10以内的整数 type: ${(5, 10)} #

⽣成5-10之间的整数 name: 糖果墙${} uuid: ${} publicTime: ${}11@Data@Component@ConfigurationProperties(prefix = "-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在springboot项⽬中使⽤Druid数据源,需要先在中加上Druid的依赖:12345 a druid-spring-boot-starter 1.2.8然后在中有两种配置⽅法,两种⽅法实现的效果⼀样17#

配法1:spring: datasource: druid: driver-class-name: url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC username: root password: 123#

配法2:spring: datasource: driver-class-name: url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC username: root password: 123 type: ataSource #

去掉type属性,Druid数据源依然会启⽤配法2去掉type属性后,再启动项⽬,发现Druid数据源依然启⽤了,这是由于引⼊了Druid数据源的依赖后,springboot会⾃动配置Druid。如果不引⼊Druid数据源的依赖,springboot默认的数据源是Hikari数据源17181920#

默认为Hikari数据源spring: datasource: driver-class-name: 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: username: root password: 123 maximum-pool-size: 50 minimum-idle: 30 idle-timeout: 30000springboot_15_02_jdbc_template使⽤JdbcTemplate1. 在中加⼊相关依赖2.

中配置数据源,另外还可以对JdbcTemplate进⾏⼀些简单的配置1718#

配置数据库和连接池spring: datasource: url: jdbc:mysql://localhost:3306/springboot_db?serverTimezone=UTC hikari: driver-class-name: 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 #

数据条数⽐较多的时候,⼀次拿多少条数据3. 测试类82930@SpringBootTestclass JdbcTemplateApplicationTests { @Autowired private JdbcTemplate jdbcTemplate; @Test void testJdbcTemplateSelect() { String sql = "select * from tbl_book"; // List> maps = orList(sql); // n(maps); RowMapper rm = new RowMapper() { @Override public Book mapRow(ResultSet rs, int rowNum) throws SQLException { Book book = new Book(); (("id")); e(ing("name")); e(ing("type")); cription(ing("description")); return book; } }; List bookList = (sql, rm); h(::println); }

@Test void testJdbcTemplateInsert() { String sql = "insert into tbl_book values(null, ?, ?, ?)"; (sql, "springboot1", "springboot2", "springboot3"); }}springboot_15_03_h2使⽤H2数据库1. 在中加⼊相关依赖2. 在中配置数据源并且启⽤H2数据库的控制台17server: port: 80

spring: #

配置数据库和连接池 datasource: url: jdbc:h2:~/test hikari: driver-class-name: username: sa password: 123456 #

启⽤H2的控制台 h2: console: enabled: true path: /h23. 在浏览器中输⼊访问H2控制台地址4. 输⼊默认密码:123456,然后点连接,会跳转到控制台主页5. 可以写sql语句建表,并插⼊⼏条数据11create 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表中的数据1select * from tbl_book;6. 写代码连接H2数据库,需要注意启动测试类连接H2数据库的时候需要将之前的H2控制台的springboot程序先停⽌,否则会造成端⼝占⽤,测试类报错。测试类:8@SpringBootTestclass H2ApplicationTests { @Autowired private JdbcTemplate jdbcTemplate; @Test public void testH2Select() { String sql = "select * from tbl_book"; RowMapper rm = new RowMapper() { @Override public Book mapRow(ResultSet rs, int rowNum) throws SQLException { Book book = new Book(); (("id")); e(ing("name")); e(ing("type")); cription(ing("description")); return book; } }; List bookList = (sql, rm); h(::println); } @Test public void testH2Save() { String sql = "insert into tbl_book values(?, ? ,? ,?)"; (sql, 5, "啊哈算法", "计算机", "数据结构和算法");282930 (sql, 5, "啊哈算法", "计算机", "数据结构和算法"); }}7.

H2数据库线上运⾏时请务必关闭。111213bind 0.0.0.0protected-mode noport 6379timeout 0save 900 1 # 900s内⾄少⼀次写操作则执⾏bgsave进⾏RDB持久化save 300 10save 60 10000rdbcompression yesdbfilename /dataappendonly yesappendfsync everysecrequirepass 12345678springboot_15_04_redis在linux上安装redis除了最原始的⽅法外,推荐使⽤docker-compose⼀键启动redis,⾮常⽅便111213bind 0.0.0.0protected-mode noport 6379timeout 0save 900 1 # 900s内⾄少⼀次写操作则执⾏bgsave进⾏RDB持久化save 300 10save 60 10000rdbcompression yesdbfilename /dataappendonly yesappendfsync everysecrequirepass 111213version: '3'services: redis: image: redis:latest container_name: redis restart: always ports: - 6379:6379 volumes: - ./:/etc/redis/:rw - ./data:/data:rw command: ["redis-server","/etc/redis/"]我的redis安装在虚拟机中的linux系统上,可以通过以下两种⽅式测试连通性:1. 通过windows版redis⾃带的redis-cli来远程连接linux上的redis服务,指令如下: -h 192.168.0.110 -p 6379 -a "123456" # 123456是密码2. 还可以使⽤AnotherRedisDesktopManager:这是⼀款⾮常稳定并且拥有美观的图形界⾯的redis客户端,操作起来也是相当简单,⼀看就会⽤,下载地址:1. 在中加⼊spring整合redis的依赖12345 spring-boot-starter-data-redis2.

1234563. 测试类1718@SpringBootTestclass RedisApplicationTests { @Autowired private StringRedisTemplate redisTemplate; @Test public void testSet() { ValueOperations valueOperations = Value(); ("age", "41"); } @Test public void testGet() { ValueOperations valueOperations = Value(); n("age = " + ("age")); n("username = " + ("username")); }}spring: # redis配置 redis: host: 192.168.0.110 port: 6379 password: 123456注:这⾥如果使⽤RedisTemplate⽽不使⽤StringRedisTemplate,去redis客户端⾥⾯查看会发现键值包含xacxedx00x05tx00特殊字符,这是由于RedisTemplate模板类在操作redis时默认使⽤JdkSerializationRedisSerializer来进⾏序列化。⽽存取序列化的⽅式从ializationRedisSerializer将序列化的⽅式改为

RedisSerializer 会⾃动去掉xacxedx00x05tx00前缀,所以有两种解决⽅法:1. 直接使⽤StringRedisTemplate;2. ⽅案2 修改默认的序列化⽅式:private RedisTemplate redisTemplate;@Autowired(required = false)public void setRedisTemplate(RedisTemplate redisTemplate) { RedisSerializer stringSerializer = new StringRedisSerializer(); Serializer(stringSerializer); ueSerializer(stringSerializer); hKeySerializer(stringSerializer); hValueSerializer(stringSerializer); emplate = redisTemplate;}java操作redis底层有两种实现分别为lettuce和jedis,其中lettuce为springboot的RedisTemplate默认使⽤的技术。如果想要切换到jedis:1. 引⼊jedis的jar包2. 在中加⼊配置111213spring: # redis配置 redis: host: 192.168.0.110 port: 6379 password: 123456 client-type: jedis #

默认为lettuce #

还可以进⼀步配置 jedis: pool: enabled: true max-active: 16 min-idle: 8springboot_15_05_mongodb解压mongodb安装包,然后在软件根⽬录下新建datadb,进⼊到bin⽬录下启动⿊窗⼝,输⼊如下命令,启动mongodb数据库,并指定数据保存到datadb⽬录下。 --dbpath=..datadb在bin⽬录下再开⼀个⿊窗⼝,然后输⼊会默认连接ip为localhost,port为27017的mongodb服务,连接成功会输出mongodb的版本等信息。由于⿊窗⼝操作较为繁琐,这⾥推荐使⽤带图形化界⾯的客户端robo3t,启动robo3t,创建⼀个连接打开这个连接右击左侧连接名,在右键菜单中选择Create Database,新建⼀个数据库填写数据库名称展开数据库名,右击Collections再新建⼀个Collection填写collection名称右击新建的Collection book,会弹出⼀个查询界⾯,可以在⽂本框中填写指令对Collection book进⾏操作1718//

查询所有lection('book').find({})//

可以简写为();//

条件查询({type: "springboot"})//

保存⽂档({"name": "springboot", type: "springboot"})//

删除操作({type: "springboot"});//

修改操作//

修改满⾜条件的第⼀条数据({name: "springboot"}, {$set:{name: "springboot2"}});//

修改满⾜条件的所有数据Many({name: "springboot"}, {$set:{name: "springboot2"}});在中加⼊springboot整合MongoDB的依赖1234 spring-boot-starter-data-mongodb在中进⾏MongoDB的配置1234spring: data: mongodb: uri: mongodb://localhost/springboot_mongodb测试代码1718192021@SpringBootTestclass MongodbApplicationTests { @Autowired private MongoTemplate mongoTemplate; @Test void testSave() { Book book = new Book(); (2); e("springboot2"); e("springboot2"); cription("springboot2"); (book); } @Test public void testFindAll() { List books = l(); h(::println); }}springboot_15_06_elasticsearch1.

2. 安装 如果有正常的json返回值,那么说明es启动正常1. 安装IK分词器插件我们希望es再新建索引的时候应⽤分词效果,所以需要先给es安装IK分词器插件在es的plugin⽬录下新建⼀个ik⽂件夹(建⽂件夹是为了⽅便管理),然后把下载好的ik分词器压缩包中的内容解压到ik⽬录下 关闭当前es启动的⿊窗⼝,去es的bin⽬录下,双击,重新启动es2. 新建索引打开postman,发送⼀个PUT请求,新建⼀个books索引请求参数17181926{ "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" } } }} 返回如下提⽰就表⽰新建索引成功3. 查询索引4. 删除索引1. 新建⽂档有3种请求⽅式: 注:如果在新建的过程中出现[TOO_MANY_REQUESTS/12/disk usage exceeded flood-stage watermark, index has read-only-allow-delete block]的问题,可以发送下⾯的请求解决如果上⾯的⽅法也不能解决,检查⼀下⾃⼰的es安装⽬录所在磁盘的可⽤空间是否太⼩,默认必须⼤于5%才可以,⽐如磁盘空间500G,需要⾄少25G的可⽤空间才可以。后来清了磁盘⼤于5%也不⾏,后来⽤这个⽅法解决了:2. 查询⽂档1. 查询全部⽂档1. 查询单个⽂档1. 按条件查询3. 删除⽂档4. 修改⽂档将_id为221432414的⽂档的name修改的值修改为springboot ⾮常好先查询⼀下发起请求执⾏修改操作,这⾥请求体⾥⾯只填写要修改的属性再查询⼀下发现修改后的⽂档,另外两个没有修改的属性没有了,这不是期望的效果,这种请求的修改⽅式是全覆盖⽅式的修改。如果要想只修改⽂档中name属性的值,需要使⽤新的请求⽅式(操作之前先将_id为221432414的⽂档数据恢复⼀下)注意:这⾥发送的是POST请求,⽽上⾯的全量修改发送的是PUT请求修改之后再查询⼀下参考整合h2、redis、mongodb的⽅式,整合es的时候应该先在中加⼊spring整合es的依赖,spring-boot-starter-data-elasticsearch,然后再去中编写es的配置,最后再去测试类⾥⾯注⼊es的template对象,进⾏相关操作。可是需要注意的是,springboot整合es有两套整合⽅案,⼀个是整合低级别的es客户端,另⼀个是整合⾼级别的es客户端。⽽开头所说的是springboot整合低级别的⽅式,这⾥不采⽤这种⽅式。直接整合⾼级别的es客户端,分为以下⼏个步骤:1. 在中加⼊es的依赖,由于测试的过程中还要⽤到对象转json字符串,所以这⾥把json解析的依赖也⼀同加上12345678 elasticsearch-rest-high-level-client jackson-databind2. 整合⾼级别的es客户端,意味着springboot没有提供默认的配置,所以就不能在中配置有关访问es客户端的url等参数了,这⾥直接编写测试类,采⽤硬编码的⽅式指定这些参数。1234567891@SpringBootTestclass 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 = ("localhost:9200"); RestClientBuilder builder = r(host); client = new RestHighLevelClient(builder);6272829363738394647484956575859666768697677787980 client = new RestHighLevelClient(builder); (); } @Test //

创建索引 public void testCreateIndex() throws IOException { CreateIndexRequest request = new CreateIndexRequest("books"); s().create(request, T); } @Test //

使⽤IK分词器创建索引 public void testCreateIndexByIK() throws IOException { CreateIndexRequest request = new CreateIndexRequest("books"); //

设置请求参数 String jsonParam = "{n" + " "mappings": {n" + " "properties": {n" + " "id": {n" + " "type": "keyword"n" + " },n" + " "name": {n" + " "type": "text",n" + " "analyzer": "ik_max_word",n" + " "copy_to": "all"n" + " },n" + " "type": {n" + " "type": "keyword"n" + " },n" + " "description": {n" + " "type": "text",n" + " "analyzer": "ik_max_word",n" + " "copy_to": "all"n" + " },n" + " "all": {n" + " "type": "text",n" + " "analyzer": "ik_max_word"n" + " }n" + " }n" + " }n" + "}"; (jsonParam, ); s().create(request, T); } @Test public void testCreateDoc() throws Exception { Book book = ById(1); IndexRequest request = new IndexRequest("books").id(() + ""); String jsonParams = alueAsString(book); (jsonParams, ); (request, T); } @Test //

将mysql数据库tbl_book表中的数据都存到es中 public void testCreateDocAllFromMySQL() throws Exception { List books = List(null); //

批处理请求,相当于⼀个request容器,可以把单个请求加进来 BulkRequest requests = new BulkRequest(); for (Book book : books) { IndexRequest request = new IndexRequest("books").id(() + ""); String jsonParams = alueAsString(book);868788899697989959120121122 String jsonParams = alueAsString(book); (jsonParams, ); (request); } (requests, T); } @Test //

按ID查询 public void testGet() throws IOException { GetRequest request = new GetRequest("books", "1"); GetResponse response = (request, T); n("book:" + rceAsString()); } @Test public void testSearch() throws IOException { SearchRequest request = new SearchRequest("books"); SearchSourceBuilder builder = new SearchSourceBuilder(); (ery("all", "spring")); (builder); SearchResponse response = (request, T); SearchHits hits = s(); for (SearchHit hit : hits) { n(rceAsString()); } } @BeforeEach void setUp() { objectMapper = new ObjectMapper(); HttpHost host = ("localhost:9200"); RestClientBuilder builder = r(host); client = new RestHighLevelClient(builder); } @AfterEach void tearDown() throws IOException { (); }}16. 整合第三⽅技术16.1 缓存springboot_16_01_01_cache_book_quickstart先⾃⼰⽤java的HashMap模拟⼀个缓存12345@GetMapping("{id}")//

模拟缓存public R getById(@PathVariable Integer id) { return new R(true, d(id));}111213@Override//

模拟缓存private HashMap cache = new HashMap<>();public Book getById(Serializable id) { //

如果当前缓存中没有本次要查询的数据,则进⾏查询,否则直接从缓存中获取数据返回 Book book = (id); if (book == null) { book = d(id); (id, book); } return book;}springboot_16_01_02_cache_book_simple使⽤spring中⾃带的缓存技术在中添加如下依赖12345 spring-boot-starter-cache在springboot启动类上加@EnableCaching注解在业务层要使⽤缓存的⽅法上加上@Cacheable(value = "cacheSpace", key = "#id")注解,如下所⽰12345@Cacheable(value = "cacheSpace", key = "#id")public Book getById(Serializable id) { n("id = " + id); return d(id);}其中value表⽰缓存空间,key=“#id”,表⽰将⽅法参数id的值作为缓存中的⼀个key。springboot_16_01_03_cache_smscode_simple1. ⼿机验证码案例基础代码准备1. 存储⼿机号和验证码的实体类12345@Datapublic class SMSCode { private String phone; private String code;}2. ⽣成验证码的⼯具类123456789@Componentpublic class CodeUtils { private final String padding = "000000"; //

⽣成验证码(位数少于6位左边填充0,填充⽅法1) public String generateCode(String phone) { int hash = de(); int encryption = 20228888; long result = hash ^ encryption; long nowTime = me();972829363738394647484956 long nowTime = me(); result = (result ^ nowTime) % 1000000; String code = result + ""; // code = phone; // ing(()) () // 6 // 0 5 // 00 4 // 000 3 // 000 3 // 0000 2 // 00000 1 // 000000 0 code = ing(()) + code; // n(code); return code; } private final String[] patch = {"000000", "00000", "0000", "000", "00", "0", ""}; //

⽣成验证码(位数少于6位左边填充0,填充⽅法2) public String generateCode1(String phone) { int hash = de(); int encryption = 20228888; long result = hash ^ encryption; long nowTime = me(); result = (result ^ nowTime) % 1000000; String code = result + ""; // code = phone; // patch[] () // 000000 0 // 00000 1 // 0000 2 // 000 3 // 00 4 // 0 5 // 6 code = patch[()] + code; // n(code); return code; } //

根据⼿机号从缓存中获取验证码,缓存中有的话返回缓存中的值,没有的话就返回null @Cacheable(value = "smsCode", key = "#phone") public String getCodeByPhoneFromCache(String phone) { return null; }}3. 业务层接⼝12345public interface SMSCodeService { String sendCodeToSMS(String phone); boolean checkCode(SMSCode smsCode);}业务层接⼝实现类1234@Servicepublic class SMSCodeServiceImpl implements SMSCodeService { @Autowired private CodeUtils codeUtils;4567891 private CodeUtils codeUtils; @Override public String sendCodeToSMS(String phone) { return teCode(phone); } @Override public boolean checkCode(SMSCode smsCode) { return false; }}2. 加⼊spring默认的缓存功能1. 在中添加缓存依赖12345 spring-boot-starter-cache2. 在SMSCodeServiceImpl的sendCodeToSMS()⽅法上添加@CachePut(value = "smsCode", key = "#phone"),如下所⽰@Override// @Cacheable(value = "smsCode", key = "#phone")//

这⾥@Cacheable注解不适⽤,因为@Cacheable注解的功能是:如果缓存中没有值就去执⾏⼀次⽅法,然后将值存到缓存中,//

如果有值就直接从缓存中取值并返回,并不会执⾏⽅法,因⽽缓存中值不会发⽣改变。//

⽽⽤户点击界⾯发送⼀次验证码就调⽤了⼀次该⽅法,需要获取到新的验证码。//

使⽤新的缓存注解@CachePut可以解决这个问题,每次调⽤都会执⾏⽅法,向缓存中存新的值并返回@CachePut(value = "smsCode", key = "#phone")public String sendCodeToSMS(String phone) { return teCode(phone);}3. 编写checkCode()⽅法:校验验证码是否正确错误的写法:1234567891@Overridepublic boolean checkCode(SMSCode smsCode) { //

取出缓存中的验证码并与传递过来的验证码⽐对,如果相同,返回true,否则,返回false //

⽤户填写的验证码 String code = e(); //

缓存中存的验证码 String cacheCode = getCodeByPhoneFromCache(ne()); return (code);}//

根据⼿机号从缓存中获取验证码,缓存中有的话返回缓存中的值,没有的话就返回null@Cacheable(value = "cacheSpace", key = "#phone")public String getCodeByPhoneFromCache(String phone) { return null;}在getCodeByPhoneFromCache()⽅法上加了@Cacheable(value = "cacheSpace", key = "#phone"),然后在checkCode()⽅法中调⽤getCodeByPhoneFromCache()⽅法,这种⽅式看似是正确的,实际上@Cacheable注解不会⽣效,导致getCodeByPhoneFromCache()的返回值始终是null。这是由于被spring管理的类内的⽅法互调注解不会被解析。由此可以想到解决办法,将getCodeByPhoneFromCache()放到另外⼀个类(这⾥为了⽅便起见,直接放到CodeUtils类中),并将这个类交由spring管理(在类上⾯加@Component注解),然后再⽤eByPhoneFromCache(ne())即这个类交由spring管理(在类上⾯加@Component注解),然后再⽤eByPhoneFromCache(ne())即可正常从缓存中拿到值。代码如下:123456789@Overridepublic boolean checkCode(SMSCode smsCode) { //

取出缓存中的验证码并与传递过来的验证码⽐对,如果相同,返回true,否则,返回false //

⽤户填写的验证码 String code = e(); //

缓存中存的验证码 String cacheCode = eByPhoneFromCache(ne()); return (code);}springboot_16_01_04_cache_smscode_ehcache基于验证码案例的代码和配置,使⽤ehcache替换spring默认的simple缓存1. 在中加⼊ehcache的依赖12345 e ehcache2. 在中加⼊如下配置123456spring: cache: type: ehcache #

如果配置⽂件改名了,可以打开下⾯的配置指定配置⽂件路径 # ehcache: # config: 3. 在resources⽬录下新建⼀个配置⽂件,内容如下:8293031323334 直接启动项⽬,并且验证码获取和验证的过程不报错,说明缓存替换成成功。LRU: Least recently used, 淘汰最近最少被使⽤的数据LFU: Least frequently used,淘汰使⽤频率最低的数据springboot_16_01_05_cache_smscode_redis基于验证码案例的代码和配置,使⽤redis替换spring默认的simple缓存1. 在中加⼊redis的依赖12345 spring-boot-starter-data-redis2. 在中加⼊如下配置111213spring: cache: type: redis redis: #

是否使⽤缓存命名空间作为前缀,如:smsCode::,默认为true use-key-prefix: true cache-null-values: true #

是否缓存空值 key-prefix: aa # use-key-prefix为false的时候该项不⽣效 time-to-live: 10s #

缓存失效时间 redis: host: 192.168.0.110 port: 6379 password: 123456直接启动项⽬,并且验证码获取和验证的过程不报错,说明缓存替换成成功。springboot_16_01_06_cache_smscode_memcached基于验证码案例的代码和配置,使⽤memcached替换spring默认的simple缓存,memcached最新的客户端技术是xmemcached1. 在中加⼊xmemcached的依赖123456 ched xmemcached 2.4.72. 由于springboot并未收录memcached,所以只能通过硬编码的⽅式完成相关配置123456789@Componentpublic class XMemcachedConfig { @Bean public MemcachedClient getMemcachedClient() throws IOException { MemcachedClientBuilder memcachedClientBuilder = new XMemcachedClientBuilder("192.168.0.102:11211"); MemcachedClient memcachedClient = (); return memcachedClient; }}82930@Servicepublic class SMSCodeServiceImpl implements SMSCodeService { @Autowired private CodeUtils codeUtils; @Autowired private MemcachedClient memcachedClient; @Override public String sendCodeToSMS(String phone) { String code = teCode(phone); try { (phone, 10, code); } catch (Exception e) { tackTrace(); } return code; } @Override public boolean checkCode(SMSCode smsCode) { String code = null; try { code = (ne()).toString(); } catch (Exception e) { tackTrace(); } return e().equals(code); }}直接启动项⽬,并且验证码获取和验证的过程不报错,说明缓存替换成成功。