Spring對(duì)@Configuration的解析處理

Spring配置Bean有多種形式,第一種常用的就是通過(guò)XML文件配置施符,另外一種就是通過(guò)@Configuration聲明類甲馋,表明是一個(gè)配置文件,他的本質(zhì)作用和XML是相同的嗜湃,作為Bean的載體奈应。今天我們來(lái)和大家聊一下Spring對(duì)Configuraion的解析。

首先我們先解釋一些知識(shí)點(diǎn)购披,因?yàn)檫@些知識(shí)貫穿Spring初始化的始終杖挣。第一點(diǎn),BeanFactoryPostProcessor的作用刚陡。允許修改spring容器中Bean的定義惩妇,也就是修改BeanDefinition。第二點(diǎn)筐乳,BeanDefinitionRegistryPostProcessor的作用歌殃,他繼承了BeanFactoryPostProcessor,說(shuō)明他擁有BeanFactoryPostProcessor的能力蝙云,除此之外氓皱,他和提供方法來(lái)注冊(cè)新的BeanDefinition。第三點(diǎn)勃刨,Spring是在什么時(shí)候處理BeanFactoryPostProcessor呢波材?這個(gè)問(wèn)題的答案可以看一下org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors的實(shí)現(xiàn),應(yīng)該不難看懂身隐。

上面的知識(shí)點(diǎn)都了解之后廷区,我們開始介紹我們今天的主角:ConfigurationClassPostProcessor。它實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor接口贾铝,所以他本身具有注冊(cè)Bean的功能躲因。那我們看看他的生命周期方法postProcessBeanDefinitionRegistry

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

方法中只留下一個(gè)解析方法早敬,無(wú)疑他就是理解解析的關(guān)鍵。下面我們來(lái)著重講解

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

        for (String beanName : candidateNames) {
            BeanDefinition beanDef = registry.getBeanDefinition(beanName);  //關(guān)注點(diǎn)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)) {
                //關(guān)注點(diǎn)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 {
            //關(guān)注點(diǎn)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());
            }
            //關(guān)注點(diǎn)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();
        }
    }

首先我們看下關(guān)注點(diǎn)1大脉,從BeanDefinitionRegistry中根據(jù)名稱獲取到具體的BeanDefinition.然后經(jīng)歷了兩個(gè)判斷方法ConfigurationClassUtils.isFullConfigurationClassConfigurationClassUtils.isLiteConfigurationClass
他們的實(shí)現(xiàn)是根據(jù)具體的字符串和BeanDefinition中key為org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass中的value進(jìn)行比較得出的搞监,此處可以先忽略,后面會(huì)有地方繼續(xù)講到镰矿。下面我們看下關(guān)注點(diǎn)2琐驴,spring是如何判斷這個(gè)這個(gè)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;
    }

這個(gè)方法的前半部分就是為了得到這個(gè)Bean對(duì)應(yīng)的AnnotationMetadata秤标。隨后通過(guò)兩個(gè)判斷方法isFullConfigurationCandidate和isLiteConfigurationCandidate判斷是否需要給BeanDefination中增加對(duì)應(yīng)的鍵值對(duì)绝淡,其中鍵的定義就是就是我們關(guān)注點(diǎn)1中提到的org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass。那么看看是具體怎么判斷的

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

如果類上有@Configuration注解說(shuō)明是一個(gè)完全(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;
        }
    }

如果是一個(gè)接口牢酵,那么不是簡(jiǎn)化(Lite)配置類,如果類上有candidateIndicators這個(gè)Set中定義的注解的話(@Component衙猪,@ComponentScan馍乙,@Import,@ImportResource)那么就是一個(gè)簡(jiǎn)化配置類垫释,如果不是上面兩種情況丝格,那么有@Bean注解修飾的方法也是簡(jiǎn)化配置類。OK棵譬,目前為止我們可以知道Spring是怎么判斷是一個(gè)配置類的显蝌,隨后將配置類放入到configCandidates這個(gè)BeanDefinitionHolder的集合中存儲(chǔ),進(jìn)行下一步的操作订咸。有了這個(gè)集合曼尊,下面我們就可以創(chuàng)建一個(gè)配置文件的解析器ConfigurationClassParser類型的實(shí)例parser,進(jìn)行解析(parse)脏嚷。那我們看看關(guān)注點(diǎn)3骆撇,parse方法的實(shí)現(xiàn)。

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

開頭創(chuàng)建了一個(gè)LinkedList存放DeferredImportSelectorHolder然眼,方法最后開始處理這個(gè)艾船。中間的過(guò)程就是具體的解析過(guò)程,我們看一下高每。parse有很多歌重載屿岂,但是最終著眼于,根據(jù)入?yún)⒕洌瑒?chuàng)建一個(gè)ConfigurationClass類型的實(shí)例爷怀,利用processConfigurationClass來(lái)處理這個(gè)ConfigurationClass類型的實(shí)例。我們繼續(xù)看

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

第一個(gè)操作是處理?xiàng)l件注解(@Conditional)带欢,如果不滿足條件注解中的條件的定義运授,直接返回不進(jìn)行下面的操作烤惊。將ConfigurationClass configClass類型的數(shù)據(jù),轉(zhuǎn)化成 SourceClass sourceClass = asSourceClass(configClass);
類型的sourceClass吁朦,隨后doProcessConfigurationClass柒室。

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {
        // Recursively process any member (nested) classes first
        //關(guān)注點(diǎn)a
        processMemberClasses(configClass, sourceClass);

        // Process any @PropertySource annotations
        //關(guān)注點(diǎn)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
        //關(guān)注點(diǎn)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
        
        //關(guān)注點(diǎn)d
        processImports(configClass, sourceClass, getImports(sourceClass), true);

        // Process any 
        // 關(guān)注點(diǎn)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 
        // 關(guān)注點(diǎn)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
        // 關(guān)注點(diǎn) 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;
    }

一個(gè)解析注解標(biāo)簽的大餐即將上演。首先看一下關(guān)注點(diǎn)a逗宜,解析成員類(內(nèi)部類)

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

找到所有的內(nèi)部類雄右,遍歷判斷內(nèi)部類是否是配置文件類,如果是調(diào)用處理配置文件的方法processConfigurationClass處理纺讲。內(nèi)部類處理完了擂仍,我們看下關(guān)注點(diǎn)b,怎么處理@PropertySources注解熬甚。如果Configration類上有@PropertySources注解的話逢渔,會(huì)調(diào)用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;
                }
            }
        }
    }

操作很簡(jiǎn)單,根據(jù)注解中的value字段得到配置文件的路徑乡括,根據(jù)路徑得到ResourcePropertySource類型的對(duì)象肃廓,里面包含已經(jīng)加載成功的屬性文件中的數(shù)據(jù),隨后調(diào)用addPropertySource方法粟判,將配置屬性添加到Enviroment中驻襟。屬性文件處理成功后稠通,我們看下關(guān)注點(diǎn)c,他要做的就是處理@ComponentScan注解书闸,這個(gè)注解設(shè)計(jì)到的內(nèi)容較多唯鸭,后面會(huì)有文章進(jìn)行詳細(xì)的介紹碘勉,此處只是簡(jiǎn)單說(shuō)一下他所做的事情:首先得到@ComponentScan實(shí)例烹棉,然后放入componentScanParser這個(gè)解析器中滥崩,得到被掃描進(jìn)行的新的BeanDefinition,再次檢查新加入的這些Bean有沒(méi)有Configration類型的涂臣,如果有惨险,繼續(xù)調(diào)用parse方法羹幸,解析配置。此處@ComponentScan注解就解析完了辫愉,我們?cè)倏搓P(guān)注點(diǎn)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"));
        }
    }

從配置類上得到所有的注解恭朗,遍歷每一個(gè)注解屏镊,如果這個(gè)注解類的不是以java開頭,另外也不是@Import的話痰腮,繼續(xù)低估尋找而芥,最后將找到的Import注解中的value的值放到imports這個(gè)集合中,當(dāng)然最后返回的肯定也是imports這個(gè)集合了膀值,所以尋找的過(guò)程是遞歸尋找@Import尋找棍丐。找到具體的要導(dǎo)入的資源了误辑,我們看看他是怎么處理這些導(dǎo)入的

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

