⚡ Spring Boot自动配置:约定优于配置的魔法!

副标题:为什么Spring Boot如此简单?自动配置的秘密!🎯


🎬 开场:Spring Boot的革命

Spring vs Spring Boot

ini 复制代码
传统Spring(繁琐):

1. 配置数据源
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"/>
    <property name="username" value="root"/>
    <property name="password" value="password"/>
</bean>

2. 配置SqlSessionFactory
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>

3. 配置事务管理器
<bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

4. 配置Web.xml
5. 配置DispatcherServlet
6. 配置视图解析器
... 还有N多配置 😫


Spring Boot(简单):

1. 添加依赖
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>

2. 配置文件
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=password

完成!✅ 其他的全部自动配置!

📚 自动配置核心原理

@SpringBootApplication注解

java 复制代码
/**
 * Spring Boot启动类
 */
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

/**
 * @SpringBootApplication是一个组合注解
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration     // 1. 配置类
@EnableAutoConfiguration     // 2. 启用自动配置(核心)
@ComponentScan              // 3. 组件扫描
public @interface SpringBootApplication {
}

@EnableAutoConfiguration核心

java 复制代码
/**
 * @EnableAutoConfiguration注解
 * 
 * 这是自动配置的入口!
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)  // 关键:导入选择器
public @interface EnableAutoConfiguration {
    
    // 排除的自动配置类
    Class<?>[] exclude() default {};
    
    // 排除的自动配置类名
    String[] excludeName() default {};
}

🎯 自动配置加载流程

完整流程图

markdown 复制代码
Spring Boot启动流程:

1. @SpringBootApplication
   ↓
2. @EnableAutoConfiguration
   ↓
3. AutoConfigurationImportSelector
   ↓
4. 读取 META-INF/spring.factories
   ↓
5. 加载所有自动配置类
   ↓
6. 条件注解过滤(@Conditional)
   ↓
7. 生效的自动配置类
   ↓
8. 创建Bean,完成配置

源码解析

java 复制代码
/**
 * 自动配置导入选择器
 * 
 * 源码位置:AutoConfigurationImportSelector
 */
public class AutoConfigurationImportSelector 
    implements DeferredImportSelector {
    
    /**
     * 选择要导入的配置类
     */
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        }
        
        // 1. 获取自动配置类
        AutoConfigurationEntry autoConfigurationEntry = 
            getAutoConfigurationEntry(annotationMetadata);
        
        return StringUtils.toStringArray(
            autoConfigurationEntry.getConfigurations()
        );
    }
    
    /**
     * 获取自动配置类
     */
    protected AutoConfigurationEntry getAutoConfigurationEntry(
        AnnotationMetadata annotationMetadata) {
        
        if (!isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        }
        
        // 2. 加载候选配置类
        List<String> configurations = getCandidateConfigurations(
            annotationMetadata, 
            getAttributes(annotationMetadata)
        );
        
        // 3. 去重
        configurations = removeDuplicates(configurations);
        
        // 4. 获取排除的配置类
        Set<String> exclusions = getExclusions(annotationMetadata, attributes);
        checkExcludedClasses(configurations, exclusions);
        configurations.removeAll(exclusions);
        
        // 5. 过滤(条件注解)
        configurations = getConfigurationClassFilter().filter(configurations);
        
        // 6. 触发事件
        fireAutoConfigurationImportEvents(configurations, exclusions);
        
        return new AutoConfigurationEntry(configurations, exclusions);
    }
    
    /**
     * 加载候选配置类
     * 
     * 核心方法:从spring.factories文件读取
     */
    protected List<String> getCandidateConfigurations(
        AnnotationMetadata metadata, 
        AnnotationAttributes attributes) {
        
        // 从META-INF/spring.factories文件加载
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
            getSpringFactoriesLoaderFactoryClass(),  // EnableAutoConfiguration.class
            getBeanClassLoader()
        );
        
        Assert.notEmpty(configurations, 
            "No auto configuration classes found in META-INF/spring.factories.");
        
        return configurations;
    }
}

spring.factories文件

