Skip to content

**# SpringBoot

简介

SpringBoot 是一个简化 Spring 应用程序开发的框架,它提供了一系列的 starter 模块,用于集成各种常见的 Spring 框架组件。

提倡约定大于配置,提供了默认配置、自动化配置和嵌入式服务器等功能,使得开发人员能快速构建、测试和部署 Spring 应用

SpringBoot 特点

简化配置:通过自动配置(EnableAutoConfiguration),根据项目的 classPathapplication 配置文件等自动为应用添加相应的依赖和配置。 内置服务器:内置 Tomcat、Jetty、Netty(仅限 WebFlux)等服务器,程序可以直接通过 java -jar 方式启动 生态强大:集成 Spring 全家桶,而且各主流组件也提供第三方的 stater 模块来支持 SpringBoot

可以通过切换依赖来切换嵌入式 Web 容器,比如移除 Tomcat 依赖,添加 Jetty 依赖

java
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
java
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
java
// 引入 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 可以将 Java 程序编译为可以直接执行的 .exe 程序时,我是很兴奋的,于是直接开始上手尝试:

  1. 下载 GraalVM 官方包:https://www.graalvm.org/downloads/ ,解压到本地,并配置环境变量,然后使用 java -versionnative-image 命令验证

GraalVM1.webp

  1. 安装 Visual Studio 组件 (Windows 系统下编译原生可执行文件需要)

GraalVM2.webp

  1. 创建 SpringBoot 项目,选择我们下载的 GraalVM JDK,并配置 native-image 支持插件

GraalVM 静态编译的致命痛点 -- 反射等动态特性

GraalVM 可以编译生成为原生可执行文件,解决了 Java 应该 JVM 冷启动问题,消耗资源更少、性能更快。

但是再厉害的静态编译优化也无法完全预测诸如反射等动态特性,SpingBoot 3.x 为此提供了大部分场景下的支持,但依然不能完全解决,部分场景需要开发者手动提供配置解决

启动流程

1. 启动 main() 方法

应用从 main() 方法启动,并通过 SpringApplication.run() 引导应用启动

启动1.webp

2. 创建 SpringApplication 对象

首先会调用 SpringApplication 的构造方法,创建 SpringApplication 对象,并进行一些初始化工作,比如:

  • 推断应用类型,到底是 Servlet(传统 Web 应用) 还是 Reactive(响应式应用) 或 None (非 Web 应用)
  • 利用 Spring Factories 机制从 META-INF/spring.factories 文件中加载 ApplicationContextInitializerApplicationListener

启动2.webp

3. 获取并启动监听器

加载并实例化 spring.factories 中定义的监听器(实现类主要是 EventPublishingRunListener),进行广播 ApplicationStartingEvent 事件。 通知所有感兴趣的 ApplicationListener 应用即将开始启动。

4. 准备环境

  • 创建运行时环境 Environment ,处理命令行参数和 application.properties/application.yml 配置文件加载环境变量
  • 广播 ApplicationEnvironmentPreparedEvent 事件,通知监听器环境已准备就绪

启动4.webp

5. 创建 ApplicationContext

