萧瑟余晖头像

Spring之SpringBoot

Spring Boot 详解

一、Spring Boot 概述与设计理念

1.1 什么是 Spring Boot

Spring Boot 是基于 Spring 框架的快速开发脚手架,旨在简化 Spring 应用的初始搭建和开发过程。它通过"约定优于配置"的理念,让开发者以最少的配置快速创建生产级别的应用。

1.2 核心设计理念

理念说明
约定优于配置提供默认配置,减少显式配置量
自动配置根据类路径依赖自动装配 Bean
起步依赖通过 Starter 聚合相关依赖
生产就绪内嵌 Actuator 提供监控、健康检查
无代码生成不依赖 XML,纯 Java 配置

1.3 Spring Boot 版本演进

Spring Boot 1.x → Spring Framework 4.x,Java 6+
Spring Boot 2.x → Spring Framework 5.x,Java 8+,引入 WebFlux
Spring Boot 3.x → Spring Framework 6.x,Java 17+,Jakarta EE,GraalVM Native

1.4 快速开始

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @GetMapping("/hello")
    public String hello() {
        return "Hello, Spring Boot!";
    }
}
<!-- pom.xml 核心依赖 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.0</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

二、启动流程源码分析(SpringApplication.run)

2.1 启动入口

public static void main(String[] args) {
    // 两步走:创建实例 + 执行run
    SpringApplication.run(Application.class, args);
}

// 等价于:
public static void main(String[] args) {
    SpringApplication app = new SpringApplication(Application.class, args);
    ConfigurableApplicationContext context = app.run(args);
}

2.2 SpringApplication 构造阶段

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    // 1. 保存主配置类
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

    // 2. 推断应用类型:SERVLET / REACTIVE / NONE
    this.webApplicationType = WebApplicationType.deduceFromClasspath();

    // 3. 从 spring.factories 加载 ApplicationContextInitializer
    this.initializers = getSpringFactoriesInstances(
        ApplicationContextInitializer.class);

    // 4. 从 spring.factories 加载 ApplicationListener
    this.listeners = getSpringFactoriesInstances(ApplicationListener.class);

    // 5. 推断主启动类(通过堆栈找到 main 方法所在类)
    this.mainApplicationClass = deduceMainApplicationClass();
}

WebApplicationType 推断逻辑:

static WebApplicationType deduceFromClasspath() {
    // 存在 org.springframework.web.reactive.DispatcherHandler
    // 且不存在 org.springframework.web.servlet.DispatcherServlet
    if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS) 
        && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS)) {
        return WebApplicationType.REACTIVE;
    }
    // 存在 javax.servlet.Servlet 或 DispatcherServlet
    for (String className : SERVLET_INDICATOR_CLASSES) {
        if (!ClassUtils.isPresent(className)) {
            return WebApplicationType.NONE;
        }
    }
    return WebApplicationType.SERVLET;
}

2.3 run() 方法主流程

public ConfigurableApplicationContext run(String... args) {
    long startTime = System.nanoTime();

    // 1. 创建 BootstrapContext
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();

    // 2. 获取所有 SpringApplicationRunListener
    SpringApplicationRunListeners listeners = getRunListeners(args);

    // 3. 发布 ApplicationStartingEvent
    listeners.starting(bootstrapContext, this.mainApplicationClass);

    try {
        // 4. 准备 Environment
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(
            listeners, bootstrapContext, applicationArguments);

        // 5. 配置忽略的 Bean 信息
        configureIgnoreBeanInfo(environment);

        // 6. 打印 Banner
        Banner printedBanner = printBanner(environment);

        // 7. 创建 ApplicationContext
        context = createApplicationContext();

        // 8. 准备上下文
        prepareContext(bootstrapContext, context, environment,
            listeners, applicationArguments, printedBanner);

        // 9. 刷新上下文(核心!触发自动配置和Bean创建)
        refreshContext(context);

        // 10. 刷新后处理(模板方法,默认空实现)
        afterRefresh(context, applicationArguments);

        // 11. 发布 ApplicationStartedEvent
        listeners.started(context, timeTakenToStartup);

        // 12. 执行 Runner
        callRunners(context, applicationArguments);

    } catch (Throwable ex) {
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }

    // 13. 发布 ApplicationReadyEvent
    listeners.ready(context, timeTakenToReady);

    return context;
}

2.4 refreshContext 核心流程

// AbstractApplicationContext.refresh() 的12个步骤
public void refresh() {
    // 1. prepareRefresh() - 准备工作,设置启动时间、活跃标志
    // 2. obtainFreshBeanFactory() - 获取 BeanFactory
    // 3. prepareBeanFactory() - 配置 BeanFactory 标准特性
    // 4. postProcessBeanFactory() - 子类扩展点
    // 5. invokeBeanFactoryPostProcessors() - 执行 BeanFactoryPostProcessor
    //    ★ 这里触发 ConfigurationClassPostProcessor,解析自动配置
    // 6. registerBeanPostProcessors() - 注册 BeanPostProcessor
    // 7. initMessageSource() - 初始化消息源(国际化)
    // 8. initApplicationEventMulticaster() - 初始化事件广播器
    // 9. onRefresh() - 子类扩展(Spring Boot 在此创建内嵌服务器)
    // 10. registerListeners() - 注册监听器
    // 11. finishBeanFactoryInitialization() - 实例化所有非懒加载单例 Bean
    // 12. finishRefresh() - 完成刷新,发布 ContextRefreshedEvent
}

