文章目录
- 一、基础功能
- 1.1 属性绑定 @ConfigurationProperties
- 1.2 YAML 文件(或者YML)
- 1.3 Spring 启动的配置和方式
- (1)分布式地启动程序
- (2)构建器启动【了解】
- 二、日志
- 2.1 为什么要有日志
- 2.2 日志记录(使用例)
- 2.3日志级别
- 2.4 日志分组
- 2.5 日志存储(存到文件)
- 2.6 文件归档和滚动切割
- 2.7日志配置
- ▽ 扩展·日志占位符->{ }
- 三、进阶
- 3.1 环境隔离
- (1)定义环境
- (2)激活环境
- ▽ 包含环境【了解】
- (3)分组
- (4)总结
- 3.2 外部化配置
- 3.3 单元测试和断言机制
- (1)单元测试注解
- (2)断言机制
- 3.4 可观测性 【了解】
- (1)实现方式
- (2)一些快速暴露后的信息端口
- 四、核心原理
- 4.1 生命周期监听器【了解】
- (1)生命周期监听器的种类
- (2)使用例^(以SpringApplicationRunListener为例)^
- (3)最佳实践
- 4.2 生命周期事件【了解】
- 4.3 事件驱动 开发【了解】
- (1)**流程:**
- ▽ 回忆·Spring自动异步处理
- ▽ 扩展·分布式事件^(消息)^模式
- 4.4 自动配置原理
- (1)原理流程图
- (2)实践·自定义starter
- 4.5 总结
一、基础功能
1.1 属性绑定 @ConfigurationProperties
对于一些注入的组件类(@Component、@Bean),为了动态取值,或者动态调控。在程序正式启动前,我们会把他们的属性和配置文件绑定,如果配置文件的对应属性有值的话,一般优先取配置文件的值到组件中,然后程序启动组件正常使用
例:
-
配置文件
emp.name=APQ emp.password=12345
-
Bean 组件(注入到容器中的才能进行属性绑定)
java">@Data @Component @ConfigurationProperties(prefix = "emp") public class Emp {private String name;private String password; }
-
测试
java">@SpringBootTest class SpringBoot01ApplicationTests {@AutowiredEmp emp;@Testvoid contextLoads() {System.out.println("emp:"+emp);System.out.println("emp.name:"+emp.getName());}}
注意点
@ConfigurationProperties
自动绑定在老版本中只能通过:在主程序前标识@EnableConfigurationProperties(Bean组件.class)
来开启属性绑定并注入到容器中。不过在较新的版本中被简化了,但是对于大型项目我们仍会使用老的做法,方便控制注入组件到容器
1.2 YAML 文件(或者YML)
.properties文件作为配置文件时:aaa.bbb.ccc=www,我们要对照每个点后的单词,这种写法层次性太差了。为了改善层次,我们就有了YAML文件
例:
# 对于对象,可以这样写:
pets:dogs:name: Rufusage: 3cats:name: Fluffyage: 5# 对于数组,可以这样写:
pets1:- dog- cat- fish# 对于数组,也可以这样写
pets2: [dog, cat, fish]# 对于Mapper,可以这样写:
mapper:dogs: aaacats: bbb
注意:
- 虽然形式和JSON的键值对显示很像,但是
- 不用双引号
- 在“ : ”冒号后还需要接一个空格
1.3 Spring 启动的配置和方式
在之前的学习中,我们对于项目的正常启动只是用:
SpringApplication.run(项目主类.class, args);
,但是这种方法之外还可以使用别的方法来启动
(1)分布式地启动程序
SpringApplication.run(主项目类名.class, args);
的本质是统合了应用创建创建SpringApplication对象和应用启动调用SpringApplication的application.run方法。其中
主项目类名
指定了项目,args
是main方法的形参传入值
- 由于我们不使用一行代码来启动程序了,这时可以对于创建的SpringApplication进行调整配置,优化运行逻辑
java">// 对main方法修改:
public static void main(String[] args) {// 1. 创建SpringApplication对象SpringApplication app = new SpringApplication(SpringBoot01Application.class);// 2. 设置配置文件app.setBannerMode(Banner.Mode.OFF);// 例如关闭banner// 3. 启动SpringApplicationapp.run(args);
}
(2)构建器启动【了解】
java">// 构建器模式
SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringBoot01Application.class);
builder.bannerMode(Banner.Mode.OFF).environment(null);
// 启动
builder.run(args);
二、日志
2.1 为什么要有日志
在之前,我们大部分时候对于一些服务器运行信息的展示都是打印到控制台上,但是这种方法的效率低、实用性差。为了高效地获取服务器运行信息,我们使用日志框架实现。(Spring自带了slf4j来实现日志)
例如SpringBoot启动时会展示好几行信息,这些是其启动日志。
日志框架可以和原来一样把信息打印上控制台,更进一步,能自动存储运行信息到指定位置,并在时间、空间上实现各项操作
2.2 日志记录(使用例)
由于日志是使用外部类实现,我们通过日志类的实例调用其方法,实现日志的记录
例1:
java">@Test
void test1() {// 实际使用不是说要用于测试类,这里为了方便直接在测试类中用了// 1、获取日志记录器对象(是于org.slf4j.LoggerFactory类,小心别导错了)Logger logger = LoggerFactory.getLogger(SpringBoot02LogApplicationTests.class);// 2、日志记录logger.info("信息日志");logger.error("错误日志");logger.debug("调试日志");logger.warn("警告日志");logger.trace("跟踪日志");logger.trace("致命错误日志", new RuntimeException("致命错误"));}
- **注意:**导入的是于org.slf4j.LoggerFactory类,小心别导错了junit里面的LoggerFactory
例2(更方便)
java">@Slf4j
@SpringBootTest
class SpringBoot02LogApplicationTests {// 实际使用不是说要用于测试类,这里为了方便直接在测试类中用了@Testvoid test2() {// 一样的效果,但是使用@Slf4j注解,更方便log.info("信息日志");log.error("错误日志");log.debug("调试日志");}}
- **注意:**该方法的前提是项目中存在lombok依赖(该死的IDEA,lombok总是导不对)
2.3日志级别
意如其名,决定了每种方式的级别高低
级别分类:
-
ALL:打印所有日志
-
TRACE:追踪框架详细流程日志,一般不使用
-
DEBUG:开发调试细节日志
-
INFO:关键、感兴趣信息日志
-
WARN:警告但不是错误的信息日志,比如:版本过时
-
ERROR:业务错误日志,比如出现各种异常
-
FATAL:致命错误日志,比如jvm系统崩溃
-
OFF:关闭所有日志记录
日志级别从低到高依次是ALL、TRACE、DEBUG、INFO、WARN、ERROR、FATAL、OFF
。设置了日志级别则表示只展示该级别和其之上级别的信息
注意:
-
等级越低输出的日志信息越详细
-
SpringBoot日志默认级别是 INFO
logging.level.root=INFO
# root是指默认级别组,如果没有指定就用该级别
- Spring中只能用以下的级别
2.4 日志分组
对于日志级别的设置,我们可以通过设置默认或者是对于某个目录进行指定,前者过于宽泛、后者一旦要求多了就麻烦,为了便于管理操作,我们可以给项目中的各个目录进行分组:比如dao和service放一组。
例:
- 设置日志组别:将dao、service、controller放到一名为yyd的组别中
logging.group.yyd=com.syq.springboot01.properties,com.syq.springboot01.controller,com.syq.springboot01.service,com.syq.springboot01.dao
- 设置日志组的日志级别:为yyd整个组设置日志级别为debug
logging.level.yyd=debug
注意:
- 框架中也自带有组别,比如web什么的,要注意别重名
2.5 日志存储(存到文件)
为了保存日志,我们不打印到控制台了,改成存储.log文件了
- .name
logging.file.name=logs.log
- 保存到当前项目根文件夹下的指定.log文件(没有也会自动生成)
- .path
logging.file.path=D://logs.log
- 保存到当前指定目录下的存储位置,**注意:**这里演示写logs.log,实际保存时会以logs.log为目录名,在改目录中生成Spring.log文件
注意:
- 当两个方式都用了,会优先使用.name指定的,可以认为是编程的精确指定
2.6 文件归档和滚动切割
**归档:**把一天中运行产生的日志放入一个文档(打成了.gz压缩包)中
**滚动切割:**当文件超过指定大小时,把装满的文件交给归档处理
默认滚动切割与归档规则如下:
配置项 | 说明 |
---|---|
logging.logback.rollingpolicy.file-name-pattern | 日志存档的文件名格式 默认值:${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz |
logging.logback.rollingpolicy.clean-history-on-start | 应用启动时是否清除以前存档; 默认值:false |
logging.logback.rollingpolicy.max-file-size | 每个日志文件的最大大小; 默认值:10MB |
logging.logback.rollingpolicy.total-size-cap | 日志文件被删除之前,可以容纳的最大大小(默认值:0B)。 设置1GB则磁盘存储超过 1GB 日志后就会删除最老的日志文件 |
logging.logback.rollingpolicy.max-history | 日志文件保存的最大天数; 默认值:7 |
切割并归档后文件和.gz文档的命名?:
- **原文件:**前面我们知道储存两种方法都会生成一个.log文件,一个固定为Spring.log一个自己命名;而更新后的原文件仍用原名–所以我们如果通过文件管理器查看,当内容大小超了该文件仍然继续增加大小。而我们刷新结构,就能看到归档后的文档
- .gz文档:对于 ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz 格式,LOG_FILE代表原.log文件名,yyyy-MM-dd代表日期,%i代表一天中的第几个文档
注意:
- 因为使用Logback,如果项目突然关闭,那么.log文件也会被转化为文档
2.7日志配置
自定义配置格式、大小等等等,通常我们配置 application.properties 就够了,当然如果真要完全自定义。每个日志框架的配置方式都不同,所以要用时上网搜或者让ai生成,当然为了统一的话公司可能会规定死了
日志系统 | 自定义 |
---|---|
Logback | logback-spring.xml / logback.xml |
Log4j2 | log4j2-spring.xml / log4j2.xml |
JDK (Java Util Logging) | logging.properties |
▽ 扩展·日志占位符->{ }
java">@Slf4j
@SpringBootTest
public class LogTest {@Testvoid test3() {log.info("{}日志", "测试");String str = "测试";log.info("{}日志", str);}
}
- 用{ }号占位,后面跟字符串、变量、对象的toString
三、进阶
3.1 环境隔离
在业务中的同一个项目,我们可能需要有开发、测试、生产等多个运行环境。为了更好的运行,我们使用环境隔离的方式,使组件、配置文件等内容分在不同环境下运行
(1)定义环境
定义环境,使组件、配置文件等内容分在不同环境下运行
-
对于组件:
通过为组件类标识
@Profile("【对应环境名】")
,限制只有在对应环境下才能使用该组件 -
对于配置文件:
和主配置类类似,
applicatiion-【对应环境名】.properties
- 可以认为和类的继承类似,新的配置文件会继承主配置文件的内容,当新文件配置了原有的内容,则会以新的配置优先
(2)激活环境
光有环境的配置设置没用,我们要指定激活其运行的环境
-
方法一:
在主配置文件中声明激活环境
sprring.profiles.active=dev
-
方法二:
在命令栏启动时指定激活环境
▽ 包含环境【了解】
sprring.profiles.active 只能激活一个环境,也许有时,我们需要多个环境互补,这时我们引入环境的保护
使用(类似于激活)
spring.profiles.include=abc,bcd,cde
- **注意:**包含能同时包含多个,如果包含的内容和激活的内容重复了,以激活的内容为准
(3)分组
通过分组,激活对应组合就能把其中的所有都激活了
例:
创建 prod 组,指定包含 aaa、ccc、bbb 配置
spring.profiles.group.prod[0]=aaa
spring.profiles.group.prod[1]=bbb
spring.profiles.group.prod[2]=ccc
(4)总结
生效的配置 = 默认的配置 + 激活的配置 + 包含的配置
- 默认的配置用来兜底,或者作为通用组成减少代码复用
- 配置以激活的为准,一个配置文件只能激活一个环境,可以通过文件互套来引用多个环境
- 包含的配置意义不大,因为其主要作用是抽取重复片段减少代码复用,但默认配置也能做到,而且不用开一堆配置文件
3.2 外部化配置
当我们的项目打包成一个.jar文件后无法对其配置修改,为了能在外边配置其内配置文件,Spring使用了外部化的配置方法
使用:
- 通过特定项目结构分层,把特定位置文件作为外部的配置进行读取运行
- 通过命令行来配置
**项目结构优先级:**激活优先 > 外层优先 ≥ 外部优先 > 读取内部
- **激活优先 > 外部优先:**当内部有dev环境配置外部没有,如果激活了dev,那么会优先读取内部dev配置
- **外层优先:**外部配置分了多层当/app 和/config 都有dev配置,那么读取dev时以/config的配置优先
- **外部优先 > 读取内部:**当内部有配置而外部同样有配置,那么优先读取外部配置
3.3 单元测试和断言机制
单元机制我们用的次数多,但是多是表层的@Test,其中还有别的注解api可以使用。
而在单元测试的同时我们可以使用断言这一功能,测试判断项目是否正常,帮助我们发现问题(类似于报错,但是这种报错更接近于业务要求的报错)
(1)单元测试注解
@Test
:表示方法是测试方法。- @ParameterizedTest :表示方法是参数化测试,下方会有详细介绍
- @RepeatedTest :表示方法可重复执行,下方会有详细介绍
@DisplayName
:为测试类或者测试方法设置展示名称@BeforeEach
:表示在每个单元测试之前执行@AfterEach
:表示在每个单元测试之后执行- @BeforeAll :表示在所有单元测试之前执行
- @AfterAll :表示在所有单元测试之后执行
- @Tag :表示单元测试类别,类似于JUnit4中的@Categories
- @Disabled :表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
- @Timeout :表示测试方法运行如果超过了指定时间将会返回错误
- @ExtendWith :为测试类或测试方法提供扩展类引用
(2)断言机制
- 使用场景:
比如对于一个类,它可能正确返回也可能返回一个null,如果不使用断言机制那么后续某步可能出错,但是要寻找错误原因则很麻烦;同样的情况下,我们在返回结果前使用Assertions.assertNotNull 断言,那么一旦有错误就会被程序发现,这样也便于我们修复bug
- 总览:
方法 | 说明 |
---|---|
assertEquals | 判断两个对象或两个原始类型是否相等 |
assertNotEquals | 判断两个对象或两个原始类型是否不相等 |
assertSame | 判断两个对象引用是否指向同一个对象 |
assertNotSame | 判断两个对象引用是否指向不同的对象 |
assertTrue | 判断给定的布尔值是否为 true |
assertFalse | 判断给定的布尔值是否为 false |
assertNull | 判断给定的对象引用是否为 null |
assertNotNull | 判断给定的对象引用是否不为 null |
assertArrayEquals | 数组断言 |
assertAll | 组合断言 |
assertThrows | 异常断言 |
assertTimeout | 超时断言 |
fail | 快速失败 |
- 很多时候标准的测试类都必须要有断言的存在,便于发现问题
3.4 可观测性 【了解】
当为了方便运行维护或者参数读取,我们的项目需要对外暴露一部分内容,比如一些内置数据、环境变量等等。这种能力就是可观测性:应用的运行数据,可以被线上进行观测、监控、预警等
(1)实现方式
原理:SpringBoot 提供了 actuator 模块,可以快速暴露应用的所有指标
-
导入: spring-boot-starter-actuator
-
访问 http://localhost:8080/actuator;(对应项目的端口+actuator)
(2)一些快速暴露后的信息端口
端点名 | 描述 |
---|---|
auditevents | 暴露当前应用程序的审核事件信息。需要一个AuditEventRepository组件 |
beans | 显示应用程序中所有Spring Bean的完整列表 |
caches | 暴露可用的缓存 |
conditions | 显示自动配置的所有条件信息,包括匹配或不匹配的原因 |
configprops | 显示所有@ConfigurationProperties |
env | 暴露Spring的属性ConfigurableEnvironment |
flyway | 显示已应用的所有Flyway数据库迁移。需要一个或多个Flyway组件。 |
health | 显示应用程序运行状况信息 |
httptrace | 显示HTTP跟踪信息(默认情况下,最近100个HTTP请求-响应)。需要一个HttpTraceRepository组件 |
info | 显示应用程序信息 |
integrationgraph | 显示Spring integrationgraph 。需要依赖spring-integration-core |
loggers | 显示和修改应用程序中日志的配置 |
liquibase | 显示已应用的所有Liquibase数据库迁移。需要一个或多个Liquibase组件。 |
metrics | 显示当前应用程序的“指标”信息 |
mappings | 显示所有@RequestMapping路径列表 |
scheduledtasks | 显示应用程序中的计划任务 |
sessions | 允许从Spring Session支持的会话存储中检索和删除用户会话。需要使用Spring Session的基于Servlet的Web应用程序 |
shutdown | 使应用程序正常关闭。默认禁用 |
startup | 显示由ApplicationStartup收集的启动步骤数据。需要使用SpringApplication进行配置BufferingApplicationStartup |
threaddump | 执行线程转储 |
heapdump | 返回hprof堆转储文件 |
jolokia | 通过HTTP暴露JMX bean(需要引入Jolokia,不适用于WebFlux)。需要引入依赖jolokia-core |
logfile | 返回日志文件的内容(如果已设置logging.file.name或logging.file.path属性)。支持使用HTTPRange标头来检索部分日志文件的内容 |
prometheus | 以Prometheus服务器可以抓取的格式公开指标。需要依赖micrometer-registry-prometheus |
四、核心原理
生命周期流程图:
4.1 生命周期监听器【了解】
类似于拦截器,但是:只有Spring启动时才触发,触发位置固定,触发不对项目启动影响
(1)生命周期监听器的种类
监听器 | 感知阶段 | 配置方式 |
---|---|---|
BootstrapRegistryInitializer | 特定阶段:引导初始化 | 1、META-INF/spring.factories 2、application.addBootstrapRegistryInitializer() |
ApplicationContextInitializer | 特定阶段:ioc容器初始化 | 1、META-INF/spring.factories 2、application.addInitializers() |
ApplicationListener | 全阶段 | 1、META-INF/spring.factories 2、SpringApplication.addListeners(…) 3、@Bean 或 @EventListener |
SpringApplicationRunListener | 全阶段 | META-INF/spring.factories |
ApplicationRunner | 特定阶段:感知应用就绪 | @Bean |
CommandLineRunner | 特定阶段:感知应用就绪 | @Bean |
(2)使用例(以SpringApplicationRunListener为例)
- 创建自己的监听器,继承SpringApplicationRunListener接口
java">@Slf4j
public class Listener implements SpringApplicationRunListener {@Overridepublic void starting(ConfigurableBootstrapContext bootstrapContext) {log.info("Listener starting:启动监听器");}@Overridepublic void ready(ConfigurableApplicationContext context, Duration timeTaken) {log.info("Listener ready:应用准备就绪");}@Overridepublic void failed(ConfigurableApplicationContext context, Throwable exception) {log.info("Listener failed:应用启动失败");}
}
- 在resources目录中创建spring.factories,在其中绑定自己的监听器和对应规定的监听器(=后面的是自己的)
org.springframework.boot.SpringApplicationRunListener=org.springframework.boot.SpringApplicationRunListener
(3)最佳实践
-
应用启动后做事:ApplicationRunner、CommandLineRunner
-
事件驱动开发:ApplicationListener
4.2 生命周期事件【了解】
和别的生命周期不同,SpringBoot的生命周期在经过某些环节时会自发地向外发出可以被感知的事件,我们可以利用这感知事件的能力自动触发一些代码
事件分类:
系统九大事件 |
---|
ApplicationStartingEvent |
ApplicationEnvironmentPreparedEvent |
ApplicationContextInitializedEvent |
ApplicationPreparedEvent |
ApplicationStartedEvent |
AvailabilityChangeEvent |
ApplicationReadyEvent |
AvailabilityChangeEvent |
ApplicationFailedEvent |
对应的事件流程:
事件和监听器对比:
4.3 事件驱动 开发【了解】
SpringBoot的事件发送和感知不仅是用于Spring的生命周期,还能通过在项目中手动发出自定义的事件,然后利用自动感知来自动触发一些功能(比如用户登录自动签到,就可以在登录的Service中发出登录事件,签到功能被自动触发);
这就是事件驱动开发模式(自定义事件以驱动程序开发),在功能自动化的同时能对代码解耦,是一种较高效的模式
(1)流程:
-
定义事件:
-
任意事件:任意类可以作为事件类,建议命名 xxxEvent,
- 其中设置好需要属性,然后使用构造器接收事件发来的参数
-
系统事件:继承 ApplicationEvent
-
-
事件发布:
-
组件实现 ApplicationEventPublisherAware
-
自动注入 ApplicationEventPublisher publisher
- 发送:publisher.publishEvent(【造好的事件类实例】)
-
-
事件监听:
- 组件类中,给方法标注@EventListener(【要监听的事件】.class)
▽ 回忆·Spring自动异步处理
- 在方法上标注@Async 异步注解
- 在运行主类上标注@EnableAsync 开启基于@Async的异步处理
▽ 扩展·分布式事件(消息)模式
前面这种基础的事件处理模式虽然方便,但是无法分布式项目,因为分布式项目的模块是分开多个服务器的,服务器无法直接感知。为此,分布式事件(消息)模式就是把事件发送到中间消息服务器,每个服务器都会访问该服务器,从而跨服务器感知消息
4.4 自动配置原理
(1)原理流程图
(2)实践·自定义starter
我们导入MVC、JDBC等等时本质上是导入了一种依赖场景,同理,我们可以自定义一个starter项目作为其他项目依赖的功能模块。
为什么和自动配置有关?:
- starter的业务功能我们已经大致知道如何实现,但是如果只是像上述的依赖一样直接导入,就会发现我们无法使用starter的功能。因为starter中的实现功能的种种组件没有进入IOC容器,为此我们要就这些功能统统配置到外部项目
处理方式:
-
**第一层抽取:**使用@Inpot 导入配置类
- 使用@Configration编写一个配置类,在其中使用@Bean 等方式添加组件
- 在外部项目中使用@Inpot 导入配置类,从而间接完成配置
-
**第二层抽取:**使用@EnableXxx 开启starter
- 编写@EnableXxx 注解,仿照比如开启异步@EnableAsync 编写,并在该注解类上标识@Inpot 导入配置类
-
**第三层抽取:**只要导入依赖就能自动配置好
- 在starter的resources 目录下创建META-INF.spring 目录
- 在META-INF.spring 目录下创建org.springframework.boot.autoconfigure.imports 文件
- 在org.springframework.boot.autoconfigure.imports 文件下写好配置类的全类名写好
要回忆的话把SSM视频的最后一集多看几遍,干看笔记没用
4.5 总结
在SSM中,要掌握如下内容:
- SpringBoot 自动配置原理
- SpringMVC 的DispatcherServlet 流程
- Spring 的IOC容器(三级缓存机制)
- Spring 的事务原理(TransactionManager、TransactionInterceptor)