创建 ApplicationContext

  • 注册包含主配置类(@SpringBootApplication)在内的所有配置类(@Configuration
  • 广播 ApplicationPreparedEvent 事件,这时 ApplicationContext 已经创建完成,但还未 refresh(刷新),可以对其进行最后的修改

6. 刷新 ApplicationContext

调用 AbstractApplicationContextrefresh() 方法,这是最复杂也是最关键的一步,是 Spring 容器初始化 Bean 的核心流程

  • 创建并初始化所有定义的单例 Bean
  • BeanPostProcessor 干预 Bean 的创建过程:依赖注入、创建 AOP 代理对象
  • 启动嵌入式 Web 服务器

启动6.webp

7. 执行 CommandLineRunnerApplicationRunner

  • 应用启动后,会先按 @order 顺序执行 ApplicationRunnerCommandLineRunner
  • 我们一般会在 ApplicationRunnerCommandLineRunner 中执行自定义的初始化逻辑,比如:缓存预热、加载初始化数据等

8. 应用启动完成,发布 ready 事件,正式开始处理业务请求或任务

启动7.webp

自动配置流程.webp

SpringBoot 如何实现缓存预热

  • 基于 SpringBoot 的启动流程,我们可以通过 ApplicationRunnerCommandLineRunner 实现缓存预热,比如:

SpringBoot 应用启动后将 Mysql 数据缓存入 Redis

java
@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 等条件注解来控制是否进行加载。

@SpringBootApplication.webp

自动配置顺序

SpringBoot 提供了 @AutoConfigurationBefore@AutoConfigurationAfter 注解来控制自动配置类的加载顺序

java
@Configuration
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
public class MyDataSourceAutoConfiguration {
    // 自定义的数据源配置
}

禁用特定的自动配置

如果 SpringBoot 的默认自动配置和我们的实际业务需求不符,可以禁用掉部分自动配置内容

  • application.properties 中添加 spring.autoconfigure.exclude 属性,指定要排除的配置类
properties
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
  • 也可以使用 @SpringBootApplication 注解的 exclude 属性
java
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class MyApplication {
   public static void main(String[] args) {
       SpringApplication.run(MyApplication.class, args);
   }
}

SpringBoot 事件机制

在之前的 SpringBoot 启动流程中就已经提到了大量的事件发布与监听。SpringBoot 的事件机制是基于观察者模式实现的,主要流程如下:

  1. 事件发布:事件通过 ApplicationEventPublisher 发布,发布者不需要关心事件的处理细节
  2. 事件监听ApplicationEventMulticaster 会把事件分发给所有的监听器,监听器 ApplicationListener 接收事件
  3. 异步处理:如果监听器使用了 @Async ,事件会被异步处理,不影响主线程的执行

SpringBoot 有许多内置事件,比如:

  • ApplicationStartingEvent:应用启动开始
  • ApplicationEnvironmentPreparedEvent:环境准备完成(解析了 application.yml 、命令行参数等)
  • ApplicationContextInitializedEvent:应用上下文已创建完毕,但还未加载 Bean 定义
  • ApplicationPreparedEvent:应用上下文已加载 Bean 定义,但 Bean 尚未实例化refresh
  • ApplicationStartedEvent:容器已刷新完成,且所有 ApplicationRunnerCommandLineRunner 执行完毕
  • ApplicationReadyEvent(最重要)应用已完全就绪,都可以开始接收服务请求,此时健康检查 /actuator/health 状态为 UP
  • ApplicationFailedEvent:启动过程中发生异常导致失败时

自定义监听器,监听内置事件:

监听内置事件.webp

也可以监听具体指定事件,如指定 ApplicationEnvironmentPreparedEvent 环境准备就绪事件:

java
@Component
public class DbConfigListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        // 此时环境已经准备就绪,可以获取环境变量了
    }
}

自定义事件

我们也可以自定义事件,利用事件驱动机制实现自定义需求,比如当用户登录成功后,自动签到加积分

  1. 创建登录成功事件
java
public class LoginSuccessEvent extends ApplicationEvent {
    public LoginSuccessEvent(UserEntity source) {
        super(source);
    }
}
  1. 创建事件推送器
java
@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;
    }
}
  1. 监听事件,事件触发时进行加积分
java
@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());
    }
}
  1. 登录成功时,进行事件推送
java
@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 表示健康, HTTP 状态码为 200; DOWN 标识为不健康, HTTP 状态码为 503(K8s 据此重启容器)
  • /actuator/info:展示构建信息、Git 提交等静态信息
  • /actuator/metrics:暴露 JVM、系统指标 (可用于对接 Prometheus + Grafana

/actuator/health 端点有两个关键探针:

  • 存活探针(liveness:检查容器是否应该重启,比如死锁、关键资源耗尽;若失败,K8s 会重启该 Pod
  • 就绪探针(readiness:检查容器是否准备好接收流量(如缓存是否预热完、数据库连接已建立);若失败,K8s 不暂时不会把流量路由到该 Pod

引入探针模块

  1. 导入依赖
xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
  1. 暴露端点,可在application.yml配置
yaml
# 暴露所有监控端点
management:
  endpoints:
    web:
      exposure:
        include: '*'
  1. 访问 http://localhost:8080/actuator 可以看到所有暴露的端点

暴露端点.webp

定制 Health 端点

  1. 实现 HealthIndicator 接口或继承 AbstractHealthIndicator
java
@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;
    }
}
java
@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();
        }
    }
}
  1. 修改 application 配置文件展示详细信息
yaml
management:
  endpoint:
    health:
      enabled: true
      show-details: always
  1. 测试结果

