SpringBoot的一个很重要的特性就是自动配置,使用注解,让服务不再需要xml等各种配置文件
然后使用main方法一键启动
@SpringBootApplication
@SpringBootApplication 注解是 SpringBoot 的源头,一切都要从该注解开始说起
标注在类上时,表示该类是 SpringBoot 的主配置类,并从该类中的 main 方法来启动 SpringBoot 应用
注解源码如下:
1 |
|
可以看出主要包含了三个注解:
@SpringBootConfiguration@ComponentScan@EnableAutoConfiguration
@SpringBootConfiguration
1 |
|
看 @SpringBootConfiguration 的源码发现其实就是一个 @Configuration 注解,表示这是一个 SpringBoot 的配置类
@ComponentScan
@ComponentScan 表示开启组件扫描
默认是扫描当前类下的package。将 @Controller/@Service/@Component/@Repository 等注解加载到IOC容器中
@EnableAutoConfiguration
@EnableAutoConfiguration 表示开启自动配置,SpringBoot 自动配置的原理都在这个注解中
1 |
|
@EnableAutoConfiguration 又主要包含 @Import、@AutoConfigurationPackage 俩个注解,先看下重点 @Import 这个注解
@Import
@Import 注解是用来导入配置类或者一些需要前置加载的类,可以将多个配置类融合成一个配置类
1 |
|
看代码可以看出,有三种使用方式:
- 导入普通 Java 类,导入的类会被加载为 Bean
- 导入
ImportSelector接口实现 - 导入
ImportBeanDefinitionRegistrar接口实现
ImportSelector
1
2
3
4
5
6
7
8
9public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}接口中只有一个
selectImports方法,用于返回全类名数组,会把返回数组中的类全部导入ImportBeanDefinitionRegistrar
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public interface ImportBeanDefinitionRegistrar {
/**
* Register bean definitions as necessary based on the given annotation metadata of
* the importing {@code @Configuration} class.
* <p>Note that {@link BeanDefinitionRegistryPostProcessor} types may <em>not</em> be
* registered here, due to lifecycle constraints related to {@code @Configuration}
* class processing.
* @param importingClassMetadata annotation metadata of the importing class
* @param registry current bean definition registry
*/
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}registerBeanDefinitions它可以手动注册bean到容器中
AutoConfigurationImportSelector.class
AutoConfigurationImportSelector.class 类实现了 ImportSelector 接口,其中的核心方法是 selectImports,来返回需要导入的组件的全类名数组
selectImports1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
}getCandidateConfigurations1
2
3
4
5
6
7
8
9
10
11
12
13
14protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
//获取EnableAutoConfiguration.class对应的全类名数组
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;
}loadFactoryNames1
2
3
4
5public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
//返回 META-INF/spring.factories 文件下 EnableAutoConfiguration.class 对应的所有值
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}loadSpringFactories1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32//从当前项目的类路径中获取所有 META-INF/spring.factories 这个文件下的信息。
private static Map<String, List<String>> loadSpringFactories( ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
//FACTORIES_RESOURCE_LOCATION:META-INF/spring.factories
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 factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.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
META-INF/spring.factories 在很多第三方依赖中都会有这个文件,第三方依赖通过这个文件并配置 EnableAutoConfiguration 参数,然后自动配置会把这个参数下面的所有类都配置到 Spring 容器中,如图

@AutoConfigurationPackage
@EnableAutoConfiguration 注解中的另一个注解 @AutoConfigurationPackage,是用来自动配置包的,将主配置类 @SpringBootConfiguration 标注的类,所在包及下面所有子包里面的所有组件扫描到 Spring 容器中
1 |
|
可以看到,AutoConfigurationPackage 注解依然使用了 import 注解,不过使用的是 ImportBeanDefinitionRegistrar 接口实现
1 | static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { |
总结
SpringBoot的自动装配是由 @EnableAutoConfiguration 注解来实现的,而 @EnableAutoConfiguration 注解又是通过 @AutoConfigurationPackage 和 @Import(AutoConfigurationImportSelector.class) 来实现服务自身的自动配置和第三方依赖的自动配置
注解关系如图:
