**# SpringBoot
简介
SpringBoot 是一个简化 Spring 应用程序开发的框架,它提供了一系列的 starter 模块,用于集成各种常见的 Spring 框架组件。提倡约定大于配置,提供了默认配置、自动化配置和嵌入式服务器等功能,使得开发人员能快速构建、测试和部署 Spring 应用。
SpringBoot 特点
- 简化配置:通过自动配置(
EnableAutoConfiguration),根据项目的classPath、application配置文件等自动为应用添加相应的依赖和配置。 - 生态强大:集成 Spring 全家桶,而且各主流组件也提供第三方的
stater模块来支持 SpringBoot - 内置服务器:内置 Tomcat、Jetty、Netty(仅限 WebFlux)等服务器,程序可以直接通过
java -jar方式启动。可以通过切换依赖来切换嵌入式 Web 容器,比如移除 Tomcat 依赖,添加 Jetty 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>// 引入 webflux 依赖就自动选择 Netty 作为 Web 容器了
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>版本演进
SpringBoot 2.x 改进
- 基于 Spring 5.x ,引入了响应式编程(WebFlux)
- 升级了嵌入式 Web 容器的默认版本
- 基于 JDK8 + ,底层组件和框架本身做了大量性能优化,更适合云原生应用和大规模微服务架构
- 自动配置机制增强,使应用自动配置更加智能
SpringBoot 3.x 改进
- 从 Java EE 迁移到 Jakarta EE,一些核心包名从
javax.*变更为jakarta.* - 基于 JDK17+ ,充分利用 JDK17 的新特性进行了性能优化
- 提供了对
GraalVM的开箱即用支持 - 安全性增强(
Spring Security 5升级为Spring Security 6) - 对内部依赖进行模块化调整,优化依赖管理,删除了一些不再使用或过时的库,更轻量化
GraalVM
SpringBoot 3.X 很重要的一项新特性就是原生支持GraalVM,它是什么、和传统 JVM 有什么区别呢?
我们先来回顾传统 JVM 的运行机制:.java 源文件先被编译为与平台无关的 .class 字节码,然后由 JVM 逐条解释字节码为机器码进行执行。 这个解释执行的过程非常灵活,支持诸如反射这类动态特性,但是没有经过编译优化性能较差。
为了解决这个运行时性能差的问题,JVM 引入了JIT(Just In Time)实时编译技术,将热点函数编译为汇编代码(经过汇编器即可转换为操作系统直接执行的机器码)。 OpenJDK 使用的 JIT 编译器分为 C1 和 C2,前者编译优化较少,后者编译优化较多,但是性能相对更好但耗费系统资源也更多。
GraalVM 是指以 JVM 为基础,以 Graal 即时编译器为核心,以能运行多种语言为目标,包含一系列框架和技术的大杂烩,想成为一统天下的最终虚拟机。
- 可以作为传统 JVM 场景使用,其实就是原来 C2 编译器做的事情交给了 Graal 编译器来做,性能比 C2 更好
- 支持 Java 的 AOT(Ahead-of-Time),编译产物不再是 Java 字节码,而是可在目标系统上直接执行的机器码(不依赖运行时环境的可执行文件,如
.exe)
GraalVM 静态编译的致命痛点 -- 反射等动态特性
GraalVM 可以编译生成为原生可执行文件,解决了 Java 应该 JVM 冷启动问题,消耗资源更少、性能更快。
但是再厉害的静态编译优化也无法完全预测诸如反射等动态特性,SpingBoot 3.x 为此提供了大部分场景下的支持,但依然不能完全解决,部分场景需要开发者手动提供配置解决
启动流程
1. 启动 main() 方法
应用从 main() 方法启动,并通过 SpringApplication.run() 引导应用启动

2. 创建 SpringApplication 对象
首先会调用 SpringApplication 的构造方法,创建 SpringApplication 对象,并进行一些初始化工作:
- 推断应用类型,到底是
Servlet(传统 Web 应用) 还是Reactive(响应式应用) 或None(非 Web 应用) - 利用 Spring Factories 机制从
META-INF/spring.factories文件中加载ApplicationContextInitializer和ApplicationListener