自定义health端点.webp

定制 Metrics 端点

我们也可以定制 Metrics 端点来自定义监控指标,比如记录某个组件的调用次数

  1. 对要创建的组件注入 MeterRegistry
java
@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;
    }
}
  1. 编写 Controller 层接口进行测试
java
@RequestMapping("/my")
@RestController
public class MyController {

    @Autowired
    MyComponent myComponent;

    @GetMapping("/hello")
    public String hello() {
        myComponent.check();
        return "哈哈哈";
    }
}
  1. 访问若干次后(对用户无感),查看 actuator/metrics/myComponent.check

自定义metrics.webp

整合 Prometheus + Grafana

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

Json格式指标数据.webp

这种数据前端展示不够友好,因此我们可以使用 Prometheus + Grafana 来进行指标数据的可视化展示。

Promethus 是一个时序数据库,因此需要单独引入相关依赖,将监控指标数据转换为 Prometheus 支持的格式。

  1. 创建项目

引入micrometer-registry-prometheus依赖

xml
<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>

时序数据.webp

  1. 将项目打包并且部署到服务器上

我使用的是我自己的云服务器来进行部署测试,包括 PrometheusGrafana 都是部署在我的云服务器上。如果没有 JDK 环境需要先安装 JDK 环境。

shell
# 下载 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 &
  1. 安装 Prometheus + Grafana

我使用 docker 的方式进行安装,Grafana 安装十分简单,默认账号密码为 admin:admin

shell
# 安装 grafana , 默认账号密码 admin:admin
docker run -d --name=grafana -p 3000:3000 grafana/grafana

grafana安装.webp

Prometheus 需要创建配置文件(/opt/promethues/conf/prometheus.yml),指定要拉取的数据源

yaml
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

shell
# 安装 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添加 prometheus 数据源.webp

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

grafana dashboard.webp

配置相关

配置文件的优先级

对常用配置优先级,从高到低排序:

  1. 命令行参数
  2. Jar 包外的 application.properties 文件
  3. Jar 包内的 application.properties 文件

补充

  • application-{profile}.properties 优先级高于 application.properties
  • application.properties 优先级高于 application.yml

官方文档完整配置优先级 完整配置优先级.webp

定义和读取自定义配置

SpringBoot 定义和读取自定义配置主要有三种方式:

java
@Value("${my.custom.property}")
private String myProperty;
java
@Component
@ConfigurationProperties(prefix = "my.custom")
public class MyCustomProperties {
    private String property;
}
java
@Autowired
private Environment env;

public void someMethod() {
    String value = env.getProperty("my.custom.property");
}

多数据源配置

我们的应用中往往需要使用不同的数据源,那么如何实现多数据源配置呢?

  1. application.yml 配置文件中为不同的数据源定义连接信息
yaml
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
  1. 为每个数据源分布配置 DataSourceSqlSessionFactoryTransactionManager,并为主数据源标记 @Primary
java
@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);
    }
}
java
@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);
    }
}
  1. 使用 @MapperScan 为每个数据源独立指定 Mapper 扫描路径,并绑定相应的 SqlSessionFactory
java
@Configuration
@MapperScan(basePackages = "com.example.primary.mapper", sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDataSourceConfig {
    // 主数据源配置
}

@Configuration
@MapperScan(basePackages = "com.example.secondary.mapper", sqlSessionFactoryRef = "secondarySqlSessionFactory")
public class SecondaryDataSourceConfig {
    // 次数据源配置
}
  1. 可以通过 @Transactional 注解指定相应的事务管理器
java
@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 作为日志框架,不过也可以切换为其他日志框架

切换日志框架

xml
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
 </dependency>
        
<!--   导入 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 如何实现默认日志配置?

1、每个 starter 场景,都会引入一个核心场景 spring-boot-starter 2、核心场景引入了日志配置 spring-boot-starter-logging 3、默认使用了 logback + slf4j 组合作为默认底层日志 4、日志是系统一启动就要用, xxxAutoConfiguration 是系统启动好了之后放进去的组件,因此日志是用监听器机制配置的 ApplicationListener 5、日志的所有功能都可以通过修改配置文件来实现

  • 如何设置日志级别、分组、文件输出等

application.properties 中设置日志级别、分组和文件输出

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.log
  • logback 通过application.yml设置日志文件归档
yaml
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

自定义 starter