Spring对@Configuration的解析处理

Spring配置Bean有多种形式,第一种常用的就是通过XML文件配置,另外一种就是通过@Configuration声明类,表明是一个配置文件,他的本质作用和XML是相同的,作为Bean的载体。今天我们来和大家聊一下Spring对Configuraion的解析。

首先我们先解释一些知识点,因为这些知识贯穿Spring初始化的始终。第一点,BeanFactoryPostProcessor的作用。允许修改spring容器中Bean的定义,也就是修改BeanDefinition。第二点,BeanDefinitionRegistryPostProcessor的作用,他继承了BeanFactoryPostProcessor,说明他拥有BeanFactoryPostProcessor的能力,除此之外,他和提供方法来注册新的BeanDefinition。第三点,Spring是在什么时候处理BeanFactoryPostProcessor呢?这个问题的答案可以看一下org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors的实现,应该不难看懂。

上面的知识点都了解之后,我们开始介绍我们今天的主角:ConfigurationClassPostProcessor。它实现了BeanDefinitionRegistryPostProcessor接口,所以他本身具有注册Bean的功能。那我们看看他的生命周期方法postProcessBeanDefinitionRegistry

@Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        ...
        processConfigBeanDefinitions(registry);
    }

方法中只留下一个解析方法,无疑他就是理解解析的关键。下面我们来着重讲解

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
        String[] candidateNames = registry.getBeanDefinitionNames();

        for (String beanName : candidateNames) {
            BeanDefinition beanDef = registry.getBeanDefinition(beanName);  //关注点1
            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)) {
                //关注点2
                configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
            }
        }

        // Return immediately if no @Configuration classes were found
        if (configCandidates.isEmpty()) {
            return;
        }

        // Sort by previously determined @Order value, if applicable
        Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
            @Override
            public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
                int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
                int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
                return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
            }
        });

        // Detect any custom bean name generation strategy supplied through the enclosing application context
        SingletonBeanRegistry singletonRegistry = null;
        if (registry instanceof SingletonBeanRegistry) {
            singletonRegistry = (SingletonBeanRegistry) registry;
            if (!this.localBeanNameGeneratorSet && singletonRegistry.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
                BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
                this.componentScanBeanNameGenerator = generator;
                this.importBeanNameGenerator = generator;
            }
        }

        // Parse each @Configuration class
        ConfigurationClassParser parser = new ConfigurationClassParser(
                this.metadataReaderFactory, this.problemReporter, this.environment,
                this.resourceLoader, this.componentScanBeanNameGenerator, registry);

        Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
        Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
        do {
            //关注点3
            parser.parse(candidates);
            parser.validate();

            Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
            configClasses.removeAll(alreadyParsed);

            // 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());
            }
            //关注点4
            this.reader.loadBeanDefinitions(configClasses);
            alreadyParsed.addAll(configClasses);

            candidates.clear();
            if (registry.getBeanDefinitionCount() > candidateNames.length) {
                String[] newCandidateNames = registry.getBeanDefinitionNames();
                Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
                Set<String> alreadyParsedClasses = new HashSet<String>();
                for (ConfigurationClass configurationClass : alreadyParsed) {
                    alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
                }
                for (String candidateName : newCandidateNames) {
                    if (!oldCandidateNames.contains(candidateName)) {
                        BeanDefinition beanDef = registry.getBeanDefinition(candidateName);
                        if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory) &&
                                !alreadyParsedClasses.contains(beanDef.getBeanClassName())) {
                            candidates.add(new BeanDefinitionHolder(beanDef, candidateName));
                        }
                    }
                }
                candidateNames = newCandidateNames;
            }
        }
        while (!candidates.isEmpty());

        // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
        if (singletonRegistry != null) {
            if (!singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
                singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
            }
        }

        if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
            ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
        }
    }

首先我们看下关注点1,从BeanDefinitionRegistry中根据名称获取到具体的BeanDefinition.然后经历了两个判断方法ConfigurationClassUtils.isFullConfigurationClassConfigurationClassUtils.isLiteConfigurationClass
他们的实现是根据具体的字符串和BeanDefinition中key为org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass中的value进行比较得出的,此处可以先忽略,后面会有地方继续讲到。下面我们看下关注点2,spring是如何判断这个这个Bean是Configaration的,我们就得看看ConfigurationClassUtils.checkConfigurationClassCandidate

public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
        String className = beanDef.getBeanClassName();
        if (className == null) {
            return false;
        }

        AnnotationMetadata metadata;
        if (beanDef instanceof AnnotatedBeanDefinition &&
                className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {
            // Can reuse the pre-parsed metadata from the given BeanDefinition...
            metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
        }
        else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
            // Check already loaded Class if present...
            // since we possibly can't even load the class file for this Class.
            Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
            metadata = new StandardAnnotationMetadata(beanClass, true);
        }
        else {
            try {
                MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
                metadata = metadataReader.getAnnotationMetadata();
            }
            catch (IOException ex) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Could not find class file for introspecting configuration annotations: " + className, ex);
                }
                return false;
            }
        }

        if (isFullConfigurationCandidate(metadata)) {
            beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
        }
        else if (isLiteConfigurationCandidate(metadata)) {
            beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
        }
        else {
            return false;
        }

        // It's a full or lite configuration candidate... Let's determine the order value, if any.
        Map<String, Object> orderAttributes = metadata.getAnnotationAttributes(Order.class.getName());
        if (orderAttributes != null) {
            beanDef.setAttribute(ORDER_ATTRIBUTE, orderAttributes.get(AnnotationUtils.VALUE));
        }

        return true;
    }

这个方法的前半部分就是为了得到这个Bean对应的AnnotationMetadata。随后通过两个判断方法isFullConfigurationCandidate和isLiteConfigurationCandidate判断是否需要给BeanDefination中增加对应的键值对,其中键的定义就是就是我们关注点1中提到的org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass。那么看看是具体怎么判断的

public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
        return metadata.isAnnotated(Configuration.class.getName());
    }

如果类上有@Configuration注解说明是一个完全(Full)的配置类。

public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
        // Do not consider an interface or an annotation...
        if (metadata.isInterface()) {
            return false;
        }

        // Any of the typical annotations found?
        for (String indicator : candidateIndicators) {
            if (metadata.isAnnotated(indicator)) {
                return true;
            }
        }

        // Finally, let's look for @Bean methods...
        try {
            return metadata.hasAnnotatedMethods(Bean.class.getName());
        }
        catch (Throwable ex) {
            if (logger.isDebugEnabled()) {
                logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
            }
            return false;
        }
    }

如果是一个接口,那么不是简化(Lite)配置类,如果类上有candidateIndicators这个Set中定义的注解的话(@Component,@ComponentScan,@Import,@ImportResource)那么就是一个简化配置类,如果不是上面两种情况,那么有@Bean注解修饰的方法也是简化配置类。OK,目前为止我们可以知道Spring是怎么判断是一个配置类的,随后将配置类放入到configCandidates这个BeanDefinitionHolder的集合中存储,进行下一步的操作。有了这个集合,下面我们就可以创建一个配置文件的解析器ConfigurationClassParser类型的实例parser,进行解析(parse)。那我们看看关注点3,parse方法的实现。

public void parse(Set<BeanDefinitionHolder> configCandidates) {
        this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();

        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 (Exception ex) {
                throw new BeanDefinitionStoreException(
                        "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
            }
        }

        processDeferredImportSelectors();
    }

开头创建了一个LinkedList存放DeferredImportSelectorHolder,方法最后开始处理这个。中间的过程就是具体的解析过程,我们看一下。parse有很多歌重载,但是最终着眼于,根据入参,创建一个ConfigurationClass类型的实例,利用processConfigurationClass来处理这个ConfigurationClass类型的实例。我们继续看

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
        if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
            return;
        }

        ...

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

第一个操作是处理条件注解(@Conditional),如果不满足条件注解中的条件的定义,直接返回不进行下面的操作。将ConfigurationClass configClass类型的数据,转化成 SourceClass sourceClass = asSourceClass(configClass);
类型的sourceClass,随后doProcessConfigurationClass。

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
        // Recursively process any member (nested) classes first
        //关注点a
        processMemberClasses(configClass, sourceClass);

        // Process any @PropertySource annotations
        //关注点b
        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
        //关注点c
        AnnotationAttributes componentScan = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ComponentScan.class);
        if (componentScan != null && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
            // 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 necessary
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) {
                    parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
                }
            }
        }

        // Process any @Import annotations
        
        //关注点d
        processImports(configClass, sourceClass, getImports(sourceClass), true);

        // Process any 
        // 关注点e
        @ImportResource annotations
        if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
            AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
            String[] resources = importResource.getAliasedStringArray("locations", ImportResource.class, sourceClass);
            Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
            for (String resource : resources) {
                String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
                configClass.addImportedResource(resolvedResource, readerClass);
            }
        }

        // Process individual 
        // 关注点f
        @Bean methods
        Set<MethodMetadata> beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName());
        for (MethodMetadata methodMetadata : beanMethods) {
            configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
        }

        // Process default methods on interfaces
        // 关注点 g
        for (SourceClass ifc : sourceClass.getInterfaces()) {
            beanMethods = ifc.getMetadata().getAnnotatedMethods(Bean.class.getName());
            for (MethodMetadata methodMetadata : beanMethods) {
                if (!methodMetadata.isAbstract()) {
                    // A default method or other concrete method on a Java 8+ interface...
                    configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
                }
            }
        }

        // Process superclass, if any
        if (sourceClass.getMetadata().hasSuperClass()) {
            String superclass = sourceClass.getMetadata().getSuperClassName();
            if (!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;
    }

一个解析注解标签的大餐即将上演。首先看一下关注点a,解析成员类(内部类)

private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
        for (SourceClass memberClass : sourceClass.getMemberClasses()) {
            if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
                    !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
                if (this.importStack.contains(configClass)) {
                    this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
                }
                else {
                    this.importStack.push(configClass);
                    try {
                        processConfigurationClass(memberClass.asConfigClass(configClass));
                    }
                    finally {
                        this.importStack.pop();
                    }
                }
            }
        }
    }

找到所有的内部类,遍历判断内部类是否是配置文件类,如果是调用处理配置文件的方法processConfigurationClass处理。内部类处理完了,我们看下关注点b,怎么处理@PropertySources注解。如果Configration类上有@PropertySources注解的话,会调用processPropertySource方法处理

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
        String name = propertySource.getString("name");
        String[] locations = propertySource.getStringArray("value");
        boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
        Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
        for (String location : locations) {
            try {
                String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
                Resource resource = this.resourceLoader.getResource(resolvedLocation);
                ResourcePropertySource rps = (StringUtils.hasText(name) ?
                        new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
                addPropertySource(rps);
            }
            catch (IllegalArgumentException ex) {
                // from resolveRequiredPlaceholders
                if (!ignoreResourceNotFound) {
                    throw ex;
                }
            }
            catch (FileNotFoundException ex) {
                // from ResourcePropertySource constructor
                if (!ignoreResourceNotFound) {
                    throw ex;
                }
            }
        }
    }

操作很简单,根据注解中的value字段得到配置文件的路径,根据路径得到ResourcePropertySource类型的对象,里面包含已经加载成功的属性文件中的数据,随后调用addPropertySource方法,将配置属性添加到Enviroment中。属性文件处理成功后,我们看下关注点c,他要做的就是处理@ComponentScan注解,这个注解设计到的内容较多,后面会有文章进行详细的介绍,此处只是简单说一下他所做的事情:首先得到@ComponentScan实例,然后放入componentScanParser这个解析器中,得到被扫描进行的新的BeanDefinition,再次检查新加入的这些Bean有没有Configration类型的,如果有,继续调用parse方法,解析配置。此处@ComponentScan注解就解析完了,我们再看关注点d,他是如何解析@Import注解的。
首先我们看看他是如何收集Import注解的

private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
        Set<SourceClass> imports = new LinkedHashSet<SourceClass>();
        Set<SourceClass> visited = new LinkedHashSet<SourceClass>();
        collectImports(sourceClass, imports, visited);
        return imports;
    }
    
private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited) throws IOException {
        if (visited.add(sourceClass)) {
            for (SourceClass annotation : sourceClass.getAnnotations()) {
                String annName = annotation.getMetadata().getClassName();
                if (!annName.startsWith("java") && !annName.equals(Import.class.getName())) {
                    collectImports(annotation, imports, visited);
                }
            }
            imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
        }
    }

从配置类上得到所有的注解,遍历每一个注解,如果这个注解类的不是以java开头,另外也不是@Import的话,继续低估寻找,最后将找到的Import注解中的value的值放到imports这个集合中,当然最后返回的肯定也是imports这个集合了,所以寻找的过程是递归寻找@Import寻找。找到具体的要导入的资源了,我们看看他是怎么处理这些导入的

        private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
            Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {

        if (importCandidates.isEmpty()) {
            return;
        }

        if (checkForCircularImports && this.importStack.contains(configClass)) {
            this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
        }
        else {
            this.importStack.push(configClass);
            try {
                for (SourceClass candidate : importCandidates) {
                    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);
                        invokeAwareMethods(selector);
                        if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
                            this.deferredImportSelectors.add(
                                    new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
                        }
                        else {
                            String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                            Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                            processImports(configClass, currentSourceClass, importSourceClasses, false);
                        }
                    }
                    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);
                        invokeAwareMethods(registrar);
                        configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
                    }
                    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 (Exception ex) {
                throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
                        configClass.getMetadata().getClassName() + "]", ex);
            }
            finally {
                this.importStack.pop();
            }
        }
    }

其实可以清晰的看到有三种情况,第一种导入的类是ImportSelector类型的。利用反射实例化这个类,随后将一些应该注入的注入进去,如果是DeferredImportSelector类型的,那么就放到deferredImportSelectors这个集合里,在文章的开篇我们声明了这个集合,可以到文章开头回顾下。如果不是这种类型的,就调用selectImports方法得到需要导入的类,随后继续递归此方法,进行导入。第二种是ImportBeanDefinitionRegistrar类型,一样利用反射进行初始化,随后注入属性,随后调用了configClass.addImportBeanDefinitionRegistrar添加进来了。最后一种情况导入的就是一个配置文件Configration类,直接调用processConfigurationClass方法进行处理。@Import注解就基本上说完了。随后我们看看关注点e,处理@ImportResource注解。这个操作也是比较简单,如果配置问价类上有@ImportResource注解,获取其中locations字段中的值,通过解析占位符等得到真正的路径,通过addImportedResource添加到configClass中。接着我们要处理的就是@Bean了,看一下关注点f.得到所有的利用@Bean注解声明的方法,利用addBeanMethod方法,将封装成BeanMethod类型的对象,添加到configClass中。针对java8的默认方法,spring也进行了支持,看一下关注点g,获取配置类上的所有的接口,如果接口中的方法有@Bean注解修饰,那么也会添加到configClass中。还有一个部分,我们提到了一个延迟的ImportSelector的集合deferredImportSelectors,因为上面的流程中有添加数据到这个结合中,最后我们看看他是如何处理的org.springframework.context.annotation.ConfigurationClassParser#processDeferredImportSelectors

private void processDeferredImportSelectors() {
        List<DeferredImportSelectorHolder> deferredImports = this.deferredImportSelectors;
        this.deferredImportSelectors = null;
        Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);

        for (DeferredImportSelectorHolder deferredImport : deferredImports) {
            ConfigurationClass configClass = deferredImport.getConfigurationClass();
            try {
                String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());
                processImports(configClass, asSourceClass(configClass), asSourceClasses(imports), false);
            }
            catch (BeanDefinitionStoreException ex) {
                throw ex;
            }
            catch (Exception ex) {
                throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +
                        configClass.getMetadata().getClassName() + "]", ex);
            }
        }
    }

和正常处理的没有太大的不同,可能就是为了延迟执行吧。获取到所有要导入的类,然后调用processImports方法进行真正的导入。OK,解析的流程我们就告一段落,将这个类中的所有的注解解析了一遍,有些数据也放到了configClass里面,还没有用到。下面我们再看看他是再哪里用到的。看一下org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions的关注点4,创建了一个读取器,使用此读取器通过配置好的ConfigurationClass进行加载BeanDefinition

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.removeImportingClassFor(configClass.getMetadata().getClassName());
            return;
        }

        if (configClass.isImported()) {
            registerBeanDefinitionForImportedConfigurationClass(configClass);
        }
        for (BeanMethod beanMethod : configClass.getBeanMethods()) {
            loadBeanDefinitionsForBeanMethod(beanMethod);
        }
        loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
        loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
    }

里面的registerBeanDefinitionForImportedConfigurationClassloadBeanDefinitionsForBeanMethodloadBeanDefinitionsFromImportedResourcesloadBeanDefinitionsFromRegistrars分别做的事情时将此Configuration注册成了BeanDefinition,将配置文件里面的使用@Bean注释的方法变成了BeanDefinition,将ImportResource进来的配置文件加载解析,从资源文件加载合适的Bean,最后一个就是调用Config类中ImportBeanDefinitionRegistrar类型的对象的registerBeanDefinitions方法,实现他们内部的注册逻辑。

今天讲了一大通,终于把@Configuration注解的处理讲完了,希望可以帮助到需要的朋友。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容