Java 框架 SpringBoot 自动装配原理
一、自动装配要解决什么问题?
在传统 Spring 应用中,我们需要大量编写 XML 配置或 Java @Configuration 类来声明 Bean 及其依赖关系(如数据源、事务管理器、MVC 组件等)。这个过程繁琐且容易出错。
自动装配的目标是:根据项目 classpath 中存在的依赖 Jar 包,自动推断出你需要哪些功能,并为你自动配置好这些功能所需的 Bean。例如:
- 当
classpath中存在H2数据库的 Jar 时,自动配置一个内存数据源。 - 当
classpath中存在Spring MVC的 Jar 时,自动配置DispatcherServlet、ViewResolver等组件。 - 当
classpath中存在spring-boot-starter-data-jpa时,自动配置 JPAEntityManagerFactory、TransactionManager等。
这一切都得益于三个核心注解、条件化配置和一种强大的发现机制。
二、核心注解:@SpringBootApplication
一切始于主类上的 @SpringBootApplication 注解。它是一个复合注解,由三个核心注解组成:
@SpringBootConfiguration // 本质是 @Configuration,标记该类为配置类
@EnableAutoConfiguration // ★ 启用自动配置的核心注解 ★
@ComponentScan( // 开启组件扫描,定位 @Component, @Service, @Controller 等
basePackages = {"com.example"}
)
public @interface SpringBootApplication {
// ...
}
其中,@EnableAutoConfiguration 是开启自动装配大门的钥匙。
三、自动装配的核心流程
@EnableAutoConfiguration 注解的关键是导入了一个选择器:AutoConfigurationImportSelector。
@Import(AutoConfigurationImportSelector.class) // ★ 导入选择器 ★
public @interface EnableAutoConfiguration {
// ...
}
整个自动装配的过程,特别是 AutoConfigurationImportSelector 的工作流程,可以通过以下流程图清晰地展现:
流程步骤解析:
-
SpringFactoriesLoader.loadFactoryNames():- 这个工具类会扫描 classpath 下所有 Jar 包中的
META-INF/spring.factories文件。 AutoConfigurationImportSelector请求加载EnableAutoConfiguration这个 key 对应的所有值。spring-boot-autoconfigure.jar是这个机制的核心。它的META-INF/spring.factories文件定义了所有官方支持的自动配置类的列表(超过 100 个),例如:# spring-boot-autoconfigure-xxx.jar/META-INF/spring.factories org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ ... (非常长的列表)
- 这个工具类会扫描 classpath 下所有 Jar 包中的
-
筛选与去重:
- 获取到所有候选配置类后,SpringBoot 会进行去重。
- 并根据
@EnableAutoConfiguration的exclude和excludeName属性排除用户明确指定的配置。
-
条件注解过滤 (最关键的步骤):
- 列表中的每一个自动配置类(如
DataSourceAutoConfiguration)都被大量的@Conditional派生注解所修饰。 - SpringBoot 会依据这些条件注解,逐一对候选配置类进行判断。只有所有条件都满足的配置类,才会被真正导入和生效。
- 列表中的每一个自动配置类(如
-
导入容器:
- 经过层层过滤后,最终有效的自动配置类会被当作普通的
@Configuration类一样,由 Spring 容器加载。 - 这些配置类内部使用
@Bean注解定义了一系列 Bean。这些@Bean方法同样受条件注解的控制。
- 经过层层过滤后,最终有效的自动配置类会被当作普通的
-
Bean 的注册:
- 自动配置类中符合条件的
@Bean方法会被执行,其返回的对象被注册到 IoC 容器中,成为我们可以直接@Autowired使用的 Bean。
- 自动配置类中符合条件的
四、条件注解:自动装配的“大脑”
条件注解是 SpringBoot 自动装配的灵魂,它决定了“在什么条件下配置才生效”。它们位于 org.springframework.boot.autoconfigure.condition 包下。
| 注解 | 生效条件 |
|---|---|
@ConditionalOnClass | classpath 下存在指定的类时生效。 |
@ConditionalOnMissingClass | classpath 下不存在指定的类时生效。 |
@ConditionalOnBean | Spring 容器中存在指定类型的 Bean 时生效。 |
@ConditionalOnMissingBean | 这是最常用的注解之一。Spring 容器中不存在指定类型的 Bean 时生效。这为用户自定义 Bean 提供了覆盖自动配置的机会。 |
@ConditionalOnProperty | 指定的配置属性有特定的值时生效。 |
@ConditionalOnResource | classpath 下存在指定的资源文件时生效。 |
@ConditionalOnWebApplication | 当前应用是 Web 应用时生效。 |
@ConditionalOnNotWebApplication | 当前应用不是 Web 应用时生效。 |
@ConditionalOnJndi | 指定的 JNDI 位置存在时生效。 |
示例:DataSourceAutoConfiguration
让我们看一个经典的例子,它展示了条件注解如何精妙地工作:
@Configuration(proxyBeanMethods = false) // 表明这是一个配置类
// 条件1: 必须在classpath下找到DataSource.class和EmbeddedDatabaseType.class
@ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class})
// 条件2: 必须在classpath下找到Spring的JdbcTemplate类
@ConditionalOnMissingBean(type = "org.springframework.jdbc.core.JdbcOperations")
// 条件3: 必须存在spring.datasource.*的相关配置属性
@ConditionalOnProperty(prefix = "spring.datasource", name = {"name"}, matchIfMissing = true)
public class DataSourceAutoConfiguration {
// 这个配置在classpath下有H2、HSQL等内嵌DB驱动时才生效
@Configuration(proxyBeanMethods = false)
@Conditional(EmbeddedDatabaseCondition.class)
@ConditionalOnMissingBean({DataSource.class, XADataSource.class})
protected static class EmbeddedDatabaseConfiguration {
@Bean
@ConditionalOnMissingBean(name = {"dataSource"}, type = {DataSource.class})
public DataSource dataSource(DataSourceProperties properties) {
// 使用配置的属性构建一个内嵌数据源
return new EmbeddedDataSourceBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
}
// 这个配置在classpath下有Tomcat JDBC连接池时才生效
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
@ConditionalOnMissingBean({DataSource.class, XADataSource.class})
@ConditionalOnProperty(prefix = "spring.datasource", name = {"type"}, havingValue = "org.apache.tomcat.jdbc.pool.DataSource", matchIfMissing = true)
protected static class TomcatDataSourceConfiguration {
@Bean
public DataSource dataSource(DataSourceProperties properties) {
// 构建一个Tomcat JDBC连接池
TomcatDataSource dataSource = new TomcatDataSource();
dataSource.setUrl(properties.getUrl());
dataSource.setUsername(properties.getUsername());
// ... 更多配置
return dataSource;
}
}
}
逻辑是:
- 如果我没提供自己的
DataSourceBean (@ConditionalOnMissingBean),并且… - classpath 里有内嵌数据库驱动(如 H2),那么 SpringBoot 就自动给我配一个内嵌
DataSource。 - 如果 classpath 里有 Tomcat JDBC 连接池,并且配置了
type,那么 SpringBoot 就自动给我配一个 Tomcat 连接池。 - 如果我手动在自定义配置类里
@Bean了一个DataSource,那么因为@ConditionalOnMissingBean条件不满足,SpringBoot 的自动配置就会跳过,从而实现了“覆盖”。
五、总结与最佳实践
自动装配原理总结:
- 启动:
@SpringBootApplication->@EnableAutoConfiguration。 - 加载:
AutoConfigurationImportSelector通过SpringFactoriesLoader加载所有META-INF/spring.factories中定义的自动配置类。 - 过滤:利用大量的条件注解 (
@ConditionalOnXxx),根据当前环境(classpath、已存在的 Bean、配置属性等)对配置类进行筛选。 - 装配:最终生效的配置类向容器中注入预先设计好的、满足当前应用场景的 Bean。
最佳实践:
- 查看自动配置详情:使用
--debug模式启动应用 (java -jar myapp.jar --debug),SpringBoot 会打印出所有自动配置类的评估报告(哪些生效,哪些未生效及其原因)。 - 覆盖自动配置:只需手动在你的
@Configuration类中定义一个 Bean(如DataSource),即可覆盖自动配置提供的默认 Bean。 - 通过属性微调:绝大多数自动配置的 Bean 都提供了相应的配置属性(以
spring.*开头),可以在application.properties中修改其行为,而无需覆盖整个 Bean。 - 排除特定自动配置:如果明确不需要某项自动配置,可以使用
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})将其关闭。
本文来自博客园,作者:NeoLshu,转载请注明原文链接:https://www.cnblogs.com/neolshu/p/19120353

浙公网安备 33010602011771号