概念:项目刚启动时需要完成重启+重载,而热部署只要重启即可(即仅加载当前开发者自定义开发的资源,不加载jar资源)
- 重启:加载自定义开发代码,包含类、页面、配置文件等,加载位置在
restart
类加载器- 重载:加载
jar
包(后续不需要再重复加载),加载位置在base
类加载器手动启动热部署:
- 添加依赖包:
<dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-devtoolsartifactId> <optional>trueoptional> dependency>
- 激活热部署:每次修改代码后,点击
build project
(快捷键为Ctrl + F9
)自动启动热部署:每次修改代码后并且编辑器失去焦点后便会自动进行热部署
- 设置自动构建项目:
- 设置自动构建项目:打开全局搜索(
Ctrl+N
),在Actions
中搜索Registry
自定义不参与重启(热部署)的文件/文件夹:不想每次修改某文件的代码后去就自动重启进行热部署,可以进行相应配置
# 写在application.yml中即可 devtools: restart: exclude: public/**,static/**
- 关闭热部署:热部署只需要在开发环境中有效,所以需要能随时关闭热部署。热部署的关闭可以在配置文件中进行,但该操作并不是优先级最高的方式,因此有可能会被其他方式的配置覆盖导致热部署又开启,所以需要进行最高优先级的配置
public static void main(String[] args) { System.setProperty("spring.devtools.restart.enabled", "false"); SpringApplication.run(SSMPApplication.class); }
- 第三方
bean
绑定属性:假设引入了DruidDataSource
后需要设置数据库连接信息。此时使用@ConfigurationProperties
接口,该注解除了可以为自定义的属性进行属性绑定(可参考01-基础篇/获取yml
配置文件中的属性值),还可以进行第三方bean
的属性绑定// 不使用@ConfigurationProperties @Bean public DruidDataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); ds.setDriverClassName("test1"); return ds; } // 使用@ConfigurationProperties @Bean @ConfigurationProperties(prefix = "datasource") public DruidDataSource dataSource(){ DruidDataSource ds = new DruidDataSource(); return ds; } // 测试代码 public static void main(String[] agrs){ ConfigurableApplicationContext ctx = SpringApplication.run( SpringbootLearnApplication.class, args ); DruidDataSource ds = ctx.getBean(DruidDataSource.class); System.out.println(ds.getDriverClassName()); }
datasource: driverClassName: test2
宽松绑定:
@ConfigurationProperties
绑定属性支持属性名宽松绑定,比如下面配置均能匹配上属性ipAddress
servers: ipAddress: 192.168.1.1 # 下面三种均可 # ip_address: 192.168.1.2 # IP_ADDRESS: 192.168.1.4 # ip-address: 192.168.1.3 # 主流模式 port: 2345 timeout: -1
@Data @ConfigurationProperties(prefix="servers") public class ServerConfig { private String ipAddress; private int port; private long timeout; } // 测试代码 public static void main(String[] agrs){ ConfigurableApplicationContext ctx = SpringApplication.run( SpringbootLearnApplication.class, args ); ServerConfig bean = ctx.getBean(ServerConfig.class); System.out.println(bean); }
- 不支持注解
@Value
引用单个属性的方式:即在yml
配置文件中使用的名称必须和类中属性名一致@Value("${servers.ipAddress}") private String msg; // 测试代码 @Test void contextLoads(){ // 如果在配置文件中不是以下写法则会报错 // servers: // ipAddress: 192.168.1.1 System.out.println(msg); }
@ConfigurationProperties
绑定仅能使用纯小写字母、数字、下划线作为合法的字符@Bean // "datasource"必须全部为小写,即使在配置文件中写的是dataSource:xxx @ConfigurationProperties(prefix = "datasource") public DruidDataSource datasource(){ DruidDataSource ds = new DruidDataSource(); return ds; }
数据校验:在
yml
配置文件中定义的值可能与类中属性类型不匹配,需要先进行校验
- 添加
JSR303
规范坐标与Hibernate
校验框架对应坐标:<dependency> <groupId>javax.validationgroupId> <artifactId>validation-apiartifactId> dependency> <dependency> <groupId>org.hibernate.validatorgroupId> <artifactId>hibernate-validatorartifactId> dependency>
- 对
Bean
开启校验功能并设置具体规则:@Component @Data @ConfigurationProperties(prefix = "servers") @Validated // 开启校验 public class ServerConfig { @Max(value = 400,message = "最大值不能超过400") // 设置具体规则 private int port; }
加载测试专用属性:该属性仅对当前测试类有效,缩小作用范围,降低冲突
- 在启动测试环境时可以通过
properties
参数设置测试环境专用的属性// 在yml配置文件中不配置test.prop属性也会生效,如果都配置了则该处配置的优先级大于配置文件中的 @SpringBootTest(properties = {"test.prop=testValue1"}) public class PropertiesAndArgsTest { @Value("${test.prop}") private String msg; @Test void testProperties(){ System.out.println(msg); // 输出testValue1 } }
- 在启动测试环境时可以通过
args
参数设置测试环境专用的命令行参数// 相当于之前在命令行中添加属性(所以是--xxx.xxx的形式),如果在yml配置文件和properties中都配置了相同属性,该处的优先级最高(命令行的设置优先级高) @SpringBootTest(args = {"--test.arg=testValue2"}) public class PropertiesAndArgsTest { @Value("${test.arg}") private String msg; @Test void testArgs(){ System.out.println(msg); // 输出testValue2 } }
- 使用
@Import
注解加载当前测试类专用的配置// 因为该类只需要在测试环境生效,所以不能定义在src目录下,应该定义在test目录下 @Configuration public class MsgConfig { @Bean public String msg(){ return "bean msg"; } }
@SpringBootTest @Import(MsgConfig.class) public class ConfigurationTest { @Autowired private String msg; @Test void testConfiguration(){ System.out.println(msg); // 输出bean msg } }
Web
环境模拟测试:之前在测试类对数据层和业务层进行了测试,并未涉及到表现层的测试,测试表现层使用的是Postman
// 以下测试通过后没有提示信息,只有未通过才会有信息 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // 模拟端口 @AutoConfigureMockMvc //开启虚拟MVC调用 public class WebTest { @Test void testGetById(@Autowired MockMvc mvc) throws Exception { // 1.创建虚拟请求,访问/books MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books"); // 执行对应的请求 ResultActions action = mvc.perform(builder); // 2.测试状态是否匹配 // 定义本次调用的预期值(下面同理) StatusResultMatchers status = MockMvcResultMatchers.status(); ResultMatcher ok = status.isOk(); // 添加预计值到本次调用过程中进行匹配(下面同理) action.andExpect(ok); // 3.测试响应头是否匹配 HeaderResultMatchers header = MockMvcResultMatchers.header(); ResultMatcher contentType = header.string("Content-Type", "application/json"); action.andExpect(contentType); // 4.测试响应体(json格式)是否匹配 ContentResultMatchers content = MockMvcResultMatchers.content(); ResultMatcher result = content.json("{\"id\":1,\"name\":\"springboot\",\"type\":\"springboot\",\"description\":\"springboot\"}"); action.andExpect(result); } }
- 业务层测试事务回滚:每次执行测试只是想测试功能的运行情况,并不想在数据库中留下测试的数据
@SpringBootTest @Transactional // 为测试用例添加事务,执行了测试代码后数据库不会存在测试数据,但是id会加1,下次插入数据时id比上一条数据的大2 // @Rollback(false) // 如果想留下测试数据,则在使用了@Transactional的前提下添加上该注解并设置为false public class DaoTest { @Autowired private BookService bookService; // 具体操作数据库的代码 ... }
- 测试用例数据设定:测试用例数据通常采用随机值进行测试,使用
SpringBoot
提供的随机数为其赋值# 其中()可以是任意字符,例如[],!!均可 # 测试的设定代码写在测试环境的配置文件中 testcast: book: id: ${random.int} # 随机整数 id2: ${random.int(10)} # 10以内随机数 type: ${random.int(10,20)} # 10到20随机数 uuid: ${random.uuid} # 随机uuid name: ${random.value} # 随机字符串,MD5字符串,32位 publishTime: ${random.long} # 随机整数(long范围)
现有数据层解决方案技术选型:数据源
DruidDataSource
+ 持久化技术:MyBatis-Plus/MyBatis
+ 数据库MySQL
- 数据源配置:假设不在配置文件中声明使用
Druid
数据源,并且不导入Druid
的依赖包,则会使用SpringBoot
内置数据源
HikariCP
:默认内置数据源对象Tomcat
提供DataSource
:HikariCP
不可用的情况下,且在web
环境中,将使用tomcat
服务器配置的数据源对象Commons DBCP
:Hikari
不可用,tomcat
数据源也不可用,将使用dbcp
数据源spring: datasource: druid: # 把该行删除,使用内置数据源 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/xxxx?serverTimezone=UTC username: xxxx password: xxxx # 通用配置无法设置具体的数据源配置信息,仅提供基本的连接相关配置,如需配置,在下一级配置中设置具体设定 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/xxxx username: xxxx password: xxxx hikari: maximum-pool-size: 50
- 内置持久化解决方案-
JdbcTemplate
:现在不使用MyBatis
的依赖,并且导入spring-boot-starter-jdbc
的依赖@SpringBootTest class SpringBootApplicationTest{ @Autowired private JdbcTemplate jdbcTemplate; @Test void testJdbc(){ String sql "select from tbl_book where id 1"; List<Book>query jdbcTemplate.query(sql,new RowMapper<Book>(){ @Override public Book mapRow(ResultSet rs,int rowNum) throws SQLException{ Book temp new Book(); temp.setId(rs.getInt("id")); temp.setName(rs.getstring("name")); temp.setType(rs.getstring("type")); temp.setDescription(rs.getstring("description")); return temp; } }); System.out.println(query); } }
- 内嵌数据库:
SpringBoot
提供了三种内嵌数据库(导入对应的依赖包就并且配置连接信息就能使用)供开发者选择,都在内存运行,运行速度快,提高开发测试效率
H2
HSQL
Derby
tips:
- 即使不在配置文件声明使用
Druid
,SpringBoot
的自动装配也会找到依赖的包并且使用对应的数据源,就和导入web
依赖包就能注解使用内置的Tomcat
服务器一个道理H2
数据库控制台仅用于开发阶段,线上项目请务必关闭控制台功能,要不然会存在安全隐患spring: h2: console: path: /h2 enabled: false
SpringBoot
可以根据url
地址自动识别数据库种类,所以在保障驱动类存在的情况下可以省略配置(即省略driver-class-name
)
上述的数据层解决方案是基于
SQL
的,现在介绍三种基于NoSQL
的
Redis
:
概念:
Redis
是一款key-value
存储结构的内存级NoSQL
数据库
- 支持多种数据存储格式
- 支持持久化
- 支持集群
下载与安装(以
Windows
为例):
- 下载(https://github.com/tporadowski/redis/releases)后在
Windows
解压安装或一键式安装- 服务端启动命令:
redis-server.exe redis.windows.conf
- 客户端启动命令:
redis-cli.exe
SpringBoot
整合Redis
:
- 导入
SpringBoot
整合Redis
的依赖包:<dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-data-redisartifactId> dependency>
- 配置
Redis
:spring: redis: host: localhost # 127.0.0.1 port: 6379
- 使用
RedisTemplate/StringRedisTemplate
操作Redis
:
RedisTemplate
以对象作为key
和value
,内部对数据进行序列化- **
StringRedisTemplate
**以字符串作为key
和value
,与Redis
客户端操作等效(常用)@SpringBootTest class NosqlApplicationTests{ @Autowired StringRedisTemplate redisTemplate; @Test void set(){ ValueOperations ops = redisTemplate.opsForValue(); ops.set("testKey","testvalue"); } }
客户端选择:可以选择使用
lettcus
(默认)与jedis
。如果要切换为jedis
则需要先导入jedis
的依赖包,然后在配置文件中声明client-type: lettuce
jedis
连接Redis
服务器是直连模式,当多线程模式下使用jedis
会存在线程安全问题,解决方案可以通过配置连接池使每个连接专用,但这样整体性能受影响lettcus
是基于Netty
框架进行与Redis
服务器连接,底层设计中采用StatefulRedisConnection
,它自身是线程安全的,可以保障并发访问安全问题,所以一个连接可以被多线程复用。lettcus
也支持多连接实例一起工作
Mongo
:
概念:
MongoDB
是一个开源、高性能、无模式的文档型数据库,是最像关系型数据库的非关系型数据库,它可以存储游戏装备数据、直播数据、物联网数据等临时存储且修改频度高的数据下载与安装(以
Windows
为例):
- 下载(https://www.mongodb.com/try/download)后在
Windows
解压并设置数据目录- 服务端启动命令:
mongod --dbpath=..\data\db
- 客户端启动命令:
mongo --host=127.0.0.1 --port=27017
SpringBoot
整合Mongo
:
- 导入
Mongodb
驱动:<dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-data-mongodbartifactId> dependency>
- 配置客户端:
spring: data: mongodb: uri: mongodb://localhost/xxxx
- 客户端读写
Mongodb
:@Test void testSave(@Autowired MongoTemplate mongoTemplate){ Book book = new Book(); book.setId(1); book.setType("springboot"); book.setName("springboot"); book.setDescription("springboot"); mongoTemplate.save(book); }
ES
:
概念:
Elasticsearch
是一个分布式全文搜索引擎下载与安装(以
Windows
为例):
- 下载(https://www.elastic.co/cn/downloads/elasticsearch)后在
Windows
解压- 运行
bin/elasticsearch.bat
SpringBoot
整合ES
:
- 导入坐标:
SpringBoot
平台并没有跟随ES的更新速度进行同步更新,所以不使用spring-boot-starter-data-elasticsearch
<dependency> <groupId>org.elasticsearch.clientgroupId> <artifactId>elasticsearch-rest-high-level-clientartifactId> dependency>
- 客户端代码:
@Test void test() throws IOException { HttpHost host = HttpHost.create("http://localhost:9200"); RestClientBuilder builder = RestClient.builder(host); RestHighLevelClient client = new RestHighLevelClient(builder); // 客户端操作 CreateIndexRequest request = new CreateIndexRequest("books"); // 获取操作索引的客户端对象,调用创建索引操作 client.indices().create(request, RequestOptions.DEFAULT); // 关闭客户端 client.close(); }
tips:
- 如果启动不了
Redis
的服务端,则需要先启动客户端,然后执行shutdown
退出客户端,再去启动服务端即可- 在安装
Mongo
时可能会报找不到某个dll
文件,则需要:
- 下载对应的
dll
文件- 拷贝到
windows
安装路径下的system32
目录中- 执行命令注册对应
dll
文件:regsvr32 xxxx.dll
缓存:
概念:
- 缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质
- 使用缓存可以有效的减少低速数据读取过程的次数(例如磁盘IO),提高系统性能
- 缓存不仅可以用于提高永久性存储介质的数据读取效率,还可以提供临时的数据存储空间(即存储代码运行过程中生成的数据)
使用:以默认的缓存方案为例(还有
Redis
、Ehcache
、memcached
等其他缓存技术,但是接口是统一的)
- 导入缓存技术对应的
starter
:<dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-cacheartifactId> dependency>
- 启用缓存:
@SpringBootApplication @EnableCaching // 开启缓存 public class CacheApplication { public static void main(String[] args) { SpringApplication.run(CacheApplication.class, args); } }
- 设置当前操作的结果数据进入缓存:
// value指定缓存放在哪里,key表示下次用什么去找该缓存好的数据 @Cacheable(value="cacheSpace", key="#id") public Book getById(Integer id) { // 初次执行会去数据库中查,后面不会,直接从缓存中取 return bookDao.selectById(id); }
任务:
概念:
定时任务是企业级应用中的常见操作,比如制作年度报表、缓存统计报告等
市面上流行的定时任务技术有
Quartz
、Spring Task
相关词汇:
- 工作(
Job
):用于定义具体执行的工作- 工作明细(
JobDetail
):用于描述定时工作相关的信息- 触发器(
Trigger
):用于描述触发工作的规则,通常使用cron
表达式定义调度规则- 调度器(
Scheduler
):描述了工作明细与触发器的对应关系使用:
SpringBoot
整合Quartz
- 导入
SpringBoot
整合quartz
的坐标:<dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-starter-quartzartifactId> dependency>
- 定义具体要执行的任务,继承
QuartzJobBean
:// 具体的工作,只需要声明为普通的类,不需要给Spring管理 public class QuartzTaskBean extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { System.out.println(“quartz job run... "); } }
- 定义工作明细与触发器,并绑定对应关系:
@Configuration public class QuartzConfig { @Bean public JobDetail printJobDetail(){ // 绑定具体的工作 return JobBuilder.newJob(QuartzTaskBean.class).storeDurably().build(); } @Bean public Trigger printJobTrigger() { CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/3 * * * * ?"); return TriggerBuilder .newTrigger() .forJob(printJobDetail()) .withSchedule(cronScheduleBuilder).build(); } }
Spring Task
:
- 开启定时任务功能:
@SpringBootApplication @EnableScheduling public class TaskApplication { public static void main(String[] args) { SpringApplication.run(TaskApplication.class, args); } }
- 设置定时执行的任务,并设定执行周期:
@Component public class ScheduledBean { @Scheduled(cron = "0/5 * * * * ?") public void printLog(){ System.out.println(Thread.currentThread().getName()+":run..."); } }
- 定时任务相关配置:
spring: task: scheduling: #任务凋度线程池大小,默认为1 pool: size: 1 #调度线程名称前缀,默认为scheduling- thread-name-prefix: xxx_ shutdown: # 线程池关闭时等待所有任务完成 await-termination: false # 调度线程关闭前最大等待时间,确保最后一定关闭 await-termination-period: 10s
消息:
概念:
- 处理消息的双方:分为消息发送方(生产者)和消息接收方(消费者)
- 消息分类:异步(不需要对方进行确认后也能接着发)和同步(需要对方进行确认后才能接着发)消息
- 消息队列:单个业务系统处理所有请求的压力太大,所以将每个请求转换为一个个的消息并存储在消息队列(
MQ
)中,而子业务系统则从中获取消息去处理。其中所有MQ
技术均需要遵循JMS
、AMQP
、MQTT
中的规范或协议
JMS(Java Message Service)
:一个规范,等同于JDBC
规范,提供了与消息服务相关的API
接口
JMS
消息模型:
peer-2-peer
:点对点模型,消息发送到一个队列中后队列保存消息。队列的消息只能被一个消费者消费,或超时publish-subscribe
:发布订阅模型,消息可以被多个消费者消费,生产者和消费者完全独立,不需要感知对方的存在JMS
消息种类:
extMessage
MapMessage
BytesMessage
StreamMessage
ObjectMessage
Message
(只有消息头和属性)JMS
实现:ActiveMQ
、Redis
、HornetMQ
、RabbitMQ
、RocketMQ
(没有完全遵守JMS
规范)
AMQP(advanced message queuing protocol)
:一种协议,规范了网络交换的数据格式,兼容JMS
,具有跨平台性,服务器供应商、生产者和消费者可以使用不同的语言来实现
AMQP
消息模型:
direct exchange
fanout exchange
topic exchange
headers exchange
system exchange
AMQP
消息种类:byte[]
AMQP
实现:RabbitMQ
、StormMQ
、RocketMQ
MQTT(Message Queueing Telemetry Transport)
:消息队列遥测传输,专为小设备设计,是物联网生态系统中主要成分监控:
- 意义:
- 监控服务状态是否宕机
- 监控服务运行指标(内存、虚拟机、线程、请求等)
- 监控日志
- 管理服务(服务下线)
- 实施方式:
- 显示监控信息的服务器:用于获取服务信息(可以主动拉取,也可以被动获取),并显示对应的信息
- 运行的服务:启动时主动上报,告知监控服务器自己需要受到监控(告知服务器自己是否需要被监控,哪些部分被监控)
- 原理:
Actuator
提供了SpringBoot
生产就绪功能,通过端点(其实就是请求,每个请求可以获取到不同的监控信息)的配置与访问,获取端点信息- 端点描述了一组监控信息,
SpringBoot
提供了多个内置端点,也可以根据需要自定义端点信息- 访问当前应用所有端点信息:
/actuator
;访问端点详细信息:/actuator/端点名称
(比如访问/actuator/health
获取到应用程序的健康信息)
@EnableConfigurationProperties
可将使用@ConfigurationProperties
对应的类加入Spring
容器,所以@EnableConfigurationProperties
与@Component
不能同时使用// 之所以将@Component注释是因为在主程序中用@EnableConfigurationProperties将ServerConfig类完成了注入,再加会报错 // @Component @Data @ConfigurationProperties(prefix = "servers") public class ServerConfig {...} // 主程序 @SpringBootApplication @EnableConfigurationProperties(ServerConfig.class) public class DemoApplication {}
IDEA
有时会报识别不了@ConfigurationProperties
注解,可添加下面依赖接触警告:<dependency> <groupId>org.springframework.bootgroupId> <artifactId>spring-boot-configuration-processorartifactId> dependency>
yml
文件中对于数字的定义支持进制书写格式,如需使用字符串请使用引号明确标注,要不然会出现以下情况:# 配置文件 dataSource: password: 0127 # 需要写为"0127"
@Value("${dataSource.password}") private String password; // 测试代码 @Test void contextLoads(){ System.out.println(password); // 输出87: 因为0127会被识别为二进制,然后转换为十进制输出 }
视频参考黑马程序员SpringBoot2全套视频教程 P70-P142