3. 获取并启动监听器
加载并实例化 spring.factories 中定义的监听器(实现类主要是 EventPublishingRunListener),进行广播 ApplicationStartingEvent 事件。 通知所有感兴趣的 ApplicationListener 应用即将开始启动。
4. 准备环境
创建运行时环境 Environment ,处理命令行参数和 application.properties/application.yml 配置文件加载环境变量,然后广播 ApplicationEnvironmentPreparedEvent 事件,通知监听器环境已准备就绪

5. 创建 ApplicationContext
注册包含主配置类(@SpringBootApplication)在内的所有配置类(@Configuration),然后广播ApplicationPreparedEvent 事件,这时 ApplicationContext 已经创建完成,但还未 refresh(刷新),可以对其进行最后的修改
6. 刷新 ApplicationContext
调用 AbstractApplicationContext 的 refresh() 方法,这是最复杂也是最关键的一步,是 Spring 容器初始化 Bean 的核心流程:
- 创建并初始化所有定义的单例 Bean
- BeanPostProcessor 干预 Bean 的创建过程:依赖注入、创建 AOP 代理对象
- 启动嵌入式 Web 服务器

7. 执行 CommandLineRunner 和 ApplicationRunner
- 应用启动后,会先按
@order顺序执行ApplicationRunner和CommandLineRunner - 我们一般会在
ApplicationRunner或CommandLineRunner中执行自定义的初始化逻辑,比如:缓存预热、加载初始化数据等
8. 应用启动完成,发布 ready 事件,正式开始处理业务请求或任务

SpringBoot 如何实现缓存预热
- 基于 SpringBoot 的启动流程,我们可以通过
ApplicationRunner或CommandLineRunner实现缓存预热,比如:
SpringBoot 应用启动后将 Mysql 数据缓存入 Redis
@Component
public class CacheLoader implements CommandLineRunner {
@Autowired
private MySqlRepository repository;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public void run(String... args) {
List<MsgDO> data = repository.list();
redisTemplate.opsForValue().set("key", data);
}
}- 也可以通过
@PostConstruct注解实现
自动配置原理
SpringBoot 的自动配置是通过 @EnableAutoConfiguration 实现的,这个注解包含 @Import(AutoConfigurationImportSelector.class) 注解, 导入的这个类会去扫描 classpath 下所有的 META-INF/spring.factories 文件,根据文件中的内容加载相应的 Bean 。
这些 Bean 通常会使用 @ConditionalOnClass 、@ConditionalOnMissingBean、@ConditionalOnProperty 等条件注解来控制是否进行加载。

自动配置顺序
SpringBoot 提供了 @AutoConfigurationBefore 和 @AutoConfigurationAfter 注解来控制自动配置类的加载顺序
@Configuration
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
public class MyDataSourceAutoConfiguration {
// 自定义的数据源配置
}禁用特定的自动配置
如果 SpringBoot 的默认自动配置和我们的实际业务需求不符,可以禁用掉部分自动配置内容
- 在
application.properties中添加spring.autoconfigure.exclude属性,指定要排除的配置类
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration- 也可以使用
@SpringBootApplication注解的exclude属性
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}SpringBoot 事件机制
SpringBoot 的事件机制是基于观察者模式实现的,主要流程如下:
- 事件发布:事件通过
ApplicationEventPublisher发布,发布者不需要关心事件的处理细节 - 事件监听:
ApplicationEventMulticaster会把事件分发给所有的监听器,监听器ApplicationListener接收事件 - 异步处理:如果监听器使用了
@Async,事件会被异步处理,不影响主线程的执行
SpringBoot 有许多内置事件,比如:
ApplicationStartingEvent:应用启动开始ApplicationEnvironmentPreparedEvent:环境准备完成(解析了application.yml、命令行参数等)ApplicationContextInitializedEvent:应用上下文已创建完毕,但还未加载 Bean 定义ApplicationPreparedEvent:应用上下文已加载 Bean 定义,但 Bean 尚未实例化 (refresh)ApplicationStartedEvent:容器已刷新完成,且所有ApplicationRunner和CommandLineRunner执行完毕ApplicationReadyEvent:应用已完全就绪,都可以开始接收服务请求,此时健康检查/actuator/health状态为UPApplicationFailedEvent:启动过程中发生异常导致失败时
自定义监听器,监听内置事件:

也可以监听具体指定事件,如指定 ApplicationEnvironmentPreparedEvent 环境准备就绪事件:
@Component
public class DbConfigListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
@Override
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
// 此时环境已经准备就绪,可以获取环境变量了
}
}自定义事件
我们也可以自定义事件,利用事件驱动机制实现自定义需求,比如当用户登录成功后,自动签到加积分:
- 创建登录成功事件
public class LoginSuccessEvent extends ApplicationEvent {
public LoginSuccessEvent(UserEntity source) {
super(source);
}
}- 创建事件推送器
@Service
public class EventPublisher implements ApplicationEventPublisherAware {
/**
* 底层发送事件用的组件,事件是广播出去的,所有监听这个事件的监听器都可以收到
*/
ApplicationEventPublisher applicationEventPublisher;
public void sendEvent(ApplicationEvent event) {
System.out.println("发布事件");
applicationEventPublisher.publishEvent(event);
}
/**
* 会被自动调用,把真正发事件的底层组件给我们注入进来
* @param applicationEventPublisher event publisher to be used by this object
*/
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}- 监听事件,事件触发时进行加积分
@Service
public class AccountService {
public void addAccountScore(String username) {
System.out.println(username + " 加了1分");
}
// 监听 LoginSuccessEvent 事件
@EventListener
public void onEvent(LoginSuccessEvent event) {
UserEntity user = (UserEntity) event.getSource();
addAccountScore(user.getUsername());
}
}- 登录成功时,进行事件推送
@Tag(name = "用户管理",description = "用户管理")
@RequestMapping("/user")
@RestController
public class UserRestController {
@Resource
EventPublisher eventPublisher;
@Operation(summary = "用户登录")
@GetMapping("/login")
public SaResult login(@RequestParam(value = "username")String username,@RequestParam("password") String password) {
String sha256 = SaSecureUtil.sha256(password);
//模拟直接登录成功 (用的 Sa-Token)
StpUtil.login(1111);
//1、创建登录成功事件
LoginSuccessEvent loginSuccessEvent = new LoginSuccessEvent(new UserEntity(username, sha256));
//2、发送事件
eventPublisher.sendEvent(loginSuccessEvent);
return SaResult.ok();
}
}探针机制
事件机制是 SpringBoot 系统内部主动推送状态变更,探针(actuator)机制则是让外部(K8S、云服务)感知应用内部的健康状态。
spring-boot-starter-actuator 关键端点(Endpoints)主要有:
/actuator/health:健康检查,UP表示健康(200);DOWN表示不健康(503)K8S 据此重启容器/actuator/info:展示构建信息、Git 提交等静态信息/actuator/metrics:暴露 JVM、系统指标 (可用于对接Prometheus+Grafana)
/actuator/health 端点有两个关键探针:
- 存活探针(
liveness):检查容器是否应该重启,比如死锁、关键资源耗尽;K8S 会重启该Pod - 就绪探针(
readiness):检查容器是否准备好接收流量(如缓存是否预热完、数据库连接已建立);若失败,K8S 暂时不会把流量路由到该Pod
引入探针模块
- 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>- 暴露端点,可在
application.yml配置
# 暴露所有监控端点
management:
endpoints:
web:
exposure:
include: '*'- 访问
http://localhost:8080/actuator可以看到所有暴露的端点

定制 Health 端点
- 实现
HealthIndicator接口或继承AbstractHealthIndicator
@Component
public class MyHealthIndicator extends AbstractHealthIndicator {
@Autowired
MyComponent myComponent;
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
//自定义检查方法
int check = myComponent.check();
if (check == 1) {
builder.up()
.withDetail("code",1000)
.withDetail("msg","活得很健康")
.withDetail("data","我是组件哈哈哈哈")
.build();
}else {
builder.down().build();
}
}
}
@Component
public class MyComponent {
public int check() {
//业务代码判断这个组件是否该是存活状态
return 1;
}
}@Component
public class MyHealthIndicator implements HealthIndicator {
@Override
public Health health() {
// 模拟某个组件健康检查方法,需要自定义
boolean check = MyComponent.check();
// 根据健康检查结果自定义响应
if (check) {
return Health.up()
.withDetail("code",200)
.build();
}else {
return Health.down()
.withDetail("code",500)
.build();
}
}
}- 修改
application配置文件展示详细信息
management:
endpoint:
health:
enabled: true
show-details: always- 测试结果

定制 Metrics 端点
我们也可以定制 Metrics 端点来自定义监控指标,比如记录某个组件的调用次数
- 对要创建的组件注入
MeterRegistry
@Component
public class MyComponent {
Counter counter = null;
public MyComponent(MeterRegistry meterRegistry) {
//得到一个名为 myComponent.check 的计数器
counter = meterRegistry.counter("myComponent.check");
}
public int check() {
//每被调用一次,计数器+1
counter.increment();
//业务代码判断这个组件是否该是存活状态
return 1;
}
}- 编写
Controller层接口进行测试
@RequestMapping("/my")
@RestController
public class MyController {
@Autowired
MyComponent myComponent;
@GetMapping("/hello")
public String hello() {
myComponent.check();
return "哈哈哈";
}
}- 访问若干次后(对用户无感),查看
actuator/metrics/myComponent.check

整合 Prometheus + Grafana
前面说了引入 spring-boot-starter-actuator 模块,即可对外暴露大量的系统内部监控指标数据,但这些数据是 json 格式的

这种数据前端展示不够友好,因此我们可以使用 Prometheus + Grafana 来进行指标数据的可视化展示。
Promethus 是一个时序数据库,需要单独引入相关依赖,将监控指标数据转换为Prometheus支持的格式。
- 创建项目
引入micrometer-registry-prometheus依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
- 将项目打包并且部署到服务器上
我使用的是我自己的云服务器来进行部署测试,包括 Prometheus 和 Grafana 都是部署在我的云服务器上。如果没有 JDK 环境需要先安装 JDK 环境。
# 下载 jdk 21
wget https://download.oracle.com/java/21/latest/jdk-21_linux-x64_bin.tar.gz
mkdir -p /opt/java
# 解压
tar -xzf jdk-21_linux-x64_bin.tar.gz -C /opt/java/
#配置环境变量
vi /etc/profile
#添加
export JAVA_HOME=/opt/java/jdk-21.0.5
export PATH=$PATH:$JAVA_HOME/bin
# 让环境变量更改生效
source /etc/profile
# 后台启动 java 应用
nohup java -jar app.jar > output.log 2>&1 &- 安装
Prometheus + Grafana
我使用 docker 的方式进行安装,Grafana 安装十分简单,默认账号密码为 admin:admin
# 安装 grafana , 默认账号密码 admin:admin
docker run -d --name=grafana -p 3000:3000 grafana/grafana
Prometheus 需要创建配置文件(/opt/promethues/conf/prometheus.yml),指定要拉取的数据源
global:
# 设置默认的数据抓取间隔为 15 s
scrape_interval: 15s
# 设置默认的告警规则评估间隔为 15 s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'spring-boot-demo'
# spring-boot 的 Prometheus 指标路径
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['8.140.207.120:8080']
labels:
nodename: 'app-demo'然后使用 docker 启动 Prometheus
# 安装 prometheus 时序数据库
docker run \
--name=prometheus \
-d \
-p 9090:9090 \
-v /opt/prometheus/conf:/etc/prometheus \
prom/prometheus \
--config.file=/etc/prometheus/prometheus.yml \
--storage.tsdb.path=/prometheus \
--web.console.libraries=/usr/share/prometheus/console_libraries \
--web.console.templates=/usr/share/prometheus/consoles \
--web.enable-lifecycle然后在 Grafana 面板中添加 Prometheus 数据源

最后为 Grafana 添加一个前端展示模板 dashboard ,就可以展示从 Prometheus 采集到的数据了