2.5 启动事件顺序

ApplicationStartingEvent          → 应用开始启动
ApplicationEnvironmentPreparedEvent → Environment 准备好
ApplicationContextInitializedEvent  → Context 创建并初始化
ApplicationPreparedEvent          → Context 加载完成但未刷新
ContextRefreshedEvent             → Context 刷新完成
ApplicationStartedEvent           → 应用已启动,Runner 未执行
AvailabilityChangeEvent(LIVENESS) → 存活状态变更
ApplicationReadyEvent             → 应用就绪,可接收请求
AvailabilityChangeEvent(READINESS)→ 就绪状态变更

三、自动配置原理

3.1 @SpringBootApplication 拆解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration   // 本质是 @Configuration
@EnableAutoConfiguration   // 核心:开启自动配置
@ComponentScan             // 扫描当前包及子包
public @interface SpringBootApplication {

    @AliasFor(annotation = EnableAutoConfiguration.class)
    Class<?>[] exclude() default {};

    @AliasFor(annotation = EnableAutoConfiguration.class)
    String[] excludeName() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    String[] scanBasePackages() default {};
}

3.2 @EnableAutoConfiguration 机制

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@AutoConfigurationPackage  // 注册主包路径
@Import(AutoConfigurationImportSelector.class)  // 导入自动配置选择器
public @interface EnableAutoConfiguration {
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}

3.3 AutoConfigurationImportSelector 核心逻辑

public class AutoConfigurationImportSelector
    implements DeferredImportSelector, BeanClassLoaderAware,
               ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

    @Override
    public AutoConfigurationEntry getAutoConfigurationEntry(
            AnnotationMetadata annotationMetadata) {

        // 1. 获取 @EnableAutoConfiguration 注解属性
        AnnotationAttributes attributes = getAttributes(annotationMetadata);

        // 2. 获取所有候选自动配置类
        List<String> configurations = getCandidateConfigurations(
            annotationMetadata, attributes);

        // 3. 去重
        configurations = removeDuplicates(configurations);

        // 4. 获取排除项(exclude)
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);

        // 5. 校验排除项合法性
        checkExcludedClasses(configurations, exclusions);

        // 6. 移除被排除的
        configurations.removeAll(exclusions);

        // 7. 过滤:通过 AutoConfigurationImportFilter(基于条件)
        configurations = getConfigurationClassFilter()
            .filter(configurations);

        // 8. 触发自动配置导入事件
        fireAutoConfigurationImportEvents(configurations, exclusions);

        return new AutoConfigurationEntry(configurations, exclusions);
    }

    protected List<String> getCandidateConfigurations(
            AnnotationMetadata metadata, AnnotationAttributes attributes) {
        // Spring Boot 2.x: 从 spring.factories 加载
        // Spring Boot 3.x: 从 AutoConfiguration.imports 加载
        List<String> configurations = ImportCandidates.load(
            AutoConfiguration.class, getBeanClassLoader())
            .getCandidates();
        Assert.notEmpty(configurations, "No auto configuration classes found");
        return configurations;
    }
}

3.4 自动配置类示例(DataSourceAutoConfiguration)

@AutoConfiguration(before = SqlInitializationAutoConfiguration.class)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import(DataSourcePoolMetadataProvidersConfiguration.class)
public class DataSourceAutoConfiguration {

    @Configuration(proxyBeanMethods = false)
    @Conditional(EmbeddedDatabaseCondition.class)
    @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    @Import(EmbeddedDataSourceConfiguration.class)
    protected static class EmbeddedDatabaseConfiguration {
    }

    @Configuration(proxyBeanMethods = false)
    @Conditional(PooledDataSourceCondition.class)
    @ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
    @Import({ Hikari.class, Tomcat.class, Dbcp2.class,
              OracleUcp.class, Simple.class })
    protected static class PooledDataSourceConfiguration {
    }
}

四、spring.factories vs AutoConfiguration.imports

4.1 Spring Boot 2.x:spring.factories

# META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.example.MyAutoConfiguration,\
  com.example.AnotherAutoConfiguration

org.springframework.context.ApplicationContextInitializer=\
  com.example.MyInitializer

org.springframework.context.ApplicationListener=\
  com.example.MyListener

问题:

  • 所有扩展点混在一个文件中,职责不清
  • 文件过大时加载性能差
  • 无法利用 IDE 的编译期检查

4.2 Spring Boot 3.x:AutoConfiguration.imports

# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.MyAutoConfiguration
com.example.AnotherAutoConfiguration

优势:

  • 每行一个类名,简洁清晰
  • 专用于自动配置,与其他扩展点分离
  • 支持 IDE 导航和重构
  • 可配合 @AutoConfiguration 注解使用

4.3 @AutoConfiguration 注解(Spring Boot 3)

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore
@AutoConfigureAfter
public @interface AutoConfiguration {

    @AliasFor(annotation = Configuration.class)
    String value() default "";

    Class<?>[] before() default {};
    String[] beforeName() default {};

    Class<?>[] after() default {};
    String[] afterName() default {};
}

4.4 兼容性说明

Spring Boot 2.7:同时支持两种方式(过渡期)
Spring Boot 3.0:移除 spring.factories 对自动配置的支持
                  其他扩展点(Initializer/Listener)仍用 spring.factories
                  或迁移到 META-INF/spring/ 下对应文件

五、条件注解详解

5.1 条件注解体系

// 所有条件注解的元注解
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass { ... }

5.2 常用条件注解

// 类路径条件
@ConditionalOnClass(name = "javax.sql.DataSource")      // 类存在
@ConditionalOnMissingClass("javax.sql.DataSource")      // 类不存在

// Bean 条件
@ConditionalOnBean(DataSource.class)                    // Bean 存在
@ConditionalOnMissingBean(DataSource.class)             // Bean 不存在
@ConditionalOnSingleCandidate(DataSource.class)         // 唯一候选

// 属性条件
@ConditionalOnProperty(
    prefix = "spring.cache",
    name = "type",
    havingValue = "redis",
    matchIfMissing = false  // 属性不存在时是否匹配
)

// 资源条件
@ConditionalOnResource(resources = "classpath:custom.properties")

// Web 应用条件
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnNotWebApplication

// 表达式条件
@ConditionalOnExpression("${server.enabled:true} && ${app.debug:false}")

// Java 版本条件(Spring Boot 3.1+)
@ConditionalOnJava(JavaVersion.SEVENTEEN)

// 云环境条件
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)

5.3 条件评估时机

ConfigurationPhase 枚举:
├── PARSE_CONFIGURATION  → 解析配置类时评估(@ConditionalOnClass)
└── REGISTER_BEAN        → 注册 Bean 时评估(@ConditionalOnBean)

注意:@ConditionalOnBean 依赖于 Bean 注册顺序,
     自动配置类默认在用户配置之后处理,因此可以检测到用户定义的 Bean。

5.4 自定义 Condition

public class OnLinuxCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context,
                          AnnotatedTypeMetadata metadata) {
        String os = context.getEnvironment().getProperty("os.name", "");
        return os.toLowerCase().contains("linux");
    }
}

// 使用
@Configuration
@Conditional(OnLinuxCondition.class)
public class LinuxSpecificConfiguration {
    // 仅在 Linux 环境生效
}

5.5 条件评估报告

# application.yml
debug: true  # 或 --debug 启动参数

启动时输出:

============================
CONDITIONS EVALUATION REPORT
============================

Positive matches:(匹配成功的自动配置)
-----------------
   DataSourceAutoConfiguration matched:
      - @ConditionalOnClass found required classes 'javax.sql.DataSource'
      - @ConditionalOnMissingBean (types: io.r2dbc.spi.ConnectionFactory)

Negative matches:(未匹配的自动配置)
-----------------
   MongoAutoConfiguration:
      - @ConditionalOnClass did not find required class 'com.mongodb.client.MongoClient'

六、Starter 机制与自定义 Starter

6.1 Starter 的本质

Starter 是一个依赖聚合 + 自动配置的打包方式:

spring-boot-starter-web
├── spring-boot-starter(核心)
├── spring-boot-starter-json(Jackson)
├── spring-boot-starter-tomcat(内嵌Tomcat)
├── spring-web
└── spring-webmvc

6.2 自定义 Starter 项目结构

my-spring-boot-starter/
├── my-spring-boot-autoconfigure/     # 自动配置模块
│   ├── src/main/java/
│   │   └── com/example/my/
│   │       ├── MyAutoConfiguration.java
│   │       ├── MyProperties.java
│   │       └── MyService.java
│   └── src/main/resources/
│       └── META-INF/spring/
│           └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
│
└── my-spring-boot-starter/           # Starter 模块(仅 pom)
    └── pom.xml(依赖 autoconfigure + 第三方库)

6.3 编写配置属性类

@ConfigurationProperties(prefix = "my.service")
public class MyProperties {

    /** 是否启用 */
    private boolean enabled = true;

    /** 服务名称 */
    private String name = "default";

    /** 超时时间(毫秒) */
    private Duration timeout = Duration.ofSeconds(30);

    /** 重试配置 */
    private Retry retry = new Retry();

    // getter/setter 省略

    public static class Retry {
        private int maxAttempts = 3;
        private Duration backoff = Duration.ofSeconds(1);
        // getter/setter 省略
    }
}

6.4 编写核心服务

public class MyService {

    private final MyProperties properties;

    public MyService(MyProperties properties) {
        this.properties = properties;
    }

    public String execute(String input) {
        if (!properties.isEnabled()) {
            throw new IllegalStateException("MyService is disabled");
        }
        // 业务逻辑
        return properties.getName() + ": " + input;
    }
}

6.5 编写自动配置类

@AutoConfiguration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
@ConditionalOnProperty(prefix = "my.service", name = "enabled",
                       havingValue = "true", matchIfMissing = true)
public class MyAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public MyService myService(MyProperties properties) {
        return new MyService(properties);
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "my.service", name = "health-indicator",
                           havingValue = "true")
    public MyServiceHealthIndicator myServiceHealthIndicator(MyService service) {
        return new MyServiceHealthIndicator(service);
    }
}

6.6 注册自动配置

# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.my.MyAutoConfiguration

6.7 Starter 模块 pom.xml

<project>
    <groupId>com.example</groupId>
    <artifactId>my-spring-boot-starter</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>my-spring-boot-autoconfigure</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!-- 第三方依赖 -->
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>my-core-library</artifactId>
            <version>2.0.0</version>
        </dependency>
    </dependencies>
</project>

6.8 使用自定义 Starter

# application.yml
my:
  service:
    enabled: true
    name: "MyApp"
    timeout: 60s
    retry:
      max-attempts: 5
      backoff: 2s
@Service
public class BusinessService {

    @Autowired
    private MyService myService;  // 自动注入

    public void doWork() {
        String result = myService.execute("task");
    }
}

七、配置管理

7.1 application.yml 基本语法

server:
  port: 8080
  servlet:
    context-path: /api

spring:
  application:
    name: my-service
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: ${DB_PASSWORD:default}  # 环境变量,带默认值
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5

logging:
  level:
    root: INFO
    com.example: DEBUG

7.2 application.properties 等价写法

server.port=8080
server.servlet.context-path=/api
spring.application.name=my-service
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=${DB_PASSWORD:default}
spring.datasource.hikari.maximum-pool-size=20
logging.level.root=INFO
logging.level.com.example=DEBUG

7.3 Profile 多环境配置

# application.yml(公共配置)
spring:
  profiles:
    active: dev  # 激活 dev 环境
    group:
      production:
        - proddb
        - promq

---
# application-dev.yml
server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/dev_db

---
# application-prod.yml
server:
  port: 80
spring:
  datasource:
    url: jdbc:mysql://prod-host:3306/prod_db

7.4 激活 Profile 的方式

# 1. 配置文件
spring.profiles.active=prod

# 2. 命令行参数
java -jar app.jar --spring.profiles.active=prod

# 3. 环境变量
export SPRING_PROFILES_ACTIVE=prod

# 4. JVM 参数
java -Dspring.profiles.active=prod -jar app.jar

7.5 @Profile 注解

@Configuration
@Profile("dev")
public class DevDataSourceConfig {
    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .build();
    }
}

@Service
@Profile({"prod", "staging"})
public class ProductionService implements MyService {
    // 仅在 prod/staging 环境生效
}

7.6 类型安全配置绑定

@Component
@ConfigurationProperties(prefix = "app.mail")
@Validated  // 支持 JSR-303 校验
public class MailProperties {

    @NotBlank
    private String host;

    @Min(1) @Max(65535)
    private int port = 25;

    private String username;
    private String password;

    private boolean sslEnabled = false;

    // getter/setter
}
app:
  mail:
    host: smtp.example.com
    port: 587
    username: user
    password: pass
    ssl-enabled: true

7.7 @Value vs @ConfigurationProperties

特性@Value@ConfigurationProperties
松散绑定不支持支持(my-name → myName)
SpEL支持不支持
校验不支持支持 @Validated
批量绑定不支持支持
元数据提示无有(spring-configuration-processor)

八、配置优先级与外部化配置

8.1 完整优先级(从高到低)

1.  命令行参数 --server.port=9090
2.  SPRING_APPLICATION_JSON 环境变量中的 JSON
3.  ServletConfig / ServletContext 初始化参数
4.  JNDI 属性(java:comp/env)
5.  Java 系统属性(-Dserver.port=9090)
6.  操作系统环境变量(SERVER_PORT=9090)
7.  random.* 随机值属性源
8.  jar 包外 application-{profile}.yml
9.  jar 包内 application-{profile}.yml
10. jar 包外 application.yml
11. jar 包内 application.yml
12. @PropertySource 注解引入
13. SpringApplication.setDefaultProperties()

8.2 外部化配置加载

# 指定配置文件位置(替换默认)
java -jar app.jar --spring.config.location=file:/opt/config/

# 追加配置位置(不替换默认)
java -jar app.jar --spring.config.additional-location=file:/opt/config/

# 指定配置文件名
java -jar app.jar --spring.config.name=myapp

# 导入其他配置(Spring Boot 2.4+)
# application.yml
spring:
  config:
    import:
      - optional:classpath:extra.yml
      - optional:file:/opt/config/override.yml
      - configtree:/etc/config/  # Kubernetes ConfigMap

8.3 配置加密(Jasypt)

<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>3.0.5</version>
</dependency>
spring:
  datasource:
    password: ENC(加密后的密文)

jasypt:
  encryptor:
    algorithm: PBEWithMD5AndDES
    password: ${JASYPT_KEY}  # 通过环境变量传入密钥

九、Actuator 监控端点

9.1 引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

9.2 端点配置

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,env,beans,loggers
        # include: "*"  # 暴露所有(生产慎用)
      base-path: /actuator
  endpoint:
    health:
      show-details: when_authorized  # never/when_authorized/always
    shutdown:
      enabled: false  # 默认关闭
  server:
    port: 9090  # 管理端口独立
  health:
    db:
      enabled: true
    redis:
      enabled: true

9.3 常用端点

