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
,来返回需要导入的组件的全类名数组
selectImports
1
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);
}getCandidateConfigurations
1
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;
}loadFactoryNames
1
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());
}loadSpringFactories
1
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)
来实现服务自身的自动配置和第三方依赖的自动配置
注解关系如图: