spring源码分析系列 (16) spring ConfigurationClassPostProcessor实现configuration配置加载

更多文章点击--spring源码分析系列

主要分析内容

一、@Import、@ImportResource注解使用demo
二、ConfigurationClassPostProcessor加载@Configuration类完整流程图
三、ConfigurationClassPostProcessor加载@Configuration类源码分析
四、@EnableXXX设计分析

(源码基于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) ;
    }
}

MyImportSelector.java

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{MyConfiguration.class.getName()};
    }
}

MyImportBeanDefinitionRegistrar.java

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");
       }
    }
}

MyImportConfiguration.java

@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) ;
    }
}

MyConfiguration.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) ;
    }
}

ImportRegistrarFlag.java

public @interface ImportRegistrarFlag {
}

Bean.java

public class Bean {
    public Bean(){
    }
    public Bean(String name){
        System.out.println("Bean构造函数被调用啦 name =" + name);
        this.name = name ;
    }
    // .......
    @Override
    public String toString() {
        return "Bean{" +
                "name='" + name + '\'' +
                '}';
    }
}

RegistryBean.java

public class RegistryBean {
    public RegistryBean(){
    }
    public RegistryBean(String name){
        this.name = name ;
    }
    // .......
    @Override
    public String toString() {
        return "RegistryBean{" +
                "name='" + name + '\'' +
                '}';
    }
}

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">
    <bean id="importResourceBean" class="com.nancy.ioc.Bean">
        <property name="name" value="importResourceBean"/>
    </bean>
</beans>

ioc-importtest.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>

ComponentToScanImport.java

@Component
public class ComponentToScanImport {
    public ComponentToScanImport(){
        System.out.println("报扫描加载Component组件,对应的bean ==> ComponentToScanImport");
    }
    @Override
    public String toString() {
        return super.toString() + " ComponentToScanImport";
    }
}

CustomImportTest.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

   @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();
            }
        }
    }
   /**
     * 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();
        }
    }
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 {
    }
}
  • 处理@PropertySource注解
  • 处理@ComponentScan包扫描注解
  • 处理@Import注解
  • 处理@ImportResource注解
  • 处理ConfigurationClass中,被@Bean注解修饰发方法,即自定义bean注入
  • 处理ConfigurationClass实现的接口中,被@Bean注解修饰发方法,即自定义bean注入
  • 处理ConfigurationClass父类

源码如下:

    @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;
    }

看看对于@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逻辑
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 {
        // ...............
    }

}
     // 根据所有ConfigurationClass加载bean definitions
    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 设计分析

@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包含两个重要注解:

  • @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大热的原因。

posted on 2020-05-28 15:14  小猩  阅读(1526)  评论(0编辑  收藏  举报

导航