properties 复制代码
# spring-boot-autoconfigure.jar中的
# META-INF/spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
... 还有100多个自动配置类

🔍 条件注解详解

常用条件注解

java 复制代码
/**
 * 条件注解大全
 */

// 1. @ConditionalOnClass
// 当classpath存在某个类时,配置生效
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {
    // 只有当classpath中有DataSource类时,才会配置数据源
}

// 2. @ConditionalOnMissingClass
// 当classpath不存在某个类时,配置生效
@ConditionalOnMissingClass("org.springframework.web.servlet.DispatcherServlet")
public class NonWebConfiguration {
}

// 3. @ConditionalOnBean
// 当容器中存在某个Bean时,配置生效
@ConditionalOnBean(DataSource.class)
public class JdbcTemplateConfiguration {
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

// 4. @ConditionalOnMissingBean
// 当容器中不存在某个Bean时,配置生效
@Configuration
public class DataSourceConfiguration {
    
    @Bean
    @ConditionalOnMissingBean  // 如果用户没有配置DataSource,才使用默认的
    public DataSource dataSource() {
        return new HikariDataSource();
    }
}

// 5. @ConditionalOnProperty
// 当配置文件中存在某个属性时,配置生效
@ConditionalOnProperty(
    prefix = "spring.datasource",
    name = "type",
    havingValue = "com.zaxxer.hikari.HikariDataSource"
)
public class HikariDataSourceConfiguration {
}

// 6. @ConditionalOnResource
// 当classpath存在某个资源文件时,配置生效
@ConditionalOnResource(resources = "classpath:mybatis-config.xml")
public class MyBatisConfiguration {
}

// 7. @ConditionalOnWebApplication
// 当是Web应用时,配置生效
@ConditionalOnWebApplication
public class WebMvcAutoConfiguration {
}

// 8. @ConditionalOnNotWebApplication
// 当不是Web应用时,配置生效
@ConditionalOnNotWebApplication
public class NonWebAutoConfiguration {
}

💻 数据源自动配置实例

DataSourceAutoConfiguration

java 复制代码
/**
 * 数据源自动配置
 * 
 * 源码:org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
         DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
    
    /**
     * 嵌入式数据库配置(H2、HSQL等)
     */
    @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({ DataSourceConfiguration.Hikari.class,
             DataSourceConfiguration.Tomcat.class,
             DataSourceConfiguration.Dbcp2.class,
             DataSourceConfiguration.OracleUcp.class,
             DataSourceConfiguration.Generic.class })
    protected static class PooledDataSourceConfiguration {
    }
}

/**
 * Hikari连接池配置
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", 
                      havingValue = "com.zaxxer.hikari.HikariDataSource",
                      matchIfMissing = true)
static class Hikari {
    
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    HikariDataSource dataSource(DataSourceProperties properties) {
        HikariDataSource dataSource = createDataSource(
            properties, HikariDataSource.class
        );
        
        if (StringUtils.hasText(properties.getName())) {
            dataSource.setPoolName(properties.getName());
        }
        
        return dataSource;
    }
}

配置属性类

java 复制代码
/**
 * 数据源配置属性
 * 
 * 源码:org.springframework.boot.autoconfigure.jdbc.DataSourceProperties
 */
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
    
    private ClassLoader classLoader;
    
    /**
     * 数据源类型
     */
    private Class<? extends DataSource> type;
    
    /**
     * JDBC驱动类名
     */
    private String driverClassName;
    
    /**
     * JDBC URL
     */
    private String url;
    
    /**
     * 用户名
     */
    private String username;
    
    /**
     * 密码
     */
    private String password;
    
    // getter和setter...
}

使用方式

properties 复制代码
# application.properties

# 数据源配置(自动配置会读取)
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# Hikari连接池配置
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=30000

🎨 自定义自动配置

创建Starter

bash 复制代码
项目结构:
my-spring-boot-starter/
├── pom.xml
└── src/main/java
    └── com/example/autoconfigure
        ├── MyAutoConfiguration.java       # 自动配置类
        ├── MyProperties.java              # 配置属性
        └── MyService.java                 # 服务类
