副标题:为什么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应用开箱即用,配置简单高效! ⚡✨