Spring 源碼(四)解析配置類

我們要找出所有符合條件的業(yè)務Bean魏烫,首先我們需要知道在Spring中什么樣的Bean是符合條件的检痰,是需要容器來管理的:

  1. 使用組件標注注解的Bean(@Controller难咕、@Service峰弹、@Repository回怜、@Component)大年,一般項目里面使用。
  2. 使用@Bean注解標準的方法鹉戚,一般導入第三方組件的時候使用鲜戒。
  3. 使用@Import注解導入的Bean,一般快速導入一批組件時使用抹凳。

doProcessConfigurationClass 配置類的解析過程

調(diào)用鏈路:

doProcessConfigurationClass:289, ConfigurationClassParser (org.springframework.context.annotation)
processConfigurationClass:247, ConfigurationClassParser (org.springframework.context.annotation)
parse:200, ConfigurationClassParser (org.springframework.context.annotation)
parse:169, ConfigurationClassParser (org.springframework.context.annotation)
processConfigBeanDefinitions:308, ConfigurationClassPostProcessor (org.springframework.context.annotation)
postProcessBeanDefinitionRegistry:228, ConfigurationClassPostProcessor (org.springframework.context.annotation)
invokeBeanDefinitionRegistryPostProcessors:272, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:92, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:687, AbstractApplicationContext (org.springframework.context.support)
refresh:525, AbstractApplicationContext (org.springframework.context.support)
<init>:84, AnnotationConfigApplicationContext (org.springframework.context.annotation)

源碼:

// 解析配置類將業(yè)務Bean的定義BeanDefinition注冊到容器
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
        throws IOException {

    // Recursively process any member (nested) classes first
    processMemberClasses(configClass, sourceClass);

    // 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.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
                    "]. Reason: Environment must implement ConfigurableEnvironment");
        }
    }

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

    // Process any @Import annotations
    processImports(configClass, sourceClass, getImports(sourceClass), true);

    // Process any @ImportResource annotations
    if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
        AnnotationAttributes importResource =
                AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
        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);
        }
    }

    // Process individual @Bean methods
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    // Process default methods on interfaces
    processInterfaces(configClass, sourceClass);

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

從源碼我們可以看到解析配置類的完整過程:

  1. 使用遞歸的方式處理嵌套類
  2. 處理@PropertySource注解遏餐,解析配置文件,將配置項加載到環(huán)境變量里面
  3. 處理@ComponentScan注解赢底,將所有符合條件Bean的BeanDefinition注冊到容器
  4. 處理@Import注解
  5. 處理@ImportResource注解
  6. 處理@Bean方法
  7. 處理接口的默認方法
  8. 處理父類

處理@ComponentScan注解

我們這里主要介紹下@ComponentScan注解的處理過程失都,最后主要的處理方法是ClassPathBeanDefinitionScanner#doScan()方法。

調(diào)用鏈路:

doScan:270, ClassPathBeanDefinitionScanner (org.springframework.context.annotation)
parse:135, ComponentScanAnnotationParser (org.springframework.context.annotation)
doProcessConfigurationClass:289, ConfigurationClassParser (org.springframework.context.annotation)
processConfigurationClass:247, ConfigurationClassParser (org.springframework.context.annotation)
parse:200, ConfigurationClassParser (org.springframework.context.annotation)
parse:169, ConfigurationClassParser (org.springframework.context.annotation)
processConfigBeanDefinitions:308, ConfigurationClassPostProcessor (org.springframework.context.annotation)
postProcessBeanDefinitionRegistry:228, ConfigurationClassPostProcessor (org.springframework.context.annotation)
invokeBeanDefinitionRegistryPostProcessors:272, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:92, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:687, AbstractApplicationContext (org.springframework.context.support)
refresh:525, AbstractApplicationContext (org.springframework.context.support)
<init>:84, AnnotationConfigApplicationContext (org.springframework.context.annotation)

源碼:

//  @ComponentScan 執(zhí)行包掃描
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
    for (String basePackage : basePackages) {
        // 根據(jù)包路徑找到需要加載到容器的業(yè)務Bean
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                // 將符合條件的Bean 定義注冊到容器
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

// 根據(jù)包路徑找到需要加載到容器的業(yè)務Bean
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
    try {
        // 根據(jù)包路徑找到需要加載到容器class文件
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
                
        // 根據(jù)資源加載器加載所有在對應包路徑下的class文件到內(nèi)存
        Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
        boolean traceEnabled = logger.isTraceEnabled();
        boolean debugEnabled = logger.isDebugEnabled();
        for (Resource resource : resources) {
            if (traceEnabled) {
                logger.trace("Scanning " + resource);
            }
            // 判斷是否是可讀
            if (resource.isReadable()) {
                try {
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    // 判斷是否是@Component組件
                    if (isCandidateComponent(metadataReader)) {
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setResource(resource);
                        sbd.setSource(resource);
                        if (isCandidateComponent(sbd)) {
                            if (debugEnabled) {
                                logger.debug("Identified candidate component class: " + resource);
                            }
                            candidates.add(sbd);
                        }
                        ...
    return candidates;
}

// 判斷是否是@Component組件
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    // 執(zhí)行excludeFilters
    for (TypeFilter tf : this.excludeFilters) {
        if (tf.match(metadataReader, this.metadataReaderFactory)) {
            return false;
        }
    }
    // 執(zhí)行includeFilters
    for (TypeFilter tf : this.includeFilters) {
        if (tf.match(metadataReader, this.metadataReaderFactory)) {
            return isConditionMatch(metadataReader);
        }
    }
    return false;
}

findCandidateComponents方法最終會調(diào)用AnnotationMetadataReadingVisitor類的hasAnnotation和hasMetaAnnotation方法來判斷是否是@Component組件幸冻。
調(diào)用鏈路:

hasMetaAnnotation:112, AnnotationMetadataReadingVisitor (org.springframework.core.type.classreading)
matchSelf:86, AnnotationTypeFilter (org.springframework.core.type.filter)
match:61, AbstractTypeHierarchyTraversingFilter (org.springframework.core.type.filter)
isCandidateComponent:354, ClassPathScanningCandidateComponentProvider (org.springframework.context.annotation)
findCandidateComponents:288, ClassPathScanningCandidateComponentProvider (org.springframework.context.annotation)
doScan:272, ClassPathBeanDefinitionScanner (org.springframework.context.annotation)
parse:135, ComponentScanAnnotationParser (org.springframework.context.annotation)
doProcessConfigurationClass:289, ConfigurationClassParser (org.springframework.context.annotation)
processConfigurationClass:247, ConfigurationClassParser (org.springframework.context.annotation)
parse:200, ConfigurationClassParser (org.springframework.context.annotation)
parse:169, ConfigurationClassParser (org.springframework.context.annotation)
processConfigBeanDefinitions:308, ConfigurationClassPostProcessor (org.springframework.context.annotation)
postProcessBeanDefinitionRegistry:228, ConfigurationClassPostProcessor (org.springframework.context.annotation)
invokeBeanDefinitionRegistryPostProcessors:272, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:92, PostProcessorRegistrationDelegate (org.springframework.context.support)
invokeBeanFactoryPostProcessors:687, AbstractApplicationContext (org.springframework.context.support)
refresh:525, AbstractApplicationContext (org.springframework.context.support)
<init>:84, AnnotationConfigApplicationContext (org.springframework.context.annotation)

源碼:

// 判斷是否是@Component組件
public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {

    ...
    // 判斷是否是業(yè)務Bean
    @Override
    public boolean hasAnnotation(String annotationName) {
        return this.annotationSet.contains(annotationName);
    }
    
    // 判斷是否是業(yè)務Bean
    @Override
    public boolean hasMetaAnnotation(String metaAnnotationType) {
        // 如果業(yè)務Bean是加的@Service注解粹庞,那么在metaAnnotationMap中將會以 
        // org.springframework.stereotype.Service=org.springframework.stereotype.Component的形式存儲,
        // 將@Service注解映射成@Component注解洽损。其他繼承自@Component注解的處理方式也一樣
        Collection<Set<String>> allMetaTypes = this.metaAnnotationMap.values();
        for (Set<String> metaTypes : allMetaTypes) {
            if (metaTypes.contains(metaAnnotationType)) {
                return true;
            }
        }
        return false;
    }
    ...
}

執(zhí)行流程:

  1. 根據(jù)@ComponentScan注解配置的包范圍找到所有符合條件的class文件
  2. 根據(jù)資源加載器庞溜,將所有的class文件加載到內(nèi)存
  3. 循環(huán)遍歷class找出符合條件的@Component組件
  4. 執(zhí)行@ComponentScan的excludeFilters
  5. 執(zhí)行@ComponentScan的includeFilters
  6. 獲取當前class的metaAnnotationMap,判斷是否是@Component組件
  7. 將符合條件的class的BeanDefinition注冊到容器

如果業(yè)務Bean是加的@Service注解碑定,那么在metaAnnotationMap中將會以 org.springframework.stereotype.Service=org.springframework.stereotype.Component的形式存儲流码, 將@Service注解映射成@Component注解。其他繼承自@Component注解的處理方式也一樣

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末延刘,一起剝皮案震驚了整個濱河市漫试,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碘赖,老刑警劉巖驾荣,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異普泡,居然都是意外死亡播掷,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進店門撼班,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叮趴,“玉大人,你說我怎么就攤上這事权烧∶幸啵” “怎么了伤溉?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長妻率。 經(jīng)常有香客問我乱顾,道長,這世上最難降的妖魔是什么宫静? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任走净,我火速辦了婚禮,結(jié)果婚禮上孤里,老公的妹妹穿的比我還像新娘伏伯。我一直安慰自己,他們只是感情好捌袜,可當我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布说搅。 她就那樣靜靜地躺著,像睡著了一般虏等。 火紅的嫁衣襯著肌膚如雪弄唧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天霍衫,我揣著相機與錄音候引,去河邊找鬼。 笑死敦跌,一個胖子當著我的面吹牛澄干,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播柠傍,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼麸俘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了携兵?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤搂誉,失蹤者是張志新(化名)和其女友劉穎徐紧,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炭懊,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡并级,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了侮腹。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘲碧。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖父阻,靈堂內(nèi)的尸體忽然破棺而出愈涩,到底是詐尸還是另有隱情望抽,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布履婉,位于F島的核電站煤篙,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏毁腿。R本人自食惡果不足惜辑奈,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望已烤。 院中可真熱鬧鸠窗,春花似錦、人聲如沸胯究。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唐片。三九已至丙猬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間费韭,已是汗流浹背茧球。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留星持,地道東北人抢埋。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像督暂,于是被迫代替她去往敵國和親揪垄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,077評論 2 355

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