概述
- 在第一篇文章:SpringBoot学习(五):自动配置的源码实现(一)@EnableAutoConfiguration详解中已经介绍过@EnableAutoConfiguration注解的设计,而Spring容器对自动化配置的加载是基于@EnableAutoConfiguration注解的这个设计来实现的,接下来具体分析该过程
1. 应用程序添加@EnableAutoConfiguration注解
-
在应用程序主类(即SpringApplication.run方法被调用的main方法所在的类)添加使用@SpringBootApplication注解,该注解包含@EnableAutoConfiguration注解或者也可以直接使用@EnableAutoConfigure注解。@EnableAutoConfigure通过@Import注解来导入AutoConfigurationImportSelector;如下为一个应用代码配置示例:
@SpringBootApplication public class MyApplication { public static void main(String[] args) { SpringApplication application = new SpringApplication(MyApplication.class); // ... customize application settings here application.run(args); } }
2. ConfigurationClassPostProcessor处理@EnableAutoConfiguration注解
ConfigurationClassPostProcessor的调用
- BeanFactory的创建:Spring框架调用ApplicationContext.refresh方法启动Spring容器:先会创建BeanFactory,扫描包,获取使用了@Configuration或者其他配置性质的注解,如@SpringBootApplication,@Import等注解,的类,生成BeanDefinition并注册到BeanFactory中;
-
BeanFactoryPostProcessor执行:BeanFactory后置处理器对BeanFactory中的BeanDefinition进行加工,如注解处理。对于@Configuration等属于配置性质的注解的处理,则是交给ConfigurationClassPostProcessor来完成:
-
ConfigurationClassPostProcessor为BeanFactoryPostProcessor接口的实现类:从BeanFactory获取所有创建好的BeanDefinition列表,遍历该列表并筛选出所在类使用了@Configuration,@Import等配置性质注解的BeanDefintion
-
使用ConfigurationClassParser的parse方法处理该类的注解,其中会创建一个类型为ConfigurationClass的对象configClass来包装当前这个配置类(应用主类就是一个配置类),使用configClass作为参数,调用doProcessConfigurationClass方法来处理该类包含的注解。其中@Import注解的处理主要在processImports方法定义。
/** * Apply processing and build a complete {@link ConfigurationClass} by reading the * annotations, members and methods from the source class. This method can be called * multiple times as relevant sources are discovered. * @param configClass the configuration class being build * @param sourceClass a source class * @return the superclass, or {@code null} if none found or previously processed */ @Nullable protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { if (configClass.getMetadata().isAnnotated(Component.class.getName())) { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); } // Process any @PropertySource annotations ... // Process any @ComponentScan annotations ... // Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations ... // 注意@Bean注解的方法的处理, // 这里针对每个方法产生MethodMetadata对象, // 将该对象放到configClass对象的beanMethods集合中 // 之后在使用ConfigurationClassBeanDefinitionReader来生成bean对象。 // Process individual @Bean methods ... // Process default methods on interfaces ... // Process superclass, if any ... // No superclass -> processing is complete return null; }
-
@Import(AutoConfigurationImportSelector.class)注解的处理
-
@EnableAutoConfiguration内部包含@Import(AutoConfigurationImportSelector.class)注解,AutoConfigurationImportSelector为DeferredImportSelector接口的实现类,会在BeanFactory对所有BeanDefinition处理后执行来进行SpringBoot自动配置类的加载、导入,并基于@Conditional条件化配置来决定是否将该配置类内部定义的Bean注册到Spring容器。
-
@Import注解需要通过ConfigurationClassParser的processImports方法来处理。processImports方法的实现如下:
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection<SourceClass> importCandidates, boolean checkForCircularImports) { if (importCandidates.isEmpty()) { return; } if (checkForCircularImports && isChainedImportOnStack(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack)); } else { this.importStack.push(configClass); try { for (SourceClass candidate : importCandidates) { // 处理ImportSelector接口的实现类 // @EnableAutoConfiguration注解对应的AutoConfigurationImportSelector就是在这里处理。 if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = candidate.loadClass(); ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); ParserStrategyUtils.invokeAwareMethods( selector, this.environment, this.resourceLoader, this.registry); // DeferredImportSelector:延迟导入 if (selector instanceof DeferredImportSelector) { // 延迟导入selector // 通过handler方法, // 存到DeferredImportSelectorGrouping这个组中, // 即Group接口的实现类, // 在parse方法处理完所有BeanDefintions后,再处理 this.deferredImportSelectorHandler.handle( configClass, (DeferredImportSelector) selector); } else { String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames); 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); configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } // 正常使用@Configuration注解的配置类的处理 else { // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as an @Configuration class this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass)); } } } ... } }
-
对于@Import注解导入的类,分三种情况进行处理:
-
实现了ImportSelector接口的类,其中AutoConfigurationImportSelector类的处理属于这种类型。
-
由源码可知,由于AutoConfigurationImportSelector为DeferredImportSelector接口的实现类,故将AutoConfigurationImportSelector放到ConfigurationClassParser类的成员属性deferredImportSelectorHandler内部的deferredImportSelectors集合中。
-
在ConfigurationClassParser在parse方法中,对所有BeanDefinitions处理完之后,再遍历deferredImportSelectorHandler的deferredImportSelectors集合,通过deferredImportSelector来获取SpringBoot自动配置类列表并处理;
-
- 实现了ImportBeanDefinitionRegistrar接口的类,如在mybatis框架中@MapperScan的注解处理器MapperScannerRegistrar就是实现了ImportBeanDefinitionRegistrar接口;
- 使用@Configuration注解的类,其中应用代码的配置类和AutoConfigurationImportSelector导入的每个自动配置类都是在这里处理,其处理逻辑主要在processConfigurationClass方法定义:
-
ConfigurationClass类型对象的创建:创建一个类型为ConfigurationClass对象configurationClass来包装该配置类,其中ConfigurationClass的设计目的为:通过统一的方式来封装使用了@Configuration注解的类,从而方便内部对使用了@Configuration注解的类的处理;
-
配置类的注解处理:将该configurationClass对象作为processConfigurationClass方法的参数,调用processConfigurationClass方法来处理该配置类所包含的注解:如@PropertySource,@ComponentScan,@Import(此处递归调用processImports)等,内部@Bean注解的方法生成beanMethod对象并放到configurationClass的beanMethods集合。并且将该configurationClass对象放到ConfigurationClassParser的成员属性configurationClasses集合,这样这个集合就包含了应用中所有的使用@Configuration注解的类对应的ConfigurationClass类型的对象;
-
@Bean方法对象创建:在ConfigurationClassPostProcessor中,将ConfigurationClassParser的configurationClasses集合交给ConfigurationClassBeanDefinitionReader,ConfigurationClassBeanDefinitionReader负责遍历configurationClasses集合,对每个configurationClass,遍历configurationClass的beanMethods集合,即针对每个@Bean注解的方法,生成对应的BeanDefinition注册到Spring容器。
-
3. AutoConfigurationImportSelector:自动配置类的导入
-
由2的分析可知,AutoConfigurationImportSelector添加到了ConfigurationClassParser的成员属性deferredImportSelectorHandler的deferredImportSelectors集合中,然后在ConfigurationClassParser的parse方法处理完其他BeanDefinitions后调用,具体调用deferredImportSelectorHandler.process方法,如下:
public void parse(Set<BeanDefinitionHolder> configCandidates) { // 处理其他使用@Configuration注解的类 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); } } // 延迟执行deferredImportSelector定义的类的处理 this.deferredImportSelectorHandler.process(); }
-
DeferredImportSelectorHandler:ConfigurationClassParser的一个内部私有类,该类内部维护了一个类型为DeferredImportSelectorHolder的deferredImportSelectors列表。在SpringBoot中该deferredImportSelectors列表大小其实就是1,即包含一个AutoConfigurationImportSelector对象。
DeferredImportSelectorHandler的process方法:自动配置类导入的内部实现
1. 功能设计
-
通过DeferredImportSelector从spring-boot-autoconfigure包的META-INF/spring.factories文件中获取EnableAutoConfiguration作为key,对应的自动配置类列表,如下:
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ ... org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ ...
在spring-boot-autoconfigure项目对应的RedisAutoConfiguration如下:
@Configuration @ConditionalOnClass(RedisOperations.class) @EnableConfigurationProperties(RedisProperties.class) @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { @Bean @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<Object, Object> redisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean public StringRedisTemplate stringRedisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; } }
-
然后遍历类名列表,加载、导入对应的配置类,相当于在应用中添加了使用@Configuration注解的配置类,然后创建Configuration类型的对象configurationClass来包装该配置类,最后将configurationClass对象交给ConfigurationClassParser的processImports方法来进行递归调用。
-
processImports方法的作用如前文所分析:对于配置类(即不是ImportSelector接口实现类和ImportBeanDefinitionRegistrar接口实现类),调用processConfigurationClass方法处理该自动配置类上面的其他注解,并将该自动配置类内部使用@Bean注解的方法,条件化生成bean注册到Spring容器,从而可以提供特定功能组件的默认实现,实现SpringBoot的自动配置功能,即应用代码可以直接通过@Autowried注入某个功能组件,而不需要显示配置。
2. 实现
(1)DeferredImportSelectorHandler:DeferredImportSelector处理器,是使用DeferredImportSelector完成自动配置类导入的功能主类
- DeferredImportSelectorHandler的类定义如下:
private class DeferredImportSelectorHandler { @Nullable private List<DeferredImportSelectorHolder> deferredImportSelectors = new ArrayList<>(); ... 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集合并注册到DeferredImportSelectorGroupingHandler表示的组中 deferredImports.forEach(handler::register); // 具体为通过DeferredImportSelectorGrouping来汇总这些deferredImports handler.processGroupImports(); } } finally { this.deferredImportSelectors = new ArrayList<>(); } } }
-
主要通过DeferredImportSelectorGroupingHandler来对deferredImportSelectors集合进行包装,表示deferredImportSelectors集合的deferredImportSelector属于同一个组,然后通过processGroupImports方法来完成以上所述的功能设计。
(2)DeferredImportSelector的分组Group的体系结构设计:自底向上分析
-
DefaultDeferredImportSelectorGroup:同一组的自动配置类相关信息的汇总,实际加载的执行器。
private static class DefaultDeferredImportSelectorGroup implements Group { private final List<Entry> imports = new ArrayList<>(); // 实际执行自动配置类信息加载 // selector.selectImports返回自动配置类的类全限定名列表 @Override public void process(AnnotationMetadata metadata, DeferredImportSelector selector) { for (String importClassName : selector.selectImports(metadata)) { this.imports.add(new Entry(metadata, importClassName)); } } @Override public Iterable<Entry> selectImports() { return this.imports; } } class Entry { private final AnnotationMetadata metadata; private final String importClassName; ... }
-
DeferredImportSelector.Group接口实现,包含一个Entry列表,每个Entry对应一个自动配置类,包含配置类名importClassName和注解信息metadata;
-
process方法:调用给定的DeferredImportSelector的selectImports方法,从META-INF/spring.factories获取key为EnableAutoConfiguration对应的自动配置类(类全限定名),对每个自动配置类,创建对应的Entry实例,然后保存到imports集合。
-
selectImports的实现:SpringBoot提供的DeferredImportSelector接口的实现类AutoConfigurationImportSelector,对selectImports方法的实现:
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); // 核心实现在getAutoConfigurationEntry AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry( autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } /** * Return the {@link AutoConfigurationEntry} based on the {@link AnnotationMetadata} * of the importing {@link Configuration @Configuration} class. * @param autoConfigurationMetadata the auto-configuration metadata * @param annotationMetadata the annotation metadata of the configuration class * @return the auto-configurations that should be imported */ protected AutoConfigurationEntry getAutoConfigurationEntry( AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); // getCandidateConfigurations: // 这里完成从META-INF/spring.factories文件获取配置类列表 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); }
getCandidateConfigurations方法的实现:使用SpringFactoriesLoader.loadFactoryNames方法,从META-INF/spring.factories文件获取key为EnableAutoConfiguration对应的配置类列表:
/** * Return the auto-configuration class names that should be considered. By default * this method will load candidates using {@link SpringFactoriesLoader} with * {@link #getSpringFactoriesLoaderFactoryClass()}. * @param metadata the source metadata * @param attributes the {@link #getAttributes(AnnotationMetadata) annotation * attributes} * @return a list of candidate configurations */ 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; } /** * Return the class used by {@link SpringFactoriesLoader} to load configuration * candidates. * @return the factory class */ protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; }
-
-
DeferredImportSelectorGrouping:同一组自动配置类信息的加载和汇总的管理器,类定义如下:
private static class DeferredImportSelectorGrouping { private final DeferredImportSelector.Group group; private final List<DeferredImportSelectorHolder> deferredImports = new ArrayList<>(); ... /** * Return the imports defined by the group. * @return each import with its associated configuration class */ public Iterable<Group.Entry> getImports() { for (DeferredImportSelectorHolder deferredImport : this.deferredImports) { // 将每个DeferredImportSelector作为DefaultDeferredImportSelectorGroup的process方法的参数 // 在DefaultDeferredImportSelectorGroup内部完成配置类信息加载和保存汇总 this.group.process(deferredImport.getConfigurationClass().getMetadata(), deferredImport.getImportSelector()); } return this.group.selectImports(); } }
- 包含一个DeferredImportSelector集合和DefaultDeferredImportSelectorGroup的一个引用;。
-
getImports方法:遍历DeferredImportSelector集合,将每个DeferredImportSelector作为参数,调用DefaultDeferredImportSelectorGroup的process方法,在DefaultDeferredImportSelectorGroup内部使用DeferredImportSelector完成配置类信息的加载并保存汇总。
-
DeferredImportSelectorGroupingHandler:包含多个自动配置类的组,通过map维护组名和组管理器DeferredImportSelectorGrouping的映射。 DeferredImportSelector的分组Group的体系结构设计的主类。
类定义如下,核心方法为processGroupImports:private class DeferredImportSelectorGroupingHandler { // key为组名,value为一个组DeferredImportSelectorGrouping private final Map<Object, DeferredImportSelectorGrouping> groupings = new LinkedHashMap<>(); private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>(); 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()); } public void processGroupImports() { // 在处理主类的@EnableAutoConfiguration注解时, // 已经添加好的deferredImportSelector集合所在的组 for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { // getImports返回的是Group.Entry列表 // 即配置类的列表, // 每个Entry表示一个自动配置类, // 包含配置类的全限定名和注解信息 grouping.getImports().forEach(entry -> { // 对每个自动配置类生成一个对应的ConfigurationClass对象 ConfigurationClass configurationClass = this.configurationClasses.get( entry.getMetadata()); try { // 当做一个正常的使用@Configuration注解的配置类来处理, // 调用processImports方法处理该配置类的@Import注解 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); } }); } } ... }
-
grouping.getImports().forEach:遍历自动配置类列表,对每个自动配置类,跟处理应用代码中使用@Configuration注解的类一样,创建ConfigurationClass类型的configurationClass对象来包装该自动配置类,将configurationClass对象作为参数调用ConfigurationClassParser的processImports方法
-
processImports方法:由于自动配置类就是使用@Configuration注解的类,所以直接调用ConfigurationClassParser的processConfigurationClass方法,跟ConfigurationClassPostProcessor处理应用代码中@Configuration注解的类一样,处理该自动配置类的注解,包括@PropertySource,@ComponentScan,@Import,内部方法@Bean等注解的处理,具体在前面已经描述过。
-