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);
}
}