ITPub博客

首页 > 数据库 > 国内数据库 > Spring之SpringBoot

Spring之SpringBoot

国内数据库 作者:丸喵喵 时间:2026-09-18 17:39:09 0 删除 编辑

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 启动参数

AI写代码

yaml

1

2

启动时输出:

============================

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

AI写代码

text

1

2

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/mappings URL 映射

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"]


dockerfile

或使用 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、事件机制)有助于:

排查问题:通过 --debug 查看条件评估报告,定位自动配置失败原因

自定义扩展:编写自己的 Starter 和 AutoConfiguration

性能调优:排除无用自动配置、合理使用懒加载

版本升级:理解 2.x → 3.x 的破坏性变更(Jakarta EE、配置文件格式)

掌握启动流程和自动配置原理,是从"会用"到"精通" Spring Boot 的关键一步。

————————————————

版权声明:本文为CSDN博主「萧瑟余晖」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

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


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/29568843/viewspace-3183014/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2014-03-27

  • 博文量
    215
  • 访问量
    537368