SpringBoot自动配置分析
@SpringBootApplication注解,这是SpringBoot应用运行开始的地方。如果去掉该注解,程序无法运行。点进该注解看看有什么东西。按住ctrt鼠标左键单击@SpringBootApplication。
先来看看@EnableAutoConfiguratiion注解。顾名思义启用自动配置,这正是我们想要的东西。继续点进去看。
又出现了一大堆的注解,不要紧我们只看我们想要的。@AutoConfigurationPackage 直译过来是自动配置包。这是个什么玩意?点进去看源码。
@Import注解导入资源,可以看到导入的是Registrar这个内部类的字节码。点进Registrar这个内部类看看源码。
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
目光停留在getPackageName()上,获取包名?打个断点,debug一下。
这样看不是很明白,结合我的项目的目录树看看就明白了。
原来所谓的包扫描就是把springboot应用的启动类所在的包下所有的类扫描一遍。遇到@bean之类的就把它加入到容器中。
再看看@EnableAutoconfigura注解里的@Import(AutoConfigurationImportSelector.class)。直译过来自动导入选择器。点进这个类看看。
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);
}
这个方法名叫获取自动配置实体,打几个断点debug一下。
可以看到configurations保存了一大堆自动配置类的名称。那么这些东西是从哪里来的呢?继续翻这个类的源码。可以看到confiurations通过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(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());这个方法。这个方法牵扯到两个方法。
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, 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 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);
}
}
目光再次停留在FACTORIES_RESOURCE_LOCATION这个常量上。点进去看看到底是什么。
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
结合上面的代码,原来loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());的作用是扫描所有jar包下的"META-INF/spring.factories"文件。所谓spring.factories文件就是一系列的键值对。读取每一个spring.factories把键值存放在Properties对象中。