配置相关
配置文件的优先级
对常用配置优先级,从高到低排序:
- 命令行参数
- Jar 包外的
application.properties文件 - Jar 包内的
application.properties文件
补充
application-{profile}.properties优先级高于application.propertiesapplication.properties优先级高于application.yml
官方文档完整配置优先级
定义和读取自定义配置
SpringBoot 定义和读取自定义配置主要有三种方式:
@Value("${my.custom.property}")
private String myProperty;@Component
@ConfigurationProperties(prefix = "my.custom")
public class MyCustomProperties {
private String property;
}@Autowired
private Environment env;
public void someMethod() {
String value = env.getProperty("my.custom.property");
}多数据源配置
我们的应用中往往需要使用不同的数据源,那么如何实现多数据源配置呢?
- 在
application.yml配置文件中为不同的数据源定义连接信息
spring:
datasource:
primary:
url: jdbc:mysql://localhost:3306/primary_db
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
secondary:
url: jdbc:mysql://localhost:3306/secondary_db
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver- 为每个数据源分布配置
DataSource、SqlSessionFactory和TransactionManager,并为主数据源标记@Primary
@Configuration
@MapperScan(basePackages = "com.example.primary.mapper", sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDataSourceConfig {
@Primary
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "primarySqlSessionFactory")
public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
return sessionFactoryBean.getObject();
}
@Primary
@Bean(name = "primaryTransactionManager")
public DataSourceTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}@Configuration
@MapperScan(basePackages = "com.example.secondary.mapper", sqlSessionFactoryRef = "secondarySqlSessionFactory")
public class SecondaryDataSourceConfig {
@Bean(name = "secondaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "secondarySqlSessionFactory")
public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
return sessionFactoryBean.getObject();
}
@Bean(name = "secondaryTransactionManager")
public DataSourceTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}- 使用
@MapperScan为每个数据源独立指定Mapper扫描路径,并绑定相应的SqlSessionFactory
@Configuration
@MapperScan(basePackages = "com.example.primary.mapper", sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDataSourceConfig {
// 主数据源配置
}
@Configuration
@MapperScan(basePackages = "com.example.secondary.mapper", sqlSessionFactoryRef = "secondarySqlSessionFactory")
public class SecondaryDataSourceConfig {
// 次数据源配置
}- 可以通过
@Transactional注解指定相应的事务管理器
@Service
public class MyService {
@Autowired
private PrimaryMapper primaryMapper;
@Autowired
private SecondaryMapper secondaryMapper;
@Transactional(transactionManager = "primaryTransactionManager")
public void usePrimaryDataSource() {
primaryMapper.insertData(...);
}
@Transactional(transactionManager = "secondaryTransactionManager")
public void useSecondaryDataSource() {
secondaryMapper.insertData(...);
}
}日志配置
SpringBoot 底层默认使用 logback 作为日志框架,不过也可以切换为其他日志框架
<!-- 导入 spring-boot-starter 就近原则,优先级高于
spring-boot-starter-web 依赖的 spring-boot-starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<!-- 排除掉默认的日志依赖 -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 使用性能更好的 log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>SpringBoot 如何实现默认日志配置?
- 每个
starter场景,都会引入一个核心场景spring-boot-starter - 核心场景引入了日志配置
spring-boot-starter-logging - 默认使用了
logback+slf4j组合作为默认底层日志 - 日志是系统一启动就要用,
xxxAutoConfiguration是系统启动好了之后放进去的组件,因此日志是用监听器机制配置的ApplicationListener - 日志的所有功能都可以通过修改配置文件来实现
如何设置日志级别、分组、文件输出等?
在 application.properties 中设置日志级别、分组和文件输出
#默认所有日志没有精确指定级别就使用 root 的默认级别
logging.level.root=debug
#也可以精确指定某个包或者类的日志级别
logging.level.com.xxx.xxx=debug
# 将多个包分为同一个组
logging.group.xxx=com.xxx.xxx,com.xxx.xxx
# 对一个组设置日志级别
logging.level.xxx=debug
# springboot 默认为我们提供了 web 和 sql 组
logging.level.web=debug
logging.level.sql=debug
# 只写名字,默认生成在当前项目同位置的 demo.log 中
logging.file.name=demo.log
# 写名字 + 路径
logging.file.name=/opt/applog/demo.loglogback 通过application.yml设置日志文件归档
logging:
logback:
rollingpolicy:
# 滚动策略配置,默认每天一个,超过单个文件最大 size 进行切割
file-name-pattern: ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz
# 单个日志文件的最大大小,默认为 10M
max-file-size: 10MB
# 应用启动时是否清楚以前文档,默认值 false
clean-history-on-start: false
# 日志文件被删除前,可以容纳的最大大小,默认为 0B,如果设置 1G,则磁盘存储超过1G后会删除旧日志文件
total-size-cap: 0B
# 保存的最大历史文件天数,默认为 7天
max-history: 7
file:
name: /opt/applog/my.log日志最佳实践
- 导入第三方框架,可以先排除它的日志包,因为 Boot 底层统一控制了日志管理
- 修改
application.yml配置,就可以调整日志的所有行为,也可以编写日志框架本身的配置文件,如logback-spring.xml

