1. MAVEN引用
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath />
</parent>
<groupId>com.example</groupId>
<artifactId>easy-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>easy-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
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 {
}
可以看到@SpringBootApplication注解本身是个组合注解,与自动配置相关的是@EnableAutoConfiguration注解,部分源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
需要关注@EnableAutoConfiguration注解的@Import注解,用来将导入并注入Spring容器成为Bean对象。源码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class<?>[] value();
}
@Import注解支持导入3中类型的类:@Configuration注解声明的配置类,ImportSelector接口实现类,ImportBeanDefinitionRegistrar接口的子类,具体用法可以参考这篇博客:spring注解之@Import注解的三种使用方式
回到正题,@EnableAutoConfiguration注解上声明的@Import采用的是ImportSelector接口实现类的方式,下面是AutoConfigurationImportSelector类的源码中的继承关系:
public class AutoConfigurationImportSelector
implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware,
BeanFactoryAware, EnvironmentAware, Ordered {
}
我们需要关注ImportSelector接口相关的代码实现,所以先看ImportSelector接口的部分源码:
public interface DeferredImportSelector extends ImportSelector {
/**
* Return a specific import group or {@code null} if no grouping is required.
* @return the import group class or {@code null}
*/
@Nullable
default Class<? extends Group> getImportGroup() {
return null;
}
}
AutoConfigurationImportSelector类中DeferredImportSelector接口相关的代码实现:
@Override
public Class<? extends Group> getImportGroup() {
return AutoConfigurationGroup.class;
}
再来看看DeferredImportSelector接口的父接口ImportSelector接口源码:
public 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);
}
AutoConfigurationImportSelector类中ImportSelector接口相关的代码实现:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) { //判断是否启用自动配置
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader); //加载META-INF/spring-autoconfigure-metadata.properties文件
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);//加载META-INF/spring.factories文件
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(annotationMetadata,attributes);这行代码,它负责加载META-INF/spring.factories文件。
源码如下:
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;
}
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()) {
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}