@ComponentScan:扫描组件
@SpringBootConfiguration下的@Configuration,表明这个类是一个配置文件,功能等同于在spring中的Application.xml文件。
重点看@EnableAutoConfiguration,进入@AutoConfigurationPackage,发现这里导入了一个注册类,@Import(AutoConfigurationPackages.Registrar.class),将主配置类 (即@SpringBootApplication标注的类)的所在包及包下面所有子包中的所有组件扫描到Spring容器 。
回到@EnableAutoConfiguration,导入了类@Import(AutoConfigurationImportSelector.class),点进这个类。
其中有方法getCandidateConfigurations,即获取候选配置。
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;
}
其中的getSpringFactoriesLoaderFactoryClass方法,返回值就是EnableAutoConfiguration。
再看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<?, ?> 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.factorie文件。(FACTORIES_RESOURCE_LOCATION指向META-INF/spring.factorie)
找到spring-boot-autoconfigure包下的META-INF下的spring.factories文件,
里面有许多xxxAutoConfiguration路径。
以org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration为例,点进去,
里面有如下注解:
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ServerProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
@Configuration:表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件。
@EnableConfigurationProperties(xxx.class):启动指定类的ConfigurationProperties。这里指定启动ServerProperties.class,我们进入这个类,我们发现他类似javabean对象,里面只有变量及其get/set方法,但是我们发现他有个注解,即@ConfigurationProperties(prefix = “server”, ignoreUnknownFields = true),这个注解的意思就是找到配置文件与本对象相的同字段的属性值注入进来。即把resources目录下的application.properties中与prefix指定值一样的且本对象中存在的属性注入进来到本对象中。
剩下几个注解,是根据当前不同的条件判断,决定这个配置类是否生效:
@Conditional扩展注解作用 | 判断是否满足指定条件 |
---|---|
@ConditionalOnBean | 容器中存在指定Bean |
@ConditionalOnMissingBean | 容器中不存在指定Bean |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是web环境 |
必须是@Conditional指定的条件成立,才给容器中添加组件,配置类里面的所有内容才生效。
总结:
SpringBoot自动装配其实就找到xxxAutoConfiguration类,xxxAutoConfiguration其实就是一个配置类,他把需要的类都加载到容器中,然后通过@EnableConfigurationProperties(xxxProperties.class)获得配置属性需要的值,即进入这个类并通过@ConfigurationProperties(prefix = “xxx”)来把配置文件中的配置导入进来。