主要分析内容
一、@Import @ImportResource 注解使用demo
二、ConfigurationClassPostProcessor加载@Configuration类完整流程图
三、ConfigurationClassPostProcessor加载@Configuration类源码分析
(源码基于spring 5.1.3.RELEASE分析)
一、@Import @ImportResource 注解使用demo
简述:demo例子中配置bean,使用常用注解@PropertySource @ImportResource @ComponentScan @Import 完成bean的加载, 往往在springboot starter组件中最为常见。
demo代码:gitee地址点击这里
入口类 ==> CustomImport.java
@Configuration @PropertySource(value = "classpath:bean.properties") @ImportResource(value = {"classpath:ioc-importresource.xml"}) @ComponentScan(basePackages = "com.nancy.ioc.BeanFactoryPostProcessor.importtest.componenttoscan") @Import(value = {MyImportSelector.class, MyImportBeanDefinitionRegistrar.class, MyImportConfiguration.class}) @ImportRegistrarFlag public class CustomImport { @Autowired private Environment environment ; @Bean("registryBean") public RegistryBean registryBean(){ String beanFieldName2 = environment.getProperty("bean.field.name2") ; System.out.println("获取@PropertySource 注入的properties ==》 bean.field.name2 = [" + beanFieldName2 + "] from environment"); return new RegistryBean("CustomImport内@Bean加载的bean, beanFieldName2=" + beanFieldName2) ; } }
public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{MyConfiguration.class.getName()}; } }MyImportSelector
import com.nancy.ioc.Bean; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; import static org.springframework.beans.factory.support.BeanDefinitionBuilder.rootBeanDefinition; public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if(importingClassMetadata.hasAnnotation(ImportRegistrarFlag.class.getName())){ System.out.println("存在ImportRegistrarFlag"); BeanDefinitionBuilder builder = rootBeanDefinition(Bean.class); builder.addConstructorArgValue("ImportBeanDefinitionRegistrar加载bean"); builder.setRole(BeanDefinition.ROLE_APPLICATION); AbstractBeanDefinition beanDefinition = builder.getBeanDefinition(); registry.registerBeanDefinition("importRegistrarFlagBean", beanDefinition); }else { System.out.println("不存在ImportRegistrarFlag"); } } }MyImportBeanDefinitionRegistrar.java
import com.nancy.ioc.Bean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; @Configuration public class MyImportConfiguration { @Autowired private Environment environment ; @org.springframework.context.annotation.Bean("selectorImportBean") public Bean bean(){ String beanFieldName2 = environment.getProperty("bean.field.name2") ; return new Bean(beanFieldName2) ; } }MyImportConfiguration.java
@Configuration public class MyConfiguration { @Autowired private Environment environment ; @org.springframework.context.annotation.Bean("myConfigurationBean") public Bean bean(){ String beanFieldName2 = environment.getProperty("bean.field.name2") ; return new Bean(beanFieldName2) ; } }MyConfiguration.java
public @interface ImportRegistrarFlag { }@ImportRegistrarFlag
public class Bean { public Bean(){ } public Bean(String name){ System.out.println("Bean构造函数被调用啦 name =" + name); this.name = name ; } private String name ; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Bean{" + "name='" + name + '\'' + '}'; } }Bean.java
public class RegistryBean { public RegistryBean(){ } public RegistryBean(String name){ this.name = name ; } private String name ; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "RegistryBean{" + "name='" + name + '\'' + '}'; } }RegistryBean.java
<?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <bean id="importResourceBean" class="com.nancy.ioc.Bean"> <property name="name" value="importResourceBean"/> </bean> </beans>ioc-importresource.xml
<?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="com.nancy.ioc.BeanFactoryPostProcessor.importtest.entry"> </context:component-scan> </beans>ioc-importtest.xml
import org.springframework.stereotype.Component; @Component public class ComponentToScanImport { public ComponentToScanImport(){ System.out.println("报扫描加载Component组件,对应的bean ==> ComponentToScanImport"); } @Override public String toString() { return super.toString() + " ComponentToScanImport"; } }ComponentToScanImport.java
public class CustomImportTest { private ApplicationContext applicationContext ; @Before public void beforeApplicationContext(){ /** * ApplicationContext 自动注册 BeanPostProcessor、InstantiationAwareBeanPostProcessor、BeanFactoryPostProcessor * 不需要手动注册 * */ applicationContext = new ClassPathXmlApplicationContext("ioc-importtest.xml") ; } @Test public void test(){ Bean selectorImportBean = applicationContext.getBean("selectorImportBean", Bean.class) ; System.out.println(selectorImportBean); Bean importRegistrarFlagBean = applicationContext.getBean("importRegistrarFlagBean", Bean.class) ; System.out.println(importRegistrarFlagBean); Bean importResourceBean = applicationContext.getBean("importResourceBean", Bean.class) ; System.out.println(importResourceBean); RegistryBean registryBean = applicationContext.getBean("registryBean", RegistryBean.class) ; System.out.println(registryBean); ComponentToScanImport componentToScanImport = applicationContext.getBean("componentToScanImport", ComponentToScanImport.class) ; System.out.println(componentToScanImport); } @After public void after(){ ((ClassPathXmlApplicationContext)applicationContext).close(); } }
运行结果:
存在ImportRegistrarFlag 报扫描加载Component组件,对应的bean ==> ComponentToScanImport Bean构造函数被调用啦 name =hello world2 Bean构造函数被调用啦 name =hello world2 获取@PropertySource 注入的properties ==》 bean.field.name2 = [hello world2] from environment Bean构造函数被调用啦 name =ImportBeanDefinitionRegistrar加载bean Bean{name='hello world2'} Bean{name='ImportBeanDefinitionRegistrar加载bean'} Bean{name='importResourceBean'} RegistryBean{name='CustomImport内@Bean加载的bean, beanFieldName2=hello world2'} com.nancy.ioc.BeanFactoryPostProcessor.importtest.componenttoscan.ComponentToScanImport@3b0090a4 ComponentToScanImport
二、ConfigurationClassPostProcessor加载@Configuration类完整流程图
三、ConfigurationClassPostProcessor加载@Configuration类源码分析
ConfigurationClassPostProcessor继承图如下,原理可参考spring源码分析系列 (1) spring拓展接口BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor
1、入口为AbstractApplicationContext#refresh
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // ..... try { // ..... // 注册和触发容器级别接口:BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // ..... } catch (BeansException ex) { // ..... } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
2、进而触发ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
/** * Derive further bean definitions from the configuration classes in the registry. */ @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId); // 加载所有configuration classes并解析得到对应的bean definitions processConfigBeanDefinitions(registry); }
跟进processConfigBeanDefinitions,主要完成步骤:
==> 先获取所有configuration classes自身的bean definitions
==> 预处理configuration的bean definitions
==> 委托ConfigurationClassParser解析所有configuration class的bean definitions, 所有支持元素:@Bean @Import @ImportResource @ComponentScans @PropertySource @PropertySources等,统一封装成ConfigurationClass对象
==> 将所有ConfigurationClass对象解析为对应的bean definitions
/** * Build and validate a configuration model based on the registry of * {@link Configuration} classes. */ public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { List<BeanDefinitionHolder> configCandidates = new ArrayList<>(); String[] candidateNames = registry.getBeanDefinitionNames(); // 1、 先获取所有configuration classes自身的bean definitions for (String beanName : candidateNames) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // Return immediately if no @Configuration classes were found if (configCandidates.isEmpty()) { return; } // 2、 预处理configuration的bean definitions // Sort by previously determined @Order value, if applicable configCandidates.sort((bd1, bd2) -> { int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition()); int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition()); return Integer.compare(i1, i2); }); // Detect any custom bean name generation strategy supplied through the enclosing application context SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } } if (this.environment == null) { this.environment = new StandardEnvironment(); } // 3、 委托ConfigurationClassParser解析所有configuration class的bean definitions // 统一封装成ConfigurationClass对象,包含所有支持元素: // @Bean @Import @ImportResource @ComponentScans @PropertySource @PropertySources等 // Parse each @Configuration class ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates); Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size()); do { parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // 4、 将第三步所有ConfigurationClass对象解析为对应的bean definitions // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); candidates.clear(); if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] newCandidateNames = registry.getBeanDefinitionNames(); Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames)); Set<String> alreadyParsedClasses = new HashSet<>(); for (ConfigurationClass configurationClass : alreadyParsed) { alreadyParsedClasses.add(configurationClass.getMetadata().getClassName()); } for (String candidateName : newCandidateNames) { if (!oldCandidateNames.contains(candidateName)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty()); // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) { sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry()); } if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) { // Clear cache in externally provided MetadataReaderFactory; this is a no-op // for a shared cache since it'll be cleared by the ApplicationContext. ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache(); } }processConfigBeanDefinitions
3、跟进ConfigurationClassParser#parse 解析ConfigurationClass,最后延迟触发DeferredImportSelector类
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); } } // 延迟处理DeferredImportSelector类 this.deferredImportSelectorHandler.process(); }
实际由ConfigurationClassParser#processConfigurationClass具体解析ConfigurationClass
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { if (configClass.isImported()) { if (existingClass.isImported()) { existingClass.mergeImportedBy(configClass); } // Otherwise ignore new imported config class; existing non-imported class overrides it. return; } else { // Explicit bean definition found, probably replacing an import. // Let's remove the old one and go with the new one. this.configurationClasses.remove(configClass); this.knownSuperclasses.values().removeIf(configClass::equals); } } // 递归解析configuration class以及其父类configuration class // Recursively process the configuration class and its superclass hierarchy. SourceClass sourceClass = asSourceClass(configClass); do { sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); this.configurationClasses.put(configClass, configClass); }
跟进doProcessConfigurationClass,主要完成步骤:
==> 首先递归处理任何成员(嵌套)类,即@Configuration修饰的类中嵌套@Configuration类,eg:springboot开启aop配置的AopAutoConfiguration
@Configuration @ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class }) @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) public class AopAutoConfiguration { @Configuration @EnableAspectJAutoProxy(proxyTargetClass = false) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false) public static class JdkDynamicAutoProxyConfiguration { } @Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) public static class CglibAutoProxyConfiguration { } }View Code
==> 处理@PropertySource注解
==> 处理@ComponentScan包扫描注解
==> 处理@Import注解
==> 处理@ImportResource注解
==> 处理ConfigurationClass中,被@Bean注解修饰发方法,即自定义bean注入
==> 处理ConfigurationClass实现的接口中,被@Bean注解修饰发方法,即自定义bean注入
==> 处理ConfigurationClass父类
/** * 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); } // 处理@PropertySource注解 // Process any @PropertySource annotations for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } // 处理@ComponentScan包扫描注解 // Process any @ComponentScan annotations 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 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()); } } } } // 处理@Import注解 // Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true); // 处理@ImportResource注解 // Process any @ImportResource annotations 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); } } // 处理被@Bean注解修饰发方法,即自定义bean注入 // Process individual @Bean methods Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // 处理ConfigurationClass实现的接口中,被@Bean注解修饰发方法,即自定义bean注入 // Process default methods on interfaces processInterfaces(configClass, sourceClass); // 处理ConfigurationClass父类 // Process superclass, if any 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; }doProcessConfigurationClass
看看对于@Import的解析:
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、DeferredImportSelector类 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); if (selector instanceof DeferredImportSelector) { 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 class处理 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)); } } } catch (BeanDefinitionStoreException ex) { throw ex; } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to process import candidates for configuration class [" + configClass.getMetadata().getClassName() + "]", ex); } finally { this.importStack.pop(); } } }
DeferredImportSelector继承自ImportSelector,实现细粒度加载控制,对于控制加载更灵活:
- 延迟加载:在ImportSelector类加载解析之后
- 同类排序:通过实现org.springframework.core.Ordered 或者 注解@Order实现排序
- 条件加载和过滤规则:实现Group逻辑
/** * A variation of {@link ImportSelector} that runs after all {@code @Configuration} beans * have been processed. This type of selector can be particularly useful when the selected * imports are {@code @Conditional}. * * <p>Implementations can also extend the {@link org.springframework.core.Ordered} * interface or use the {@link org.springframework.core.annotation.Order} annotation to * indicate a precedence against other {@link DeferredImportSelector DeferredImportSelectors}. * * <p>Implementations may also provide an {@link #getImportGroup() import group} which * can provide additional sorting and filtering logic across different selectors. * * @author Phillip Webb * @author Stephane Nicoll * @since 4.0 */ public interface DeferredImportSelector extends ImportSelector { /** * Return a specific import group or {@code null} if no grouping is required. * @return the import group class or {@code null} */ @Nullable default Class<? extends Group> getImportGroup() { return null; } /** * Interface used to group results from different import selectors. */ interface Group { /** * Process the {@link AnnotationMetadata} of the importing @{@link Configuration} * class using the specified {@link DeferredImportSelector}. */ void process(AnnotationMetadata metadata, DeferredImportSelector selector); /** * Return the {@link Entry entries} of which class(es) should be imported for this * group. */ Iterable<Entry> selectImports(); /** * An entry that holds the {@link AnnotationMetadata} of the importing * {@link Configuration} class and the class name to import. */ class Entry { private final AnnotationMetadata metadata; private final String importClassName; public Entry(AnnotationMetadata metadata, String importClassName) { this.metadata = metadata; this.importClassName = importClassName; } /** * Return the {@link AnnotationMetadata} of the importing * {@link Configuration} class. */ public AnnotationMetadata getMetadata() { return this.metadata; } /** * Return the fully qualified name of the class to import. */ 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); } } } }DeferredImportSelector.java
4、委托给ConfigurationClassBeanDefinitionReader#loadBeanDefinitions,根据所有ConfigurationClass加载得出所有bean definitions,并注入容器中。
// 根据所有ConfigurationClass加载bean definitions /** * * Read {@code configurationModel}, registering bean definitions * with the registry based on its contents. */ public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) { TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator(); for (ConfigurationClass configClass : configurationModel) { loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator); } } private void loadBeanDefinitionsForConfigurationClass( ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) { if (trackedConditionEvaluator.shouldSkip(configClass)) { String beanName = configClass.getBeanName(); if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) { this.registry.removeBeanDefinition(beanName); } this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName()); return; } // ConfigurationClass将其自身声明为 bean if (configClass.isImported()) { registerBeanDefinitionForImportedConfigurationClass(configClass); } // ConfigurationClass 中对应的@Bean修饰的方法 for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } // 导入xml loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); // 导入ImportBeanDefinitionRegistrar loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
由此所有bean definitions加载完,由几个核心工具类配合完成:
- ConfigurationClassPostProcessor(容器级别接口) 加载解析的入口以及完成收尾部分;
- ConfigurationClassParser 负责将Configuration的bean definitions解析为对应的ConfigurationClass;
- ConfigurationClassBeanDefinitionReader 负责将ConfigurationClass集合解析为实际的bean definitions,并注入容器;
@EnableXXX 类似的注解在springboot中最为常见,starter组件加载往往会设计一个类似的启动注解。以@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 { /** * Exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ @AliasFor(annotation = EnableAutoConfiguration.class) Class<?>[] exclude() default {}; /** * Exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */ @AliasFor(annotation = EnableAutoConfiguration.class) String[] excludeName() default {}; /** * Base packages to scan for annotated components. Use {@link #scanBasePackageClasses} * for a type-safe alternative to String-based package names. * @return base packages to scan * @since 1.3.0 */ @AliasFor(annotation = ComponentScan.class, attribute = "basePackages") String[] scanBasePackages() default {}; /** * Type-safe alternative to {@link #scanBasePackages} for specifying the packages to * scan for annotated components. The package of each class specified will be scanned. * <p> * Consider creating a special no-op marker class or interface in each package that * serves no purpose other than being referenced by this attribute. * @return base packages to scan * @since 1.3.0 */ @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses") Class<?>[] scanBasePackageClasses() default {}; }SpringBootApplication
其中@SpringBootApplication包含两个重要注解:
- @SpringBootConfiguration ==> 标识为spring Boot应用,实际就是被标记为spring的@Configuration配置类
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { }
- @EnableAutoConfiguration ==> spring Boot最核心功能,自动装配入口
@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 {}; }
看到了熟悉的ImportSelector接口,其实现类AutoConfigurationImportSelector#selectImports源码如下:
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } // 加载所有spring Boot支持的默认自动装配配置 AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); 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 StringUtils.toStringArray(configurations); }
spring-boot-autoconfigure的jar中包含了一个文件META-INF/spring-autoconfigure-metadata.properties,里面包含所有默认自动化配置
final class AutoConfigurationMetadataLoader { // 默认自动装配配置存放文件 protected static final String PATH = "META-INF/" + "spring-autoconfigure-metadata.properties"; private AutoConfigurationMetadataLoader() { } public static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader) { return loadMetadata(classLoader, PATH); } static AutoConfigurationMetadata loadMetadata(ClassLoader classLoader, String path) { try { Enumeration<URL> urls = (classLoader != null) ? classLoader.getResources(path) : ClassLoader.getSystemResources(path); Properties properties = new Properties(); while (urls.hasMoreElements()) { properties.putAll(PropertiesLoaderUtils .loadProperties(new UrlResource(urls.nextElement()))); } return loadMetadata(properties); } catch (IOException ex) { throw new IllegalArgumentException( "Unable to load @ConditionalOnClass location [" + path + "]", ex); } } // ............ }
由此可以借鉴@EnableAutoConfiguration的设计思路,定义一个@EnableXXX注解用于开启自定义的功能模块。
除此之外,springboot还有一个工厂类加载机制,SpringFactoriesLoader类默认加载META-INF/spring.factories文件实现自动化装配,灵活的拓展机制也是springboot大热的原因。