本文基于 SpringBoot 2.3.4.RELEASE 版本进行分析
1、启动类
@SpringBootApplication
public class SpringbootTestApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootTestApplication.class, args);
}
}
该类主要为项目启动提供入口,通过 main()
即可启动整个 SpringBoot 项目
2、@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 = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
该注解看似有那么多其他注解修饰,实际上我们只需要重点关注 @SpringBootConfiguration
、@EnableAutoConfiguration
、@ComponentScan
这个三个注解即可
-
@SpringBootConfiguration
- 继承自
@Configuration
注解,作用与其一致,用于标记当前类为配置类,相当于 XML 配置中的<beans></beans>
- 继承自
-
@EnableAutoConfiguration
- 开启自动配置,将所有符合自动配置条件的 Bean 对象加载进 IoC 容器中
-
@ComponentScan
- 开启组件扫描,默认扫描当前类所在的包及其子包中的所有 Bean 对象,相当于 XML 配置中的
<context:component-scan></<context:component-scan>
- 开启组件扫描,默认扫描当前类所在的包及其子包中的所有 Bean 对象,相当于 XML 配置中的
3、@EnableAutoConfiguration 注解
这里我们需要重点关注 @EnableAutoConfiguration
这个注解,因为 SpringBoot 就是通过它来开启自动配置
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
在该注解中,我们需要关注 @AutoConfigurationPackage
和 @Import
这两个注解
4、@AutoConfigurationPackage 注解
先来看 @AutoConfigurationPackage
注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
该注解主要通过 @Import
注解来往 Spring 容器中注入 Registrar 组件
5、Registrar 类
Registrar 类,是 AutoConfigurationPackages 类的一个静态内部类
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
/*
* new PackageImports(metadata)
* .getPackageNames().toArray(new String[0])
*
* 项目包根路径,即 root package
*/
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
在该类中,我们需要关注的是 registerBeanDefinitions()
方法,该方法用于将 root package 注册进 Spring 容器中,供后续使用
6、@Import 注解
再来看 @Import 注解,该注解用于往 Spring 容器中注入 AutoConfigurationImportSelector 组件
7、AutoConfigurationImportSelector 类
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
...
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 获取注解属性,并将其封装为 AnnotationAttributes 对象
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 获取候选配置列表 (即配置类的全限定类名集合)
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去除重复配置类
configurations = removeDuplicates(configurations);
// 根据注解中的 exclude 属性获取不需要的配置类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
// 进一步确认不需要的配置类
checkExcludedClasses(configurations, exclusions);
// 删除不需要的配置类
configurations.removeAll(exclusions);
// 进一步过滤不需要的配置类
configurations = getConfigurationClassFilter().filter(configurations);
// 触发自动配置导入时间
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
...
}
在该类中,我们需要关注的是 selectImports()
方法,该方法主要用于获取需要注入 Spring 容器的组件
7.1 getCandidateConfigurations() 方法
在 selectImports()
方法中,我们需要重点关注 getCandidateConfigurations()
方法
// 获取候选配置列表 (即配置类的全限定类名集合)
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
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;
}
该方法将具体实现委托给了 loadFactoryNames()
方法
7.2 loadFactoryNames() 方法
我们再来看 loadFactoryNames()
方法
public static final String FACTORIES_RESOURCE_LOCATION = "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 {
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<?, ?> 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);
}
}
该方法主要用于加载 META-INF/spring.factories
文件,并根据参数的不同获取不同配置类的全限定类名
举例说明:如果 loadFactoryNames()
方法中的参数为 ApplicationContextInitializer.class
,则获取的就是 ApplicationContextInitializer 接口所有配置类的全限定类名
8、归纳总结
SpringBoot 自动配置流程如下
-
启动 SpringBoot 项目
-
@SpringBoot 注解起作用
-
@EnableAutoConfiguration 注解起作用
-
@AutoConfigurationPackage 注解起作用,该注解通过其子注解 @Import(AutoConfigurationPackages.Registrar.class) 将 Registrar 组件注册进 Spring 容器中,Registrar 类通过其
registerBeanDefinitions()
方法,将 root package 注册进 Spring 容器中,供后续使用 -
@Import(AutoConfigurationImportSelector.class) 注解起作用,将 AutoConfigurationImportSelector 组件注册进 Spring 容器中,AutoConfigurationImportSelector 类通过其
selectImports()
方法,获取需要注入 Spring 容器的组件,再将其交给类加载器进行加载