@Nullable protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // Recursively process any member (nested) classes first // 递归循环的解析内部类的配置类(因此,即使是内部类的配置类,我们Spring也是支持的,很强大有木有) processMemberClasses(configClass, sourceClass); // Process any @PropertySource annotations // 处理@PropertySources注解和@PropertySource注解,交给processPropertySource去解析 // 显然必须是ConfigurableEnvironment的环境采取解析,否则发出警告:会忽略这个不进行解析 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } // Process any @ComponentScan annotations // 解析@ComponentScans和@ComponentScan注解,进行包扫描。最终交给ComponentScanAnnotationParser#parse方法进行处理 Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if needed // 这一步非常重要:如果被扫描的Bean定义信息,还是属于@Configuration的配置组件,那就继续调用本类的parse方法,进行递归解析============== // 所以我们在进行包扫描的时候,也是会扫描到@Configuration并且进行解析的。。。 for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations // 这里是今天的主菜:解析@Import注解,后面详解processImports方法 processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations // 显然,先是处理了@Import,才过来解析@ImportResource的====最终交给environment.resolveRequiredPlaceholders(resource)去处理了 AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods // 处理被标注了@Bean注解的方法们 // 遍历@Bean注释的方法,添加到configClass中的BeanMethod // 这里需要注意的是:最终会实例化的时候是执行此工厂方法来获取到对应实例的 // if (mbd.getFactoryMethodName() != null) { ... } 这里会是true,从而执行此方法内部逻辑。 原理同XML中的FactoryMethod方式创建Bean Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process default methods on interfaces // 这个特别有意思:处理接口中被@Bean注解默认方法,代码如下 // 因为JDK8以后接口可以写default方法了,所以接口竟然也能给容器里注册Bean了 // 但是需要注意:这里的意思并不是说你写个接口然后标注上@Configuration注解,然后@Bean注入就可以了 // 这个解析的意思是我们的配置类可以实现接口,然后在所实现的接口里面若有@Bean的注解默认方法,是会加入到容器的 processInterfaces(configClass, sourceClass); // Process superclass, if any // 如果有父类的话,则返回父类进行进一步的解析,否则返回null // 这个也是很厉害的,如果有父类,也是能够继续解析的。@EnableWebMvc中的DelegatingWebMvcConfiguration就是这么玩的 // 它自己标注了@Configuration注解,但是真正@Bean注入,都是它父类去干的 if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse // 若找到了父类,会返回然后继续处理 return sourceClass.getSuperClass(); } } // No superclass -> processing is complete // 没有父类,就停止了,处理结束 return null; }
因此本文,就重点来看看processImports这个方法,如下:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) { // 相当于没有找到@Import注解,那就不处理了 // 说明:获取@Import是递归获取,任意子类父类上标注有都行的 if (importCandidates.isEmpty()) { return; } //循环依赖检查:如果存在循环依赖的话,则直接抛出异常(比如你@Import我,我@Import你这种情况) if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { // 依次处理每个@Import里面候选的Bean们 for (SourceClass candidate : importCandidates) { // 分之一:如果实现了ImportSelector接口(又分为两种,因为有子接口DeferredImportSelector呢) if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); // 根据空的构造函数,把这个Bean实例化出来, ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); // 这里面注入了一下感知接口的元素,包括environment、resourceLoader、registry等等(实现了DeferredImportSelector也在此处注入了哦) ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry); // 判断是否是DeferredImportSelectorHolder的子类,是的话先加入进入 不处理先 if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) { this.deferredImportSelectors.add( new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector)); } // 否则立马调用它的`selectImports`方法,拿到一个BeanName的数组 else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); // 这里面高级了:因为我们这里放进去的Bean,有可能是普通Bean,当然也还有可能是实现了ImportSelector等等接口的,因此此处继续调用processImports进行处理,递归的效果~~~~ processImports(configClass, currentSourceClass, importSourceClasses, false); } } //如果实现了ImportBeanDefinitionRegistrar这个接口的 else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // Candidate class is an ImportBeanDefinitionRegistrar -> // delegate to it to register additional bean definitions Class<?> candidateClass = candidate.loadClass(); ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class); ParserStrategyUtils.invokeAwareMethods( registrar, this.environment, this.resourceLoader, this.registry); // 完成了实例化后和Aware方法后,添加进configClass类的属性importBeanDefinitionRegistrars里先缓存着(至于执行时机,留给下面讲吧) configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as an @Configuration class // 什么都接口都没有实现,那就是普通的配置类嘛,那就直接交给processConfigurationClass()去处理了 // 备注:这个方法的处理流程,请参照上面哦 // 这里面有个特别重要的地方:是candidate.asConfigClass(configClass)这一句,给包装陈一个ConfigurationClass去处理 // 因为传入了configClass属于它的importedBy属性,这样一来ConfigurationClass#isImported()就返回true了,表面这个Bean是被单纯的、单纯的、单纯的的导入进来的 this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass)); } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } finally { // 上面push,下面pop出来 this.importStack.pop(); } } }
从上面的源码处理过程,我们可以很清楚的知道了ImportSelector#selectImports执行时机,然后并且把DeferredImportSelector和ImportBeanDefinitionRegistrar都先装起来了。
doProcessConfigurationClass执行完成之后,processConfigurationClass也就执行完了,接下来就开始执行顶层parse方法内部的:processDeferredImportSelectors():
附录上DeferredImportSelector的源码:(Spring4和Spring4差异很大) 本文都是基于Spring5进行讲解的
// Spring4的源码,啥都木有 public interface DeferredImportSelector extends ImportSelector { } // Sparing5的源码,加了不少东西 public interface DeferredImportSelector extends ImportSelector { @Nullable default Class<? extends Group> getImportGroup() { return null; } // 内部接口 interface Group { void process(AnnotationMetadata metadata, DeferredImportSelector selector); Iterable<Entry> selectImports(); // 内部的内部类 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; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Entry entry = (Entry) o; return Objects.equals(this.metadata, entry.metadata) && Objects.equals(this.importClassName, entry.importClassName); } @Override public int hashCode() { return Objects.hash(this.metadata, this.importClassName); } } } }
源码的差异很大,就就造成了processDeferredImportSelectors
的处理方式不尽相同。同样的,本文就以Spring5的源码进行讲解了:
private void processDeferredImportSelectors() { List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; if (deferredImports == null) { return; } // 排序:注意这个比较器。它是按照PriorityOrdered、Ordered等进行优先级排序的 // 因此我们可以看到一大特性:DeferredImportSelector是支持Order排序的 deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); // 这个Map厉害了,key竟然是Object。。。 Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>(); Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>(); // 对这些个DeferredImportSelector一个个处理吧 //遍历DeferredImportSelector接口集合,获取Group集合类,默认为DefaultDeferredImportSelectorGroup for (DeferredImportSelectorHolder deferredImport : deferredImports) { // getImportGroup()方法是DeferredImportSelector接口的default方法,若不复写,默认return null // 该接口的作用是:子类可以对一些Import的类进行分类 //Group 为DeferredImportSelector的一个内部接口~~~~~~~~~~~ Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup(); // 按照group 或者 deferredImport 进行分组 DeferredImportSelectorGrouping grouping = groupings.computeIfAbsent((group == null ? deferredImport : group), (key) -> new DeferredImportSelectorGrouping(createGroup(group))); grouping.add(deferredImport); configurationClasses.put(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getConfigurationClass()); } //遍历Group集合,作用也是调用processImport()方法用于解析@Import for (DeferredImportSelectorGrouping grouping : groupings.values()) { grouping.getImports().forEach((entry) -> { ConfigurationClass configurationClass = 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); } }); }
DeferredImportSelector接口在Spring-core/Context中没有实现类。但是在Spring Boot的自动配置中有大量的实现。那么我们将在讲解Spring Boot源码分析的时候,会再回来重点讲解。也看看Spring5为何还要对此接口继续提升