端点说明
GET /actuator/health健康检查
GET /actuator/info应用信息
GET /actuator/metrics指标列表
GET /actuator/metrics/{name}具体指标
GET /actuator/env环境变量
GET /actuator/beans所有 Bean
GET /actuator/mappingsURL 映射
GET /actuator/configprops配置属性
GET /actuator/loggers日志级别
POST /actuator/loggers/{name}修改日志级别
GET /actuator/threaddump线程转储
GET /actuator/heapdump堆转储(下载)
POST /actuator/shutdown优雅停机

9.4 自定义 HealthIndicator

@Component
public class CustomHealthIndicator implements HealthIndicator {

    @Override
    public Health health() {
        boolean serviceUp = checkExternalService();
        if (serviceUp) {
            return Health.up()
                .withDetail("service", "external-api")
                .withDetail("latency", "50ms")
                .build();
        }
        return Health.down()
            .withDetail("service", "external-api")
            .withDetail("error", "Connection timeout")
            .build();
    }

    private boolean checkExternalService() {
        // 检查外部服务可用性
        return true;
    }
}

9.5 自定义 Endpoint

@Endpoint(id = "custom")
@Component
public class CustomEndpoint {

    @ReadOperation  // GET /actuator/custom
    public Map<String, Object> info() {
        Map<String, Object> map = new HashMap<>();
        map.put("version", "1.0.0");
        map.put("timestamp", System.currentTimeMillis());
        return map;
    }

    @WriteOperation  // POST /actuator/custom
    public String update(@Selector String key, String value) {
        // 执行写操作
        return "Updated " + key + " = " + value;
    }

    @DeleteOperation  // DELETE /actuator/custom/{key}
    public String delete(@Selector String key) {
        return "Deleted " + key;
    }
}

9.6 集成 Prometheus

<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
management:
  endpoints:
    web:
      exposure:
        include: prometheus

访问 /actuator/prometheus 获取 Prometheus 格式指标数据。


十、内嵌 Web 服务器

10.1 默认 Tomcat 配置

server:
  port: 8080
  servlet:
    context-path: /
  tomcat:
    threads:
      max: 200
      min-spare: 10
    max-connections: 8192
    accept-count: 100
    connection-timeout: 20000
  compression:
    enabled: true
    mime-types: text/html,application/json
  ssl:
    key-store: classpath:keystore.p12
    key-store-password: changeit
    key-store-type: PKCS12

10.2 切换到 Jetty

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

10.3 切换到 Undertow

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

10.4 编程式自定义

@Component
public class TomcatCustomizer implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

    @Override
    public void customize(TomcatServletWebServerFactory factory) {
        factory.addConnectorCustomizers(connector -> {
            connector.setPort(9090);
            Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
            protocol.setMaxThreads(300);
            protocol.setMinSpareThreads(20);
        });

        factory.addContextCustomizers(context -> {
            context.setSessionTimeout(30, TimeUnit.MINUTES);
        });
    }
}

10.5 WAR 部署(外置服务器)

@SpringBootApplication
public class Application extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(Application.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
<packaging>war</packaging>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>

十一、日志管理

11.1 默认日志配置

Spring Boot 默认使用 SLF4J + Logback:

logging:
  level:
    root: INFO
    com.example: DEBUG
    org.springframework.web: WARN
  file:
    name: logs/app.log
    max-size: 100MB
    max-history: 30
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
    file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
  logback:
    rollingpolicy:
      max-file-size: 100MB
      max-history: 30
      total-size-cap: 3GB

11.2 logback-spring.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <springProperty scope="context" name="APP_NAME"
                    source="spring.application.name" defaultValue="app"/>

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/${APP_NAME}.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>logs/${APP_NAME}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <maxFileSize>100MB</maxFileSize>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>

    <!-- 按 Profile 区分 -->
    <springProfile name="dev">
        <root level="DEBUG">
            <appender-ref ref="CONSOLE"/>
        </root>
    </springProfile>

    <springProfile name="prod">
        <root level="INFO">
            <appender-ref ref="CONSOLE"/>
            <appender-ref ref="FILE"/>
        </root>
    </springProfile>
</configuration>

11.3 切换到 Log4j2

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>

11.4 日志分组

logging:
  group:
    mygroup: com.example.service, com.example.repository
  level:
    mygroup: DEBUG  # 一次设置整组级别

十二、异常处理

12.1 默认错误处理机制

Spring Boot 默认注册 BasicErrorController,处理 /error 请求:

// 默认 JSON 错误响应
{
    "timestamp": "2024-01-01T10:00:00.000+00:00",
    "status": 500,
    "error": "Internal Server Error",
    "path": "/api/users"
}

12.2 全局异常处理(推荐)

@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    // 处理业务异常
    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<ErrorResponse> handleBusiness(BusinessException ex) {
        ErrorResponse error = new ErrorResponse(
            ex.getCode(),
            ex.getMessage(),
            LocalDateTime.now()
        );
        return ResponseEntity.status(ex.getHttpStatus()).body(error);
    }

    // 处理参数校验异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidation(
            MethodArgumentNotValidException ex) {
        String message = ex.getBindingResult().getFieldErrors().stream()
            .map(e -> e.getField() + ": " + e.getDefaultMessage())
            .collect(Collectors.joining("; "));
        ErrorResponse error = new ErrorResponse(400, message, LocalDateTime.now());
        return ResponseEntity.badRequest().body(error);
    }

    // 处理资源未找到
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
        ErrorResponse error = new ErrorResponse(404, ex.getMessage(), LocalDateTime.now());
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
    }

    // 兜底处理
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleAll(Exception ex) {
        log.error("Unhandled exception", ex);
        ErrorResponse error = new ErrorResponse(
            500, "Internal Server Error", LocalDateTime.now());
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
    }
}

12.3 统一错误响应体

@Data
@AllArgsConstructor
public class ErrorResponse {
    private int code;
    private String message;
    private LocalDateTime timestamp;
    private String traceId;  // 链路追踪ID
}

// 自定义业务异常
public class BusinessException extends RuntimeException {
    private final int code;
    private final HttpStatus httpStatus;

    public BusinessException(int code, String message, HttpStatus httpStatus) {
        super(message);
        this.code = code;
        this.httpStatus = httpStatus;
    }
}

12.4 ProblemDetail(Spring Boot 3 / RFC 7807)

# application.yml
spring:
  mvc:
    problemdetails:
      enabled: true
@RestControllerAdvice
public class ProblemDetailExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ProblemDetail handleNotFound(ResourceNotFoundException ex) {
        ProblemDetail problem = ProblemDetail.forStatusAndDetail(
            HttpStatus.NOT_FOUND, ex.getMessage());
        problem.setTitle("Resource Not Found");
        problem.setProperty("timestamp", Instant.now());
        problem.setProperty("resourceId", ex.getResourceId());
        return problem;
    }
}

响应格式:

{
    "type": "about:blank",
    "title": "Resource Not Found",
    "status": 404,
    "detail": "User with id 123 not found",
    "instance": "/api/users/123",
    "timestamp": "2024-01-01T10:00:00Z",
    "resourceId": "123"
}

十三、Spring Boot 3 新特性

13.1 Jakarta EE 迁移

// Spring Boot 2.x
import javax.servlet.http.HttpServletRequest;
import javax.persistence.Entity;
import javax.validation.constraints.NotNull;

// Spring Boot 3.x
import jakarta.servlet.http.HttpServletRequest;
import jakarta.persistence.Entity;
import jakarta.validation.constraints.NotNull;

13.2 GraalVM Native Image

<!-- pom.xml -->
<plugin>
    <groupId>org.graalvm.buildtools</groupId>
    <artifactId>native-maven-plugin</artifactId>
</plugin>
# 构建 Native Image
mvn -Pnative native:compile

# 或使用 Buildpacks
mvn -Pnative spring-boot:build-image
// AOT 处理:注册反射信息
@RegisterReflectionForBinding(UserDTO.class)
@Configuration
public class NativeConfig {
}

// RuntimeHints(编程式)
public class MyRuntimeHints implements RuntimeHintsRegistrar {
    @Override
    public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
        hints.resources().registerPattern("my-config/*");
        hints.reflection().registerType(MyClass.class,
            MemberCategory.INVOKE_PUBLIC_METHODS);
    }
}

@ImportRuntimeHints(MyRuntimeHints.class)
@Configuration
public class AppConfig {
}

13.3 HTTP 接口客户端(声明式)

// 定义接口
@HttpExchange("/users")
public interface UserClient {

    @GetExchange
    List<User> findAll();

    @GetExchange("/{id}")
    User findById(@PathVariable Long id);

    @PostExchange
    User create(@RequestBody UserCreateRequest request);

    @DeleteExchange("/{id}")
    void delete(@PathVariable Long id);
}

// 配置 Bean
@Configuration
public class HttpClientConfig {

    @Bean
    public UserClient userClient() {
        RestClient restClient = RestClient.builder()
            .baseUrl("http://localhost:8080/api")
            .build();

        HttpServiceProxyFactory factory = HttpServiceProxyFactory
            .builderFor(RestClientAdapter.create(restClient))
            .build();

        return factory.createClient(UserClient.class);
    }
}

13.4 虚拟线程支持(Spring Boot 3.2+)

spring:
  threads:
    virtual:
      enabled: true  # Tomcat 使用虚拟线程处理请求
// 编程式使用虚拟线程
@Bean
public TaskExecutor applicationTaskExecutor() {
    return new TaskExecutorAdapter(
        Executors.newVirtualThreadPerTaskExecutor());
}

13.5 Docker Compose 支持(Spring Boot 3.1+)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-docker-compose</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>
# compose.yaml(项目根目录)
services:
  postgres:
    image: postgres:16
    ports:
      - "5432:5432"
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
  redis:
    image: redis:7
    ports:
      - "6379:6379"

启动时自动连接 Docker Compose 中的服务,无需手动配置连接信息。

13.6 可观测性(Observability)

@RestController
public class OrderController {

    private final ObservationRegistry observationRegistry;

    @GetMapping("/orders/{id}")
    public Order getOrder(@PathVariable Long id) {
        return Observation.createNotStarted("get-order", observationRegistry)
            .lowCardinalityKeyValue("order.type", "standard")
            .highCardinalityKeyValue("order.id", id.toString())
            .observe(() -> orderService.findById(id));
    }
}

十四、自定义 AutoConfiguration 实战

14.1 实战场景:自定义限流 Starter

需求: 提供基于注解的接口限流能力,支持配置限流策略。

14.2 项目结构

ratelimit-spring-boot-starter/
├── ratelimit-spring-boot-autoconfigure/
│   ├── src/main/java/com/example/ratelimit/
│   │   ├── RateLimitAutoConfiguration.java
│   │   ├── RateLimitProperties.java
│   │   ├── RateLimitAspect.java
│   │   ├── RateLimiter.java
│   │   ├── SlidingWindowRateLimiter.java
│   │   └── RateLimitExceededException.java
│   └── src/main/resources/META-INF/spring/
│       └── ...AutoConfiguration.imports
└── ratelimit-spring-boot-starter/
    └── pom.xml

14.3 注解定义

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {
    /** 限流 key 前缀 */
    String key() default "";
    /** 时间窗口内最大请求数 */
    int permits() default 100;
    /** 时间窗口(秒) */
    int window() default 60;
    /** 限流维度:IP / USER / GLOBAL */
    RateLimitType type() default RateLimitType.IP;
}

public enum RateLimitType {
    IP, USER, GLOBAL
}

14.4 限流器实现

public interface RateLimiter {
    boolean tryAcquire(String key, int permits, int windowSeconds);
}

public class SlidingWindowRateLimiter implements RateLimiter {

    private final ConcurrentHashMap<String, Deque<Long>> windows =
        new ConcurrentHashMap<>();

    @Override
    public boolean tryAcquire(String key, int permits, int windowSeconds) {
        long now = System.currentTimeMillis();
        long windowStart = now - windowSeconds * 1000L;

        Deque<Long> timestamps = windows.computeIfAbsent(key,
            k -> new ConcurrentLinkedDeque<>());

        // 移除窗口外的记录
        while (!timestamps.isEmpty() && timestamps.peekFirst() < windowStart) {
            timestamps.pollFirst();
        }

        if (timestamps.size() < permits) {
            timestamps.addLast(now);
            return true;
        }
        return false;
    }
}

14.5 AOP 切面

@Aspect
public class RateLimitAspect {

    private final RateLimiter rateLimiter;
    private final RateLimitProperties properties;

    public RateLimitAspect(RateLimiter rateLimiter, RateLimitProperties properties) {
        this.rateLimiter = rateLimiter;
        this.properties = properties;
    }

    @Around("@annotation(rateLimit)")
    public Object around(ProceedingJoinPoint point, RateLimit rateLimit) throws Throwable {
        String key = buildKey(point, rateLimit);
        int permits = rateLimit.permits() > 0 ?
            rateLimit.permits() : properties.getDefaultPermits();
        int window = rateLimit.window() > 0 ?
            rateLimit.window() : properties.getDefaultWindow();

        if (!rateLimiter.tryAcquire(key, permits, window)) {
            throw new RateLimitExceededException(
                "Rate limit exceeded for key: " + key);
        }
        return point.proceed();
    }

    private String buildKey(ProceedingJoinPoint point, RateLimit rateLimit) {
        String prefix = rateLimit.key().isEmpty() ?
            point.getSignature().toShortString() : rateLimit.key();

        return switch (rateLimit.type()) {
            case IP -> prefix + ":" + getClientIp();
            case USER -> prefix + ":" + getCurrentUser();
            case GLOBAL -> prefix + ":global";
        };
    }
}

14.6 自动配置类

@AutoConfiguration
@ConditionalOnClass(Aspect.class)
@EnableConfigurationProperties(RateLimitProperties.class)
@ConditionalOnProperty(prefix = "ratelimit", name = "enabled",
                       havingValue = "true", matchIfMissing = true)
public class RateLimitAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(RateLimiter.class)
    public RateLimiter rateLimiter(RateLimitProperties properties) {
        return new SlidingWindowRateLimiter();
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(RateLimiter.class)
    public RateLimitAspect rateLimitAspect(RateLimiter rateLimiter,
                                           RateLimitProperties properties) {
        return new RateLimitAspect(rateLimiter, properties);
    }

    @Bean
    @ConditionalOnMissingBean
    public RateLimitExceptionHandler rateLimitExceptionHandler() {
        return new RateLimitExceptionHandler();
    }
}

14.7 使用方式

@RestController
@RequestMapping("/api/orders")
public class OrderController {

    @GetMapping
    @RateLimit(permits = 50, window = 60, type = RateLimitType.IP)
    public List<Order> listOrders() {
        return orderService.findAll();
    }

    @PostMapping
    @RateLimit(key = "create-order", permits = 10, window = 60, type = RateLimitType.USER)
    public Order createOrder(@RequestBody OrderRequest request) {
        return orderService.create(request);
    }
}
# application.yml
ratelimit:
  enabled: true
  default-permits: 100
  default-window: 60

十五、最佳实践

15.1 项目结构规范

com.example.myapp/
├── Application.java              # 启动类
├── config/                       # 配置类
│   ├── WebConfig.java
│   ├── SecurityConfig.java
│   └── CacheConfig.java
├── controller/                   # 控制层
├── service/                      # 业务层
│   └── impl/
├── repository/                   # 数据层
├── model/                        # 实体/DTO
│   ├── entity/
│   ├── dto/
│   └── vo/
├── common/                       # 公共组件
│   ├── exception/
│   ├── result/
│   └── util/
└── infrastructure/               # 基础设施
    ├── mq/
    ├── cache/
    └── external/

15.2 配置管理最佳实践

// 1. 使用 @ConfigurationProperties 而非大量 @Value
// 2. 敏感配置外部化
// 3. 提供合理默认值
// 4. 使用 spring-configuration-processor 生成元数据

@ConfigurationProperties(prefix = "app.payment")
public class PaymentProperties {
    private String apiKey;           // 从环境变量注入
    private Duration timeout = Duration.ofSeconds(30);  // 合理默认值
    private RetryConfig retry = new RetryConfig();      // 嵌套配置

    public record RetryConfig(int maxAttempts, Duration backoff) {
        public RetryConfig() { this(3, Duration.ofSeconds(1)); }
    }
}

15.3 优雅停机

server:
  shutdown: graceful  # 等待请求处理完毕

spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s  # 最长等待时间

15.4 健康检查与探针(Kubernetes)

management:
  endpoint:
    health:
      probes:
        enabled: true  # 启用 /actuator/health/liveness 和 /readiness
      group:
        readiness:
          include: db, redis, diskSpace
# Kubernetes Deployment
livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
  initialDelaySeconds: 30
readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080
  initialDelaySeconds: 10

15.5 测试最佳实践

// 集成测试
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
class OrderControllerIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private OrderService orderService;

    @Test
    void shouldCreateOrder() throws Exception {
        when(orderService.create(any())).thenReturn(new Order(1L, "test"));

        mockMvc.perform(post("/api/orders")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{\"item\":\"test\",\"quantity\":1}"))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.id").value(1));
    }
}

// 切片测试(仅加载 Web 层)
@WebMvcTest(OrderController.class)
class OrderControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private OrderService orderService;

    @Test
    void shouldReturn404WhenNotFound() throws Exception {
        when(orderService.findById(999L))
            .thenThrow(new ResourceNotFoundException("Order 999 not found"));

        mockMvc.perform(get("/api/orders/999"))
            .andExpect(status().isNotFound());
    }
}

// Testcontainers 集成
@SpringBootTest
@Testcontainers
class RepositoryIntegrationTest {

    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16")
        .withDatabaseName("testdb");

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }

    @Autowired
    private OrderRepository orderRepository;

    @Test
    void shouldSaveAndFindOrder() {
        Order saved = orderRepository.save(new Order(null, "item1"));
        assertThat(orderRepository.findById(saved.getId())).isPresent();
    }
}

15.6 容器化部署

# 多阶段构建
FROM eclipse-temurin:17-jdk AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN ./mvnw package -DskipTests

FROM eclipse-temurin:17-jre
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar

# 非 root 用户
RUN useradd -r -s /bin/false appuser
USER appuser

EXPOSE 8080
ENTRYPOINT ["java", "-XX:+UseContainerSupport", \
            "-XX:MaxRAMPercentage=75.0", \
            "-jar", "app.jar"]

或使用 Buildpacks(无需 Dockerfile):

mvn spring-boot:build-image -Dspring-boot.build-image.imageName=myapp:1.0

15.7 性能优化清单

# 1. 懒加载(开发环境加速启动)
spring:
  main:
    lazy-initialization: true  # 仅开发环境!

# 2. 减少不必要的自动配置
spring:
  autoconfigure:
    exclude:
      - org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration
      - org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration

# 3. JVM 参数优化
# -XX:+UseZGC -XX:+UseStringDeduplication
# -Dspring.jmx.enabled=false(关闭 JMX)
# -Dspring.config.location=file:/opt/config/(减少配置扫描)

15.8 安全建议

# Actuator 安全配置
management:
  endpoints:
    web:
      exposure:
        include: health,info  # 最小暴露
  endpoint:
    health:
      show-details: when_authorized

# 配合 Spring Security
spring:
  security:
    user:
      name: ${ADMIN_USER}
      password: ${ADMIN_PASSWORD}
@Configuration
@EnableWebSecurity
public class ActuatorSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.securityMatcher("/actuator/**")
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/actuator/health", "/actuator/info").permitAll()
                .anyRequest().hasRole("ADMIN"))
            .httpBasic(Customizer.withDefaults());
        return http.build();
    }
}

总结

Spring Boot 的核心价值在于通过自动配置和 Starter 机制大幅降低了 Spring 应用的搭建成本。理解其底层原理(条件装配、ImportSelector、事件机制)有助于:

  1. 排查问题:通过 --debug 查看条件评估报告,定位自动配置失败原因
  2. 自定义扩展:编写自己的 Starter 和 AutoConfiguration
  3. 性能调优:排除无用自动配置、合理使用懒加载
  4. 版本升级:理解 2.x → 3.x 的破坏性变更(Jakarta EE、配置文件格式)

掌握启动流程和自动配置原理,是从"会用"到"精通" Spring Boot 的关键一步。
在这里插入图片描述

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/weixin_49076592/article/details/163504512

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--