这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战」
前言
在前面我们解析了:
- @Import注解的引入是如何实现的:
- @Import注解将指定的ImportBDRegistrar接口存放到缓存中。
- 在调用到某个位置的时候,调用这些importRegistrar的registrarBD方法,将这些实现类中指定的BD实例化并注入到context中。
但这并没有解决我们的问题:
- 自动注入到底是咋实现的?
虽然根据之前的@Import注解的分析,大部分自动注入都可以通过在**@EnableXXX注解里import对应的ImportBDRegistrar**来做到对应的方式,然而看看这个:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface EnableEurekaClient
这里属于啥都没有,这种又是怎么实现的呢?
AutoConfigurationImportSelector
还记得之前在EnableAutoConfiguration中的这个东西吗?
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
回顾一下在processImport方法中,importSelector是如何解析的:
graph LR
getImports获取Import写的内容-->一堆import的SourceClass-->实例化importSelector
实例化importSelector-->importSelector.selectImport-->getImports获取Import写的内容
如果是deferredImportSelector,那么前面的有失偏颇,处理流程实际上是:
graph LR
getImports获取Import写的内容-->一堆import的SourceClass-->实例化deferredImportSelector
实例化deferredImportSelector-->保存对应的DeferredImportSelectorGrouping并等待调用
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
if (this.deferredImportSelectors == null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
handler.processGroupImports();
}
else {
this.deferredImportSelectors.add(holder);
}
}
public void register(DeferredImportSelectorHolder deferredImport) {
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
grouping.add(deferredImport);
this.configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getConfigurationClass());
}
//而这些grouping的处理,在这里:
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
Predicate<String> exclusionFilter = grouping.getCandidateFilter();
//注意看这里
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter),
Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)),
exclusionFilter, false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
//这个getImports,不是importSelectors的,而是group的:
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
//【A】
return this.group.selectImports();
}
那么我们就来看看这个类里对应的group的process:
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
对应debug,打完断点发现在这一行拿到了很多autoConfigurationEntry:
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
其中就包括了我们之前提到的eureka的大部分Configuration。
根据这边往上看就知道,这些配置类会被放到processImport方法中执行,这样子和之前的自动装配[1.1],[1.2],[1.3]就对上了:
//【A】的地方调用了!
public Iterable<Entry> selectImports() {
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
}
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
看到这里对整个流程就更清楚一点了:
- 我们可以通过@Import的方式,手动注入一些BDRegistrar。
- 也可以借助这里的AutoConfigurationImportSelector.AutoConfigurationGroup的这个process方法中的getAutoConfigurationEntry,附带到ConfigurationClassParser中,来做注入的工作。
通过断点可以看到,采取第二种方法的中间件要更多一些,说明这里就是SpringBoot自动注入的关键。那么我们就看看这个方法:
getAutoConfigurationEntry
这个方法仍在AutoConfigurationImportSelector中
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//这里是获取annotationMetadata中,@AutoConfiguration中的注解
//一般来说我们如果没有手动添加这个注解,这里拿出来的是两个String[0]
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);
}
//我们能获取到一大堆配置的关键
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<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
//通过debug进来,发现这里result并不是空,并且包含了我们所需的所有configuration
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
//关键就在这里,会根据classLoader,去解析对应包中,META-INF/spring.factories中的配置信息,并装载到缓存中
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 factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
//而这个缓存,仅在这里做put操作
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
- 这里的annotationMetadata指的是启动类上加的注解。
在Spring.factories中可以看到对应的信息,这下就清楚了:
//open-feign-core包中
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration,\
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration,\
org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration
也就是说,最终还是通过这里来装载的。这下,自动装配的原理就清楚了:其实就是让Spring和中间件,来帮助我们把对应的Configuration注入到包中。
不妨来看看,这些自动注入的都是些什么东西,可以发i西安就是一些中间件中底层的bean。
总结
看到这里就大概明白,自动装配是怎么做的了,流程图大致如下:
www.processon.com/diagraming/…
看到这里也可以对之前的结论做一个补充:
- 显式的@Import(ImportBDRegistrar),一般是用于像feign一样需要对业务代码中的接口做代理的(例如:mybatis,openFeign),在这里会通过这些包中指定的规则,来生成对应的代理类。
- 如果不是@Import的,一般就是导入一些AutoConfiguration,这些大部分在对应中间件包中以AutoConfiguration作为后缀,并且必须在对应包中的META-INF/spring.factories声明。
- 这部分的内容,依靠AutoConfigurationImportSelector来导入到容器中。
这里还有一些遗留的问题:
- ClassLoader,是如何装载类的,Spring中呢?
- 这些自动装配的类,是在哪个节点上被写入的?