SpringBoot自动装配原理
SpringBoot自动装配原理深度解析
目录
引言
SpringBoot的核心特性之一就是"约定优于配置",而这一理念的具体实现就是自动装配(Auto-Configuration)。自动装配极大地简化了Spring应用的开发,使开发者不再需要大量的XML配置或Java配置,只需引入相应的依赖,SpringBoot就能自动完成组件的装配工作。本文将深入探讨SpringBoot自动装配的原理、实现机制和源码分析,帮助读者全面理解这一核心特性。
自动装配基本概念
什么是自动装配
自动装配是SpringBoot的核心功能,它能够根据应用的依赖、环境和配置,自动配置Spring应用所需的组件。简单来说,自动装配就是SpringBoot应用在启动时,根据项目中导入的依赖jar包,自动创建并配置Bean。
// 一个典型的SpringBoot应用入口
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
解析:
- 仅通过
@SpringBootApplication
注解和一个main方法,就能启动一个完整的Spring应用 - 无需显式配置数据源、事务管理器、Web容器等组件
- SpringBoot会根据classpath中的依赖自动配置这些组件
传统Spring配置方式
在传统的Spring应用中,我们通常需要大量的XML配置或Java配置:
<!-- 传统的Spring XML配置 -->
<beans>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 其他配置 -->
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<!-- 更多配置... -->
</beans>
或者使用Java配置:
@Configuration
public class AppConfig {
@Bean
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("password");
return dataSource;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
// 其他配置
return em;
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
// 更多配置...
}
解析:
- 传统方式需要手动配置每个组件
- 配置繁琐,代码量大
- 组件之间的依赖关系需要手动维护
- 修改配置需要修改代码或XML
SpringBoot自动装配的优势
相比之下,SpringBoot的自动装配有以下优势:
- 简化配置:只需添加相应的依赖,无需显式配置
- 约定优于配置:提供合理的默认配置,大多数情况下可直接使用
- 可覆盖性:默认配置可以通过配置文件或自定义Bean覆盖
- 模块化:每个功能模块的自动配置相互独立
- 条件化装配:只有在满足特定条件时才进行装配
// 使用SpringBoot,只需添加依赖和少量配置
// pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
// application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=password
解析:
- 只需添加
spring-boot-starter-data-jpa
依赖 - 简单的属性配置即可完成数据源、JPA、事务等复杂组件的配置
- SpringBoot会自动创建DataSource、EntityManagerFactory、TransactionManager等Bean
自动装配核心原理
@SpringBootApplication注解
@SpringBootApplication
是SpringBoot应用的核心注解,它是一个组合注解,包含了以下三个注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
// 属性定义...
}
解析:
@SpringBootConfiguration
:标识这是一个SpringBoot配置类,本质上是@Configuration
@EnableAutoConfiguration
:启用自动装配机制@ComponentScan
:启用组件扫描,扫描同包及子包下的组件
@EnableAutoConfiguration注解
@EnableAutoConfiguration
是自动装配的核心注解,它的定义如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// 属性定义...
}
解析:
@AutoConfigurationPackage
:将注解所在包注册为自动配置包@Import(AutoConfigurationImportSelector.class)
:导入AutoConfigurationImportSelector,这是自动装配的关键类
ImportSelector机制
SpringBoot自动装配的核心是Spring的ImportSelector
接口,AutoConfigurationImportSelector
实现了这个接口:
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
AutoConfigurationImportSelector
的selectImports
方法会返回需要导入的自动配置类的类名数组:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 加载所有自动配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重
configurations = removeDuplicates(configurations);
// 排除不需要的配置
configurations = filter(configurations, autoConfigurationMetadata);
// 触发自动配置导入事件
fireAutoConfigurationImportEvents(configurations, exclusions);
return configurations.toArray(new String[0]);
}
spring.factories文件
SpringBoot使用spring.factories
文件来注册自动配置类。这个文件位于META-INF目录下,遵循Java的SPI(Service Provider Interface)机制。
# spring.factories文件示例
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfigure.FooAutoConfiguration,\
com.example.autoconfigure.BarAutoConfiguration
解析:
spring.factories
是一个properties文件- 键为
org.springframework.boot.autoconfigure.EnableAutoConfiguration
- 值为自动配置类的全限定名,多个类用逗号分隔
SpringBoot在启动时会扫描classpath下所有jar包中的META-INF/spring.factories
文件,加载EnableAutoConfiguration
对应的配置类。
条件注解
SpringBoot提供了丰富的条件注解,用于控制自动配置类是否生效:
@Configuration
@ConditionalOnClass(DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnProperty(name = "spring.datasource.type")
public DataSource dataSource(DataSourceProperties properties) {
// 创建数据源
}
}
常用的条件注解包括:
条件注解 | 描述 |
---|---|
@ConditionalOnClass | 当classpath中存在指定类时条件成立 |
@ConditionalOnMissingClass | 当classpath中不存在指定类时条件成立 |
@ConditionalOnBean | 当容器中存在指定Bean时条件成立 |
@ConditionalOnMissingBean | 当容器中不存在指定Bean时条件成立 |
@ConditionalOnProperty | 当配置属性满足条件时条件成立 |
@ConditionalOnResource | 当资源文件存在时条件成立 |
@ConditionalOnWebApplication | 当应用是Web应用时条件成立 |
@ConditionalOnExpression | 当SpEL表达式为true时条件成立 |
解析:
- 条件注解是自动装配的关键机制
- 确保只有在满足特定条件时,自动配置才会生效
- 避免了不必要的Bean创建,提高了应用性能
- 允许用户通过自定义Bean覆盖默认配置
自动装配执行流程
启动阶段
SpringBoot应用启动时,自动装配的执行流程如下:
- 从
SpringApplication.run()
方法开始 - 创建
ApplicationContext
(根据环境选择具体实现类) - 准备
ApplicationContext
(设置环境、初始化器等) - 刷新
ApplicationContext
,启动Spring容器 - 在容器刷新过程中,处理
@EnableAutoConfiguration
注解
// SpringApplication.run方法简化版
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return new SpringApplication(primarySource).run(args);
}
// 容器刷新过程中会处理@Import注解
public void refresh() {
// ...
// 注册Bean定义,包括处理@Import注解
invokeBeanFactoryPostProcessors(beanFactory);
// ...
}
加载候选配置
AutoConfigurationImportSelector
会加载所有候选的自动配置类:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 加载spring.factories中的配置
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
EnableAutoConfiguration.class, getBeanClassLoader());
return configurations;
}
SpringFactoriesLoader
负责扫描classpath下所有jar包中的META-INF/spring.factories
文件:
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
// 获取接口的全限定名
String factoryTypeName = factoryType.getName();
// 加载所有spring.factories文件中指定接口的实现类
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
条件过滤
加载候选配置后,会根据条件注解进行过滤:
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
// 创建过滤器链
List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
// 应用过滤器
for (AutoConfigurationImportFilter filter : filters) {
// 判断每个配置类是否匹配条件
boolean[] match = filter.match(configurations.toArray(new String[0]), autoConfigurationMetadata);
// 保留匹配的配置类
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
configurations.set(i, null);
}
}
// 移除不匹配的配置类
configurations.removeIf(Objects::isNull);
}
return configurations;
}
主要的过滤器包括:
OnClassCondition
:检查classpath中是否存在指定类OnBeanCondition
:检查容器中是否存在指定BeanOnPropertyCondition
:检查配置属性是否满足条件
Bean注册
过滤后的自动配置类会被注册为Bean定义,然后在容器刷新过程中实例化:
// ConfigurationClassParser处理@Import注解
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
// ...
// 处理ImportSelector
if (candidate.isAssignable(ImportSelector.class)) {
// 实例化ImportSelector
ImportSelector selector = candidate.instantiate();
// 调用selectImports方法获取需要导入的类
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
// 处理导入的类
processImports(configClass, asSourceClasses(importClassNames), checkForCircularImports);
}
// ...
}
最终,满足条件的自动配置类会被注册为Bean定义,并在容器刷新过程中实例化,完成自动装配。
自定义自动装配
创建自动配置类
要创建自己的自动配置,首先需要创建一个配置类:
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {
private final MyProperties properties;
public MyAutoConfiguration(MyProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public MyService myService() {
MyService service = new MyService();
service.setProperty(properties.getProperty());
return service;
}
}
配置spring.factories
在项目的META-INF/spring.factories
文件中注册自动配置类:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyAutoConfiguration
条件控制
使用条件注解控制自动配置的生效条件:
@Configuration
@ConditionalOnClass(MyService.class) // 当classpath中存在MyService类时
@ConditionalOnProperty(prefix = "my", name = "enabled", havingValue = "true", matchIfMissing = true) // 当my.enabled=true或未配置时
public class MyAutoConfiguration {
// ...
}
配置属性绑定
使用@ConfigurationProperties
绑定配置属性:
@ConfigurationProperties(prefix = "my")
public class MyProperties {
private String property = "default"; // 默认值
// getter和setter
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
用户可以在application.properties
或application.yml
中配置:
my.property=custom-value
自动装配源码分析
AutoConfigurationImportSelector
AutoConfigurationImportSelector
是自动装配的核心类,它实现了ImportSelector
接口:
public class AutoConfigurationImportSelector implements ImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 判断自动装配是否启用
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 加载自动配置元数据
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// 获取注解属性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取候选配置
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去重
configurations = removeDuplicates(configurations);
// 获取排除项
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 检查排除项
checkExcludedClasses(configurations, exclusions);
// 移除排除项
configurations.removeAll(exclusions);
// 过滤不满足条件的配置
configurations = filter(configurations, autoConfigurationMetadata);
// 触发自动配置导入事件
fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回需要导入的配置类
return StringUtils.toStringArray(configurations);
}
// 其他方法...
}
加载过程
getCandidateConfigurations
方法负责加载候选的自动配置类:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
// 使用SpringFactoriesLoader加载spring.factories中的配置
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. " +
"If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
SpringFactoriesLoader
的loadFactoryNames
方法会扫描classpath下所有jar包中的META-INF/spring.factories
文件:
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 尝试从缓存获取
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 获取所有spring.factories文件的URL
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
// 遍历每个URL,加载properties文件
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
// 解析properties文件,填充结果
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
// 缓存结果
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
过滤机制
filter
方法负责过滤不满足条件的配置类:
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
long startTime = System.nanoTime();
// 获取所有过滤器
List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
// 设置过滤器的环境信息
for (AutoConfigurationImportFilter filter : filters) {
invokeAwareMethods(filter);
}
// 转换为数组,便于处理
String[] candidates = StringUtils.toStringArray(configurations);
boolean[] match = new boolean[candidates.length];
// 应用每个过滤器
for (int i = 0; i < candidates.length; i++) {
match[i] = true;
}
for (AutoConfigurationImportFilter filter : filters) {
boolean[] filterMatch = filter.match(candidates, autoConfigurationMetadata);
for (int i = 0; i < filterMatch.length; i++) {
match[i] = match[i] && filterMatch[i];
}
}
// 保留匹配的配置
List<String> result = new ArrayList<>(candidates.length);
for (int i = 0; i < candidates.length; i++) {
if (match[i]) {
result.add(candidates[i]);
}
}
// 记录过滤耗时
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in " +
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
return result;
}
主要的过滤器包括:
OnClassCondition
:检查classpath中是否存在指定类OnBeanCondition
:检查容器中是否存在指定BeanOnPropertyCondition
:检查配置属性是否满足条件
这些过滤器都实现了AutoConfigurationImportFilter
接口:
public interface AutoConfigurationImportFilter {
boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata);
}
总结
问:SpringBoot自动装配的原理是什么?
关键知识点
SpringBoot自动装配的核心原理可以概括为:
- 通过
@SpringBootApplication
中的@EnableAutoConfiguration
注解启用自动装配 @EnableAutoConfiguration
通过@Import(AutoConfigurationImportSelector.class)
导入选择器AutoConfigurationImportSelector
实现了ImportSelector
接口,会返回需要导入的类名数组- 选择器通过
SpringFactoriesLoader
扫描classpath下所有jar包中的META-INF/spring.factories
文件 - 加载
EnableAutoConfiguration
对应的配置类列表 - 通过条件注解(如
@ConditionalOnClass
、@ConditionalOnMissingBean
等)过滤不满足条件的配置类 - 将满足条件的配置类注册为Bean定义,在容器刷新时实例化
深入解析
-
启动流程:
- SpringBoot应用启动时,从
SpringApplication.run()
方法开始 - 创建并准备
ApplicationContext
- 刷新容器,处理
@Import
注解,调用ImportSelector.selectImports()
方法 AutoConfigurationImportSelector
返回需要导入的自动配置类- 容器将这些类注册为Bean定义,并在后续过程中实例化
- SpringBoot应用启动时,从
-
条件装配:
- SpringBoot大量使用条件注解控制自动配置的生效条件
@ConditionalOnClass
:当classpath中存在指定类时条件成立@ConditionalOnMissingBean
:当容器中不存在指定Bean时条件成立@ConditionalOnProperty
:当配置属性满足条件时条件成立- 这些条件确保只有在需要时才创建相应的Bean
-
自定义扩展:
- 创建自动配置类,添加
@Configuration
和条件注解 - 使用
@EnableConfigurationProperties
绑定配置属性 - 在
META-INF/spring.factories
中注册自动配置类 - 提供默认配置,但允许用户覆盖
- 创建自动配置类,添加
-
追问:
- 自动装配与传统配置的区别:自动装配基于约定和条件,简化配置;传统方式需手动配置每个组件
- 如何禁用特定的自动配置:使用
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
或配置spring.autoconfigure.exclude
属性 - 自动装配的执行顺序:通过
@AutoConfigureOrder
、@AutoConfigureBefore
、@AutoConfigureAfter
控制 - 自动装配的优缺点:优点是简化配置、提高开发效率;缺点是可能引入不需要的依赖、增加启动时间
SpringBoot的自动装配机制是其"约定优于配置"理念的核心实现,通过巧妙地利用Spring的扩展点和条件装配,实现了简化配置的目标,使开发者能够专注于业务逻辑而非基础设施的配置。