看这篇文章之前,一定要先看下SpringBoot Bean的注册与解析,并且还要记得解析Bean中关键的几个方法,文章地址:juejin.cn/post/689118…
在上一篇文章中我们自己写了一个starter,并且将它配置到springBoot项目中生效,那么现在问题来了,springBoot是怎么知道我们写了配置类的呢??又是什么时候被解析呢??
1:在Java中一切皆对象,那么配置类也是个对象,既然要解析我们的配置类,那么肯定是在解析Bean的时候扫描到我们的配置类,想来想去好像只有这一步能够去扫描我们的配置类并且解析了。那么我们现在回到解析Bean的源码中
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
throws IOException {
processImports(configClass, sourceClass, getImports(sourceClass), true);
return null;
}
在SpringBoot启动类的的@SpringBootApplication注解里面有一个@EnableAutoConfiguration注解,点进这个注解发现有一个
@Import(AutoConfigurationImportSelector.class)的注解
所以 processImports(configClass, sourceClass, getImports(sourceClass), true); 这个方法就会实例化
AutoConfigurationImportSelector.class这个类,凡是使用@Import注解实现的类都会被加入到ConfigurationClassParse类的
this.deferredImportSelectors的一个集合当中去,具体可以进入processImports方法中去有一个方法
this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);进去可以看到有一行
this.deferredImportSelectors.add(holder);
2:既然找到了这个类,那么肯定就有处理它的地方,我们会带ConfigurationClassParse 的 parse()方法
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}

3:进步一步处理
public void process() {
List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
this.deferredImportSelectors = null;
try {
if (deferredImports != null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
deferredImports.sort(DEFERRED_IMPORT_COMPARATOR);
deferredImports.forEach(handler::register);
handler.processGroupImports();
}
}
finally {
this.deferredImportSelectors = new ArrayList<>();
}
}
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(
entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to process import candidates for configuration class [" +
configurationClass.getMetadata().getClassName() + "]", ex);
}
});
}
}
public Iterable<Group.Entry> getImports() {
for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
this.group.process(deferredImport.getConfigurationClass().getMetadata(),
deferredImport.getImportSelector());
}
return this.group.selectImports();
}
@Override
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);
}
}
4:进入AutoConfigurationImportSelector类
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
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);
}
5:进入getCandidateConfigurations()方法
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;
}
6: 一直跟进 loadSpringFactories()这个方法
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader)
if (result != null) {
return result
}
try {
// public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"
// 这步是去指定文件去加载我们的配置类,现在知道为什么我们在自定义starter的时候最后一步就是要去创建这个文件
// 并且把我们的配置类的全路径配置进去,就是为了这一步,这样就可以通过反射拿到我们的配置类的实例了
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 factoryClassName = ((String) entry.getKey()).trim()
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim())
}
}
}
cache.put(classLoader, result)
return result
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex)
}
}
7:配置类拿到了,但是还需要进行过滤,因为我们在配置类上配置了@Condition相关的注解,@Condition注解就是必须满足某些条件才会被解析,下一篇文章我们可以自己实现@Conditon的用法。
8:结束语:我们自定义starter的用法以及SpringBoot如何解析我们的配置并让我们的配置生效已经结束了,建议从头到尾debug跟踪,这样才不会感觉迷茫