Spring Boot自动装配代码详解
-
概述
- Spring Boot自动装配是其核心特性之一,它能够根据项目中添加的依赖自动配置Spring应用程序。通过自动装配,开发人员可以减少大量的配置工作,快速搭建起一个可用的Spring应用。
-
关键组件和注解
@SpringBootApplication
注解- 这是Spring Boot应用的主注解,它是一个组合注解,实际上包含了
@Configuration
、@EnableAutoConfiguration
和@ComponentScan
三个注解。 @Configuration
:表明这个类是一个配置类,用于定义Spring的Bean。在配置类中,可以通过@Bean
注解方法来创建和配置Bean实例。例如:@Configuration public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } }
- 这里定义了一个名为
myService
的Bean,类型是MyService
,其实现是MyServiceImpl
。
- 这里定义了一个名为
@EnableAutoConfiguration
:这是自动装配的关键注解。它会启用Spring Boot的自动配置机制,告诉Spring Boot去根据项目的依赖和配置自动配置应用。它通过@Import
注解导入了AutoConfigurationImportSelector
类来实现自动配置的功能。@ComponentScan
:用于扫描指定包及其子包下的组件(如@Component
、@Service
、@Repository
、@Controller
等注解标记的类),将它们注册为Spring的Bean。默认情况下,它会扫描主应用类所在的包及其子包。例如,如果主应用类在com.example.myapp
包下,那么@ComponentScan
会扫描com.example.myapp
及其所有子包下的组件。
- 这是Spring Boot应用的主注解,它是一个组合注解,实际上包含了
AutoConfigurationImportSelector
类- 这个类是自动装配的核心实现类。它实现了
ImportSelector
接口,该接口的selectImports
方法用于返回要导入的配置类的全限定名数组。 - 在
AutoConfigurationImportSelector
中,selectImports
方法会从META - INF/spring.factories
文件中读取自动配置类的列表。它通过SpringFactoriesLoader.loadFactoryNames
方法来加载这些配置类,例如:public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
- 这里的
getAutoConfigurationEntry
方法会获取需要自动导入的配置类,这些配置类是根据项目中的依赖和条件来确定的。
- 这个类是自动装配的核心实现类。它实现了
-
META - INF/spring.factories
文件机制- 这是自动装配的重要配置文件。在Spring Boot的各个依赖中,都可以包含
META - INF/spring.factories
文件。 - 这个文件的格式是
key = value
的形式,其中一个关键的配置项是org.springframework.boot.autoconfigure.EnableAutoConfiguration
。它的值是一个自动配置类的列表,例如:org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ // 其他自动配置类
- 当Spring Boot启动时,
AutoConfigurationImportSelector
会读取这些文件,找到所有的自动配置类。然后,根据条件注解(如@ConditionalOnClass
、@ConditionalOnMissingBean
等)来判断这些自动配置类是否应该被应用。 - 条件注解示例
@ConditionalOnClass
:这个注解用于判断某个类是否在类路径上。例如,DataSourceAutoConfiguration
中有如下注解:@Configuration @ConditionalOnClass({DataSource.class, EmbeddedDatabaseType.class}) public class DataSourceAutoConfiguration { // 配置内容 }
- 这表示只有当
DataSource
类和EmbeddedDatabaseType
类都在类路径上时,DataSourceAutoConfiguration
这个自动配置类才会被应用。这样可以确保只有在项目中添加了相关的数据源依赖时,才会进行数据源的自动配置。
- 这表示只有当
@ConditionalOnMissingBean
:用于判断某个类型的Bean是否不存在。例如,在某个自动配置类中定义一个方法来配置一个Bean:@Bean @ConditionalOnMissingBean public MyBean myBean() { return new MyBeanImpl(); }
- 这表示只有当容器中不存在
MyBean
类型的Bean时,才会创建并添加MyBeanImpl
这个Bean到容器中。
- 这表示只有当容器中不存在
- 这是自动装配的重要配置文件。在Spring Boot的各个依赖中,都可以包含
-
自动配置的过程
- Spring Boot应用启动时,首先会加载主应用类,由于
@SpringBootApplication
注解的存在,@Configuration
注解使得这个类被视为一个配置类,@ComponentScan
开始扫描组件。 - 同时,
@EnableAutoConfiguration
触发自动装配过程。AutoConfigurationImportSelector
从各个依赖的META - INF/spring.factories
文件中获取自动配置类列表。 - 然后,对于每个自动配置类,根据条件注解来检查是否满足应用条件。如果满足条件,就会将这个自动配置类加载到Spring容器中,自动配置类中的
@Bean
方法会被调用,创建和配置相应的Bean,从而完成自动装配的过程。
- Spring Boot应用启动时,首先会加载主应用类,由于
具体源码讲解
-
@SpringBootApplication
注解源码分析- 首先查看
@SpringBootApplication
注解的定义:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoExcludeFilter.class) }) public class SpringBootApplication { }
- 可以看到它是一个组合注解,包含了
@SpringBootConfiguration
、@EnableAutoConfiguration
和@ComponentScan
。 @SpringBootConfiguration
:它实际上就是@Configuration
注解,用于标识这个类是一个配置类。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public class SpringBootConfiguration { }
@EnableAutoConfiguration
:这是自动装配的关键。它的源码如下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public class EnableAutoConfiguration { }
- 其中
@Import(AutoConfigurationImportSelector.class)
是核心部分。这个AutoConfigurationImportSelector
类用于加载自动配置类。 @ComponentScan
:用于扫描组件,它有一些默认的扫描规则和可以自定义的过滤规则,如上述代码中的excludeFilters
,用于排除某些类型的组件扫描。
- 首先查看
-
AutoConfigurationImportSelector
源码分析AutoConfigurationImportSelector
实现了ImportSelector
接口,其中关键的方法是selectImports
:
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
- 这个方法首先检查自动配置是否启用。然后通过
AutoConfigurationMetadataLoader.loadMetadata
加载自动配置元数据,再通过getAutoConfigurationEntry
获取自动配置项。 - 深入
getAutoConfigurationEntry
方法:
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } 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 new AutoConfigurationEntry(configurations, exclusions); }
- 在这里,
getCandidateConfigurations
方法用于获取候选的自动配置类列表,它会从META - INF/spring.factories
文件中读取相关配置:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { 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; }
SpringFactoriesLoader.loadFactoryNames
方法会读取META - INF/spring.factories
文件,查找org.springframework.boot.autoconfigure.EnableAutoConfiguration
对应的配置类列表。
-
META - INF/spring.factories
文件读取源码分析SpringFactoriesLoader
类用于读取spring.factories
文件,关键方法是loadFactoryNames
:
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList()); }
- 它调用了
loadSpringFactories
方法:
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result!= null) { return result; } try { Enumeration<URL> urls = (classLoader!= null? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<Object, Object> 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); } }
- 这个方法首先检查缓存中是否已经有读取的结果。如果没有,就通过
ClassLoader
获取META - INF/spring.factories
文件的URL
,然后将其内容读取到Properties
对象中。最后,将文件中的配置解析出来,以key - value
的形式存储在MultiValueMap
中,其中key
是配置项的类型(如org.springframework.boot.autoconfigure.EnableAutoConfiguration
),value
是对应的配置类列表。
-
条件注解的源码体现(以
@ConditionalOnClass
为例)@ConditionalOnClass
注解用于判断某个类是否在类路径上。它的实现基于Condition
接口。- 当Spring容器在处理自动配置类时,会检查条件注解。
@ConditionalOnClass
对应的Condition
实现类是OnClassCondition
。在自动配置类加载过程中,会调用OnClassCondition
的matches
方法来判断条件是否满足:
@Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 省略部分代码 List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class); if (onClasses!= null) { List<String> missing = filter(onClasses, ClassNameFilter.MISSING, context); if (!missing.isEmpty()) { return false; } } List<String> onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class); if (onMissingClasses!= null) { List<String> present = filter(onMissingClasses, ClassNameFilter.PRESENT, context); if (!present.isEmpty()) { return false; } } return true; }
- 这个方法会获取
@ConditionalOnClass
注解中指定的类列表,然后检查这些类是否在类路径上。如果有任何一个指定的类不存在,就返回false
,表示条件不满足,自动配置类不会被加载。通过这样的机制,Spring Boot可以根据类的存在与否来决定自动配置类的加载与否,实现智能的自动装配。