1. 概述
SpringBoot 的核心就是自动装配,他致力于“约定优于配置”的原则,它通过提供默认的配置来减少开发者的配置工作。这意味着,多数情况下,开发者可以快速启动一个Spring应用,而不需要进行复杂的配置。
2. 前置知识
3. SpringApplication.run()方法
每个 SpringBoot 服务的启动入口都是SpringApplication.run()
,那么它究竟是什么呢?我们来看代码
上面的代码,我们可以看到,已经到了 spring 源码层面了,而在 spring 源码中refresh()
方法里面包含了创建容器、创建 bean、实例化 bean、初始化bean以及 postprocessor 等一系列的操作。
由此可知,springboot 中的SpringApplication.run
方法底层调用的是 spring 的refresh()
方法。
4. @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 {
// ..........
}
@ComponentScan
前面我们介绍过了,是扫描路径的。
@SpringBootConfiguration
注解实质上就是@Configuration
注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
所以重点就是@EnableAutoConfiguration
注解了。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
由此可见使用了 Import 注解,我们继续看导入的类AutoConfigurationImportSelector
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
public Class<? extends DeferredImportSelector.Group> getImportGroup() {
return AutoConfigurationGroup.class;
}
}
实现了DeferredImportSelector接口,而DeferredImportSelector继承ImportSelector接口
分析至此,很多同学可能觉得,必是调用ImportSelector接口的selectImports方法完成的自动状态,其实不然,尽管AutoConfigurationImportSelector类确实有selectImports方法,但是,当我们 debug 的时候看到并没有进入selectImports方法,那么是在哪里调用的呢?别急,我们后面分析这块,这里留个标记!!!
public interface DeferredImportSelector extends ImportSelector {
@Nullable
default Class<? extends Group> getImportGroup() {
return null;
}
public interface Group {
void process(AnnotationMetadata metadata, DeferredImportSelector selector);
Iterable<Entry> selectImports();
public static class Entry {
private final AnnotationMetadata metadata;
private final String importClassName;
public Entry(AnnotationMetadata metadata, String importClassName) {
this.metadata = metadata;
this.importClassName = importClassName;
}
public AnnotationMetadata getMetadata() {
return this.metadata;
}
public String getImportClassName() {
return this.importClassName;
}
public boolean equals(@Nullable Object other) {
if (this == other) {
return true;
} else if (other != null && this.getClass() == other.getClass()) {
Entry entry = (Entry)other;
return this.metadata.equals(entry.metadata) && this.importClassName.equals(entry.importClassName);
} else {
return false;
}
}
public int hashCode() {
return this.metadata.hashCode() * 31 + this.importClassName.hashCode();
}
public String toString() {
return this.importClassName;
}
}
}
}
好,我们继续往下面分析代码
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
// 获取元注解中的属性
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
// 从 所有spring.factories文件获取需要导入的类的全路径名
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
// 去掉重复项
configurations = this.removeDuplicates(configurations);
// 获取需要排除的导入项:就是SpringBootApplication注解的exclude和excludeName属性值
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 过滤,检查候选配置类上的注解@ConditionalOnClass,如果要求的类不存在,则这个候选类会被过滤不被加载
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
configurations = this.getConfigurationClassFilter().filter(configurations); 根据spring-autoconfiguration-metadata.properties文件中的配置判断要导入的 Configuration 是否满足条件,若不满足,则不导入,通过这种条件过滤可以有效的减少@configuration类的数量从而降低SpringBoot的启动时间。
我们重点分析下this.getCandidateConfigurations(annotationMetadata, attributes);语句
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
Map<String, List<String>> result = new HashMap();
try {
// 加载所有的配置文件资源
Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Map.Entry<?, ?> entry = (Map.Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
现在我们梳理下上面遗留的问题:为什么没有走AutoConfigurationImportSelector类的selectImports方法?
问题的原因在于AutoConfigurationImportSelector实现的是DeferredImportSelector接口(Deferred:延迟),问题就出在这个接口上面,接下来我们看一下源码:
上面我们分析到了spring 中的代码 refresh 方法
以上只是将所有的延迟加载导入类添加到 list 中了,那么在哪里处理的呢?
上面的步骤往回退一退,我们可以看到
然后再看processGroupImports方法。
然后我们就回到了DeferredImportSelector接口中
进而我们回到最原始的AutoConfigurationImportSelector类,这个类中有个内部类AutoConfigurationGroup实现了DeferredImportSelector.Group接口
private static class AutoConfigurationGroup implements DeferredImportSelector.Group, BeanClassLoaderAware, BeanFactoryAware, ResourceLoaderAware {
//。。。。。。。。。。
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector, () -> {
return String.format("Only %s implementations are supported, got %s", AutoConfigurationImportSelector.class.getSimpleName(), deferredImportSelector.getClass().getName());
});
// 在这里调用的getAutoConfigurationEntry方法,正式进入自动装配逻辑
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector)deferredImportSelector).getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
Iterator var4 = autoConfigurationEntry.getConfigurations().iterator();
while(var4.hasNext()) {
String importClassName = (String)var4.next();
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
public Iterable<DeferredImportSelector.Group.Entry> selectImports() {
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
} else {
Set<String> allExclusions = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
Set<String> processedConfigurations = (Set)this.autoConfigurationEntries.stream().map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
return (Iterable)this.sortAutoConfigurations(processedConfigurations, this.getAutoConfigurationMetadata()).stream().map((importClassName) -> {
return new DeferredImportSelector.Group.Entry((AnnotationMetadata)this.entries.get(importClassName), importClassName);
}).collect(Collectors.toList());
}
}
}