└── src/main/resources
    └── META-INF
        └── spring.factories               # 配置文件

自动配置类

java 复制代码
/**
 * 自定义自动配置类
 */
@Configuration
@EnableConfigurationProperties(MyProperties.class)  // 启用配置属性
@ConditionalOnClass(MyService.class)                // 类存在条件
@ConditionalOnProperty(
    prefix = "my.service",
    name = "enabled",
    havingValue = "true",
    matchIfMissing = true                           // 默认启用
)
public class MyAutoConfiguration {
    
    @Autowired
    private MyProperties properties;
    
    /**
     * 创建服务Bean
     */
    @Bean
    @ConditionalOnMissingBean
    public MyService myService() {
        MyService service = new MyService();
        service.setName(properties.getName());
        service.setTimeout(properties.getTimeout());
        return service;
    }
}

/**
 * 配置属性类
 */
@ConfigurationProperties(prefix = "my.service")
public class MyProperties {
    
    /**
     * 是否启用
     */
    private boolean enabled = true;
    
    /**
     * 服务名称
     */
    private String name = "default";
    
    /**
     * 超时时间(秒)
     */
    private int timeout = 30;
    
    // getter和setter...
}

/**
 * 服务类
 */
public class MyService {
    
    private String name;
    private int timeout;
    
    public void doSomething() {
        System.out.println("MyService: " + name + ", timeout: " + timeout);
    }
    
    // getter和setter...
}

spring.factories文件

properties 复制代码
# META-INF/spring.factories

# 自动配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfigure.MyAutoConfiguration

使用自定义Starter

xml 复制代码
<!-- pom.xml -->
<dependency>
    <groupId>com.example</groupId>
    <artifactId>my-spring-boot-starter</artifactId>
    <version>1.0.0</version>
</dependency>
properties 复制代码
# application.properties

# 自定义配置(自动生效)
my.service.enabled=true
my.service.name=MyCustomService
my.service.timeout=60
java 复制代码
/**
 * 使用自动配置的Service
 */
@SpringBootApplication
public class Application {
    
    @Autowired
    private MyService myService;  // 自动注入
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
    
    @PostConstruct
    public void test() {
        myService.doSomething();
        // 输出:MyService: MyCustomService, timeout: 60
    }
}

🔧 配置优先级

优先级顺序

matlab 复制代码
Spring Boot配置优先级(从高到低):

1. 命令行参数
   java -jar app.jar --server.port=8080

2. Java系统属性
   System.setProperty("server.port", "8080")

3. 操作系统环境变量
   export SERVER_PORT=8080

4. application-{profile}.properties/yml
   application-prod.properties

5. application.properties/yml
   application.properties

6. @PropertySource指定的配置文件

7. 默认属性
   SpringApplication.setDefaultProperties()

优先级高的会覆盖优先级低的!

示例

properties 复制代码
# application.properties
server.port=8080
my.name=default

# application-dev.properties
server.port=8081
my.name=dev

# application-prod.properties
server.port=8082
my.name=prod
bash 复制代码
# 启动命令
java -jar app.jar --spring.profiles.active=prod --server.port=9999

# 最终配置:
# server.port=9999(命令行参数优先级最高)
# my.name=prod(profile配置)

📊 自动配置报告

查看自动配置

properties 复制代码
# application.properties

# 开启自动配置报告
debug=true

启动日志输出

sql 复制代码
============================
CONDITIONS EVALUATION REPORT
============================

Positive matches:(生效的配置)
-----------------

   DataSourceAutoConfiguration matched:
      - @ConditionalOnClass found required classes 'javax.sql.DataSource' (OnClassCondition)
      
   WebMvcAutoConfiguration matched:
      - @ConditionalOnClass found required classes 'DispatcherServlet' (OnClassCondition)
      - @ConditionalOnWebApplication found 'SERVLET' web application (OnWebApplicationCondition)

Negative matches:(未生效的配置)
-----------------

   RedisAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'org.springframework.data.redis.core.RedisOperations' (OnClassCondition)
         
   MongoAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'com.mongodb.client.MongoClient' (OnClassCondition)

Actuator端点

xml 复制代码
<!-- 添加依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
properties 复制代码
# 暴露端点
management.endpoints.web.exposure.include=conditions

访问:http://localhost:8080/actuator/conditions

json 复制代码
{
  "contexts": {
    "application": {
      "positiveMatches": {
        "DataSourceAutoConfiguration": [
          {
            "condition": "OnClassCondition",
            "message": "@ConditionalOnClass found required classes 'javax.sql.DataSource'"
          }
        ]
      },
      "negativeMatches": {
        "RedisAutoConfiguration": {
          "notMatched": [
            {
              "condition": "OnClassCondition",
              "message": "did not find required class 'RedisOperations'"
            }
          ]
        }
      }
    }
  }
}

🎉 总结

核心原理

markdown 复制代码
1. @EnableAutoConfiguration
   - 自动配置的入口
   
2. AutoConfigurationImportSelector
   - 加载META-INF/spring.factories
   - 导入所有自动配置类
   
3. 条件注解过滤
   - @ConditionalOnClass
   - @ConditionalOnBean
   - @ConditionalOnProperty
   
4. 配置属性绑定
   - @ConfigurationProperties
   - 读取application.properties
   
5. 创建Bean
   - 满足条件的配置类生效
   - 创建相应的Bean

自动配置流程

less 复制代码
启动应用
    ↓
@SpringBootApplication
    ↓
@EnableAutoConfiguration
    ↓
读取spring.factories
    ↓
加载自动配置类
    ↓
条件注解过滤
    ↓
读取配置文件
    ↓
创建Bean
    ↓
应用启动完成

最佳实践

markdown 复制代码
1. 使用Starter简化配置 ✅
2. 通过配置文件定制行为 ✅
3. 使用@ConditionalOnMissingBean允许用户覆盖 ✅
4. 提供合理的默认值 ✅
5. 使用debug=true查看自动配置报告 ✅

记忆口诀

ini 复制代码
Spring Boot自动配置真神奇,
约定优于配置是核心。

EnableAutoConfiguration入口,
AutoConfigurationImportSelector选择器。

spring.factories配置文件,
列出所有自动配置类。

条件注解来过滤,
ConditionalOn系列注解。
OnClass类存在,
OnBean Bean存在,
OnProperty属性存在,
OnMissing都不存在。

ConfigurationProperties属性,
读取配置文件值。
application.properties配,
自动绑定到属性。

条件满足就生效,
创建Bean放容器。
用户配置优先级高,
可以覆盖默认配置。

自定义Starter很简单,
AutoConfiguration写配置,
Properties定义属性,
spring.factories注册类。

debug=true开报告,
查看哪些配置生效。
Positive生效的,
Negative未生效。

约定优于配置好,
开箱即用真方便!

愿你的Spring Boot应用开箱即用,配置简单高效! ⚡✨

相关推荐
aicoding_sh5 小时前
为 Claude Code CLI 提供美观且高度可定制的状态行,具有powerline support, themes, and more.
后端·github
码农小站5 小时前
从零搭建vsftpd服务器:避坑指南+实战解决方案
后端
掘金一周5 小时前
一个前端工程师的年度作品:从零开发媲美商业级应用的后台管理系统 | 掘金一周 10.23
前端·人工智能·后端
凤山老林5 小时前
SpringBoot 如何实现零拷贝:深度解析零拷贝技术
java·linux·开发语言·arm开发·spring boot·后端
程序员小假5 小时前
我们来讲一讲阻塞队列及其应用
java·后端
xtstart5 小时前
从1.5秒到80毫秒:我如何优化元数据平台的“万能搜索”接口
后端
Value_Think_Power5 小时前
okta access token 用户登录一小时后失效,如何延迟避免用户频发登录,保存在哪里,前端Vue 实现,后端是golang ; 提供一个方案
后端
用户68545375977695 小时前
📬 分布式消息队列:三大终极难题!
后端
调试人生的显微镜5 小时前
Wireshark抓包教程:JSON和HTTPS抓取
后端