其實(shí)可以清晰的看到有三種情況,第一種導(dǎo)入的類是ImportSelector類型的歌逢。利用反射實(shí)例化這個(gè)類巾钉,隨后將一些應(yīng)該注入的注入進(jìn)去,如果是DeferredImportSelector類型的秘案,那么就放到deferredImportSelectors這個(gè)集合里睛琳,在文章的開篇我們聲明了這個(gè)集合,可以到文章開頭回顧下踏烙。如果不是這種類型的师骗,就調(diào)用selectImports方法得到需要導(dǎo)入的類,隨后繼續(xù)遞歸此方法讨惩,進(jìn)行導(dǎo)入辟癌。第二種是ImportBeanDefinitionRegistrar類型,一樣利用反射進(jìn)行初始化荐捻,隨后注入屬性黍少,隨后調(diào)用了configClass.addImportBeanDefinitionRegistrar添加進(jìn)來(lái)了。最后一種情況導(dǎo)入的就是一個(gè)配置文件Configration類处面,直接調(diào)用processConfigurationClass方法進(jìn)行處理厂置。@Import注解就基本上說(shuō)完了。隨后我們看看關(guān)注點(diǎn)e魂角,處理@ImportResource注解昵济。這個(gè)操作也是比較簡(jiǎn)單,如果配置問(wèn)價(jià)類上有@ImportResource注解野揪,獲取其中l(wèi)ocations字段中的值访忿,通過(guò)解析占位符等得到真正的路徑,通過(guò)addImportedResource添加到configClass中斯稳。接著我們要處理的就是@Bean了海铆,看一下關(guān)注點(diǎn)f.得到所有的利用@Bean注解聲明的方法,利用addBeanMethod方法挣惰,將封裝成BeanMethod類型的對(duì)象卧斟,添加到configClass中。針對(duì)java8的默認(rèn)方法憎茂,spring也進(jìn)行了支持珍语,看一下關(guān)注點(diǎn)g,獲取配置類上的所有的接口,如果接口中的方法有@Bean注解修飾唇辨,那么也會(huì)添加到configClass中廊酣。還有一個(gè)部分,我們提到了一個(gè)延遲的ImportSelector的集合deferredImportSelectors赏枚,因?yàn)樯厦娴牧鞒讨杏刑砑訑?shù)據(jù)到這個(gè)結(jié)合中亡驰,最后我們看看他是如何處理的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);
            }
        }
    }

和正常處理的沒(méi)有太大的不同晓猛,可能就是為了延遲執(zhí)行吧。獲取到所有要導(dǎo)入的類凡辱,然后調(diào)用processImports方法進(jìn)行真正的導(dǎo)入戒职。OK,解析的流程我們就告一段落透乾,將這個(gè)類中的所有的注解解析了一遍洪燥,有些數(shù)據(jù)也放到了configClass里面,還沒(méi)有用到乳乌。下面我們?cè)倏纯此窃倌睦镉玫降呐踉稀?匆幌?code>org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions的關(guān)注點(diǎn)4汉操,創(chuàng)建了一個(gè)讀取器再来,使用此讀取器通過(guò)配置好的ConfigurationClass進(jìn)行加載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());
    }

里面的registerBeanDefinitionForImportedConfigurationClassloadBeanDefinitionsForBeanMethod磷瘤、loadBeanDefinitionsFromImportedResources芒篷、loadBeanDefinitionsFromRegistrars分別做的事情時(shí)將此Configuration注冊(cè)成了BeanDefinition,將配置文件里面的使用@Bean注釋的方法變成了BeanDefinition,將ImportResource進(jìn)來(lái)的配置文件加載解析采缚,從資源文件加載合適的Bean,最后一個(gè)就是調(diào)用Config類中ImportBeanDefinitionRegistrar類型的對(duì)象的registerBeanDefinitions方法针炉,實(shí)現(xiàn)他們內(nèi)部的注冊(cè)邏輯。

今天講了一大通扳抽,終于把@Configuration注解的處理講完了篡帕,希望可以幫助到需要的朋友。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末摔蓝,一起剝皮案震驚了整個(gè)濱河市赂苗,隨后出現(xiàn)的幾起案子愉耙,更是在濱河造成了極大的恐慌贮尉,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件朴沿,死亡現(xiàn)場(chǎng)離奇詭異猜谚,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)赌渣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門魏铅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人坚芜,你說(shuō)我怎么就攤上這事览芳。” “怎么了鸿竖?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵沧竟,是天一觀的道長(zhǎng)铸敏。 經(jīng)常有香客問(wèn)我,道長(zhǎng)悟泵,這世上最難降的妖魔是什么杈笔? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮糕非,結(jié)果婚禮上蒙具,老公的妹妹穿的比我還像新娘。我一直安慰自己朽肥,他們只是感情好禁筏,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著衡招,像睡著了一般融师。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚁吝,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天旱爆,我揣著相機(jī)與錄音,去河邊找鬼窘茁。 笑死怀伦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的山林。 我是一名探鬼主播房待,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼驼抹!你這毒婦竟也來(lái)了桑孩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤框冀,失蹤者是張志新(化名)和其女友劉穎流椒,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體明也,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宣虾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了温数。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绣硝。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖撑刺,靈堂內(nèi)的尸體忽然破棺而出鹉胖,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布甫菠,位于F島的核電站败许,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏淑蔚。R本人自食惡果不足惜市殷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望刹衫。 院中可真熱鬧醋寝,春花似錦、人聲如沸带迟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)仓犬。三九已至嗅绰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間搀继,已是汗流浹背窘面。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叽躯,地道東北人财边。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像点骑,于是被迫代替她去往敵國(guó)和親酣难。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容