1.首先看下启动类:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
启动类使用@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 {
再进入@EnableAutoConfiguration
中查看
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
发现加载了一个AutoConfigurationImportSelector.class
类
温馨提示
:所有实现ImportSelector的类,都会在启动时被ConfigurationClassParser
中的processImports
进行实例化,并执行selectImports
方法。
2.查看AutoConfigurationImportSelector
中的selectImports
@Override
public String[]selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
通过方法可知是为了选出想要加载的import类,而如何获取这些类呢?其实是通过一个SpringFactoriesLoader去加载对应的spring.factories
下面展示如何和此类建立关系。
我们进入selectImports
方法中的getCandidateConfigurations
protected ListgetCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List 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;
}
仔细看SpringFactoriesLoader.loadFactoryNames
所需的参数,里面有个getSpringFactoriesLoaderFactoryClass()
。下面看下这个方法的返回值是什么?
protected Class> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class; //返回的是一个接口
}
可以看出返回的是一个EnableAutoConfiguration.class
,接下来SpringFactoriesLoader会根据这个interface
去所有spring.factories
找EnableAutoConfiguration.class
所对应的values
,并返回。
3. SpringFactoriesLoader如何加载
此类中只有三个方法,我们使用了其中二个
- loadFactoryNames
public static ListloadFactoryNames(Class factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
- loadSpringFactories
private static Map>loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap result =cache.get(classLoader);
if (result !=null) {
return result;
}
try {
Enumeration 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 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);
}
}
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :就是去加载对应的文件。