【四】Spring IoC 最全源码详解之 invokeBeanFactoryPostProcessors与ConfigurationClassPostProcessor扫包

版权声明:本博客原创文章遵循CC BY-NC-SA 2.5 CN 协议 https://blog.csdn.net/wuyuwei/article/details/87483509

地球形成                   距今46亿年
地球冷却形成地壳    距今39亿年~25亿年
生命出现            距今35亿年前
细菌的出现          距今30亿年前
光合作用            距今20亿年前
多细胞生物          距今16亿年前
生命登上陆地        距今5亿年前
鱼类出现            距今5亿年前
植物出现            距今4亿年前
两栖类出现          距今3亿年前
爬虫类出现          距今2.4亿年
哺乳类出现          距今2亿年前
鸟类出现            距今1.3亿年前
灵长类出现          距今4000万年
猿与黑猩猩谱系分离  距今3000万年
南方古猿出现        距今350万年
尼安德特人消失      距今12万年前
金字塔修建          距今2700年
秦始皇统一中国      公元前221
大清亡了            距今107年

阅读本文大致需要4小时,比起上述这些时间又算得了什么呢。

目录

1.  获取配置类和执行其回调

1.1 对实现了PriorityOrdered.class接口的BeanFactoryPostProcessor进行回调方法的执行

1.2 方法回调

1.2.1 获取根路径

1.2.2 扫包

1.2.3 getResources

2. 对实现了Ordered.class接口的BeanFactoryPostProcessor进行回调方法的执行

3.  对其他BeanDefinitionRegistryPostProcessors回调方法的执行。

4. 执行其他BeanFactoryPostProcessor的回调


本节将介绍refresh()方法中的invokeBeanFactoryPostProcessors(beanFactory)方法。该方法主要进行BeanFactoryPostProcessors方法的注册和对应回调方法的执行。核心方法是

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

它非常长,我们分以下几个部分进行详细介绍。

1.  获取配置类和执行其回调

    入参List<BeanFactoryPostProcessor> beanFactoryPostProcessors是通过getBeanFactoryPostProcessors()获取已经注册到Spring的BeanFactoryPostProcessor。显然在本步骤之前是没有BeanFactoryPostProcessor被注册的,不然refresh调用invokeBeanFactoryPostProcessors方法不是多此一举吗?

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
	Set<String> processedBeans = new HashSet<>();
	if (beanFactory instanceof BeanDefinitionRegistry) {
		BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
		List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
		List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

		// 入参beanFactoryPostProcessors是获取的bean工厂List<BeanFactoryPostProcessor> beanFactoryPostProcessors中提前被注册好的postProcessors
		// 这里因为项目中并未实现BeanFactoryPostProcessor接口,故循环不会进入。
		for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
			if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
				BeanDefinitionRegistryPostProcessor registryProcessor =
						(BeanDefinitionRegistryPostProcessor) postProcessor;
				registryProcessor.postProcessBeanDefinitionRegistry(registry);
				registryProcessors.add(registryProcessor);
			}
			else {
				regularPostProcessors.add(postProcessor);
			}
		}

		List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

		// postProcessorNames: internalConfigurationAnnotationProcessor, 他实现了BeanFactoryPostProcessor接口
		String[] postProcessorNames =
				beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

		// 第一步,根据后置处理器名字判断是否实现了PriorityOrdered接口
		for (String ppName : postProcessorNames) {			
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				// 1.beanFactory.getBean(xx,xx)方法是实例化对象,然后将其添加进bean工厂中交给Spring管理
				// 2 将该bean放入到List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors中
				// 3.最终实例化的对象类型是ConfigurationClassPostProcessor.class
				currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
				processedBeans.add(ppName);
			}
		}
		sortPostProcessors(currentRegistryProcessors, beanFactory);
		registryProcessors.addAll(currentRegistryProcessors);

		//利用ConfigurationClassPostProcessor处理器对项目的配置类进行解析。
		invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
		currentRegistryProcessors.clear();

1.1 对实现了PriorityOrdered.class接口的BeanFactoryPostProcessor进行回调方法的执行

beanFactoryPostProcessors为null意味着第一个for循环直接跳过。第一个有价值的代码是:

String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

getBeanNamesForType ==> doGetBeanNamesForType,进入到doGetBeanNamesForType方法中,

private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
    List<String> result = new ArrayList<>();

    for (String beanName : this.beanDefinitionNames) {
        // 只使用大名
        if (!isAlias(beanName)) {
            //为了节约篇幅,去掉了try..catch..块
            RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);            
            if (!mbd.isAbstract() && (allowEagerInit ||
                    (mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
                            !requiresEagerInitForType(mbd.getFactoryBeanName()))) {                
                boolean isFactoryBean = isFactoryBean(beanName, mbd);
                BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
                boolean matchFound =
                        (allowEagerInit || !isFactoryBean ||
                                (dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
                        (includeNonSingletons ||
                                (dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
                        isTypeMatch(beanName, type);
                //如果没匹配到,但该bean又是FactoryBean,则按照FactoryBean的规则在beanName前加上'&',用新的名字再找一次
                if (!matchFound && isFactoryBean) {
                    beanName = FACTORY_BEAN_PREFIX + beanName;
                    matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
                }
                //找到了,就将其加入到resute的List中
                if (matchFound) {
                    result.add(beanName);
                }
            }            
        }
    }

    // 在"environment", "systemProperties","systemEnvironment" 中进行寻找
    for (String beanName : this.manualSingletonNames) {
        if (isFactoryBean(beanName)) {
            if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) {
                result.add(beanName);
                continue;
            }
            beanName = FACTORY_BEAN_PREFIX + beanName;
        }
        // 判断名为beanName的bean是否是type类型
        if (isTypeMatch(beanName, type)) {
            result.add(beanName);
        }
    }

    return StringUtils.toStringArray(result);
}

首先是遍历List<String> beanDefinitionNames,从以下候选项中找出符合条件的bdName添加到result中。

0 = "org.springframework.context.annotation.internalConfigurationAnnotationProcessor"
1 = "org.springframework.context.annotation.internalAutowiredAnnotationProcessor"
2 = "org.springframework.context.annotation.internalCommonAnnotationProcessor"
3 = "org.springframework.context.event.internalEventListenerProcessor"
4 = "org.springframework.context.event.internalEventListenerFactory"
5 = "caseApplication"

然后再从手动添加的那三个"environment", "systemProperties","systemEnvironment" beanName中寻找除满足要求的beanName也添加到result中。最终找寻的结果只有一个,internalConfigurationAnnotationProcessor,这个玩意是事先就注册进去的。具体何时注册和怎么注册,请参考本系列文章《【二】Spring IoC 最全源码详解之 register》详细分析。

for (String ppName : postProcessorNames) {			
	if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
		// 1.beanFactory.getBean(xx,xx)方法是实例化对象,然后将其添加进bean工厂中交给Spring管理
		// 2 将该bean放入到List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors中
		// 3.最终实例化的对象类型是ConfigurationClassPostProcessor.class
		currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
		processedBeans.add(ppName);
	}
}
//这里currentRegistryProcessors里面就一个值,还排个卵
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);

获取String[] postProcessorNames之后紧接着就执行BeanFactoryPostProcessor注册操作。因为ConfigurationClassPostProcessor实现了PriorityOrdered接口。所以typeToMatch.isAssignableFrom(beanType)的执行结果是true。落入if块中,beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)是将名字为ppName的后置处理实例化出来放入bean工厂中。随后再将该后置处理器对象放入List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors中。实例化过程不是本节的重点内容,后续再讲。isTypeMatch里面的很难,简单进行分析并注释如下,就不占用大量篇幅介绍了。

public boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException {
    //如果是普通的bean则beanName不变,如果是FactroyBean,则去掉前缀'&'
    String beanName = transformedBeanName(name);

    // 这里高能,不好理解。见代码块下方提示。
    Object beanInstance = getSingleton(beanName, false);
    if (beanInstance != null && beanInstance.getClass() != NullBean.class) {
        dosomething();        
    }

    //如果bean工厂中singletonObjects容器中含有该对象,但是bdmap中却不存在,说明是其他线程正在并发执行移除bean的流程。故不能再将移除的bd再进行注册了。
    else if (containsSingleton(beanName) && !containsBeanDefinition(beanName)) {
        // null instance registered
        return false;
    }

    // 我们就用了默认的bean工厂,从未自己实现过自己的bean工厂,并且还将其作为Spring默认bean工厂的父类,故这里也不会进
    BeanFactory parentBeanFactory = getParentBeanFactory();
    if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {        
        return parentBeanFactory.isTypeMatch(originalBeanName(name), typeToMatch);
    }
	
    // 融合bd。融合的对象是指定的bean及沿着继承路径一路向上的父类们
    RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);

    //从typeToMatch中获取之前封装在typeToMatch中的目标匹配class类,比如interface org.springframework.core.PriorityOrdered
    Class<?> classToMatch = typeToMatch.resolve();
    if (classToMatch == null) {
        classToMatch = FactoryBean.class;
    }

    // typesToMatch[2] = {interface org.springframework.beans.factory.FactoryBean, interface org.springframework.core.PriorityOrdered}
    Class<?>[] typesToMatch = (FactoryBean.class == classToMatch ?
            new Class<?>[] {classToMatch} : new Class<?>[] {FactoryBean.class, classToMatch});

    // 如果有装饰类,则优先匹配装饰类。 但这里并没有装饰类,不会进入
    BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
    if (dbd != null && !BeanFactoryUtils.isFactoryDereference(name)) {
        RootBeanDefinition tbd = getMergedBeanDefinition(dbd.getBeanName(), dbd.getBeanDefinition(), mbd);
        Class<?> targetClass = predictBeanType(dbd.getBeanName(), tbd, typesToMatch);
        if (targetClass != null && !FactoryBean.class.isAssignableFrom(targetClass)) {
            return typeToMatch.isAssignableFrom(targetClass);
        }
    }

    // 根据beanName预测出最终的bean类型。这个方法仅能预测标准的bean(standard bean)。
    // 比如beanName是:internalAutowiredAnnotationProcessor 预测结果是:AutowiredAnnotationBeanPostProcessor.class
    Class<?> beanType = predictBeanType(beanName, mbd, typesToMatch);
    if (beanType == null) {
        return false;
    }

    // 从类型上判断是否是FactoryBean或者是否可以转化为FactoryBean
    if (FactoryBean.class.isAssignableFrom(beanType)) {
        if (!BeanFactoryUtils.isFactoryDereference(name) && beanInstance == null) {
            beanType = getTypeForFactoryBean(beanName, mbd);
            if (beanType == null) {
                return false;
            }
        }
    }

    //从BeanName推断是否是FactoryBean
    else if (BeanFactoryUtils.isFactoryDereference(name)) {
        beanType = predictBeanType(beanName, mbd, FactoryBean.class);
        if (beanType == null || !FactoryBean.class.isAssignableFrom(beanType)) {
            return false;
        }
    }

    ResolvableType resolvableType = mbd.targetType;
    if (resolvableType == null) {
        resolvableType = mbd.factoryMethodReturnType;
    }
    if (resolvableType != null && resolvableType.resolve() == beanType) {
        return typeToMatch.isAssignableFrom(resolvableType);
    }

    // 判断beanType是否可以转换成typeToMatch。 
    // 这里beanType是ConfigurationClassPostProcessor.class
    // typeToMatch是入参:PriorityOrdered.class
    return typeToMatch.isAssignableFrom(beanType);
}

        isTypeMatch值得说明的是,Object beanInstance = getSingleton(beanName, false);当前一般的结果是null。如果bean存在,但容器中又找不到对应的beanName和bd,则说明当前有另外的线程正在销毁当前本线程正在注册的bd。遇到那样的场景就不应该将注册该bd的流程进行下去了。

循环体执行完成之后,本章节最重要的流程分析开始了!

1.2 方法回调

//利用ConfigurationClassPostProcessor处理器对项目的配置类进行解析。
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();

invokeBeanDefinitionRegistryPostProcessors依次回调currentRegistryProcessors中所有配置类的postProcessBeanDefinitionRegistry方法。ConfigurationClassPostProcessor类回调方法的具体执行是processConfigBeanDefinitions(registry)。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    //获取当前已经注册的所有的bd
    String[] candidateNames = registry.getBeanDefinitionNames();

    //从所有bd中,找出配置类bd。 简单来说,就是找出CaseApplication.class的bd:caseApplication
    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        //如果该bd之前就被标记过,则说明已经被解析过,不会加入configCandidates中等待再次被解析
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
            if (logger.isDebugEnabled()) {
                logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }
        // 在bd中标记是full还是lite类型,然后加入configCandidates中等待解析。
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }

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

    // 项目中就一个,没什么好排序的
    configCandidates.sort((bd1, bd2) -> {
        int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
        int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
        return Integer.compare(i1, i2);
    });

    // 没有自定义bean名称的命名器,所以这个if快可以不看。
    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();
    }

    //创建一个配置类解析器,用于解析和收纳扫描出的需要Spring管理的类。
    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 {
        // 通过配置类CaseApplication.class的bd解析出所有所需要Spring管理的类。然后将其放入parser的Map<ConfigurationClass, ConfigurationClass> configurationClasses中。
        parser.parse(candidates);
        parser.validate();

        //获取parser的Map<ConfigurationClass, ConfigurationClass> configurationClasses中的类,本项目中共计29个。
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
        configClasses.removeAll(alreadyParsed);

        // 新建一个配置类的读取器
        if (this.reader == null) {
            this.reader = new ConfigurationClassBeanDefinitionReader(
                    registry, this.sourceExtractor, this.resourceLoader, this.environment,
                    this.importBeanNameGenerator, parser.getImportRegistry());
        }
        // 通过读取器将刚才寻找到的类依次解析成为bd,放入bean工厂的beanDefinitionMap中。
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);

        candidates.clear();
        if (registry.getBeanDefinitionCount() > candidateNames.length) {
            //再次获取bean工厂中被注册的bdName
            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());
            }

            //再从候选的newCandidateNames中找出是否有新的配置类,如果有则加入到candidates集合中
            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());

    // 将"org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry"的对象添加到bean工厂的singletonObjects容器中
    // 并且向bean工厂的manualSingletonNames中添加"org.springframework.context.annotation.ConfigurationClassPostProcessor.importRegistry"类名
    if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
        sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
    }
 
    if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
        ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
    }
}

首先通过String[] candidateNames = registry.getBeanDefinitionNames()从bean工厂中获取当前已经注册的所有bd。

0 = "org.springframework.context.annotation.internalConfigurationAnnotationProcessor"
1 = "org.springframework.context.annotation.internalAutowiredAnnotationProcessor"
2 = "org.springframework.context.annotation.internalCommonAnnotationProcessor"
3 = "org.springframework.context.event.internalEventListenerProcessor"
4 = "org.springframework.context.event.internalEventListenerFactory"
5 = "caseApplication"

然后通过checkConfigurationClassCandidate的层次检查,决定是否将该bd加入到List<BeanDefinitionHolder> configCandidates中。只要加上了@Configuration注解,那么该配置类就是FULL的,如果只是通过@Component, @ComponentScan, @Import, @ImportResource修饰,那么就是lite的。checkConfigurationClassCandidate内部会通过上述逻辑判断当前的bd是哪一种类型,并且将full还是lite类型标记在bd上。在此之前对isFullConfigurationClassisLiteConfigurationClass的判断就是对bd上是full还是lite属性的标记进行检查,在该checkConfigurationClassCandidate还未标记之前,得到都是默认值false。

本项目中第一轮解析得到的结果是configCandidates中就一个结果:"caseApplication"。然后根据caseApplication所在路径进行扫包,然后将扫出的类解析为bd,然后进行注册。随后第N轮会因为N-1轮扫包结果在configCandidates添加很多类,当do...while循环处理完所有的类后,才会开始后续的处理流程。

1.2.1 获取根路径

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        // 配置类bd,本项目中是bdName是class com.Hodey.learn.CaseApplication
        BeanDefinition bd = holder.getBeanDefinition();
        // 在内部封装好后,最终都会调用processConfigurationClass(ConfigurationClass configClass)方法
        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 (Exception ex) {            
            throw new xxx;
        }
    }

    this.deferredImportSelectorHandler.process();
}

try块中的三个parse重载方法都是将不同的输入封装成一个ConfigurationClass configClass对象,最终都会统一调用processConfigurationClass方法进行具体的执行。processConfigurationClass方法中第一步还是判断该配置类是否被解析过,如果解析过则不用再走下面流程。SourceClass sourceClass = asSourceClass(configClass);从传入的(AnnotatedBeanDefinition) bd).getMetadata()中解析出sourceClass,本项目得到的结果是class com.Hodey.learn.CaseApplication。然后调用doProcessConfigurationClass方法继续执行。

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
        throws IOException {
    // 处理@Component注解
    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
        // Recursively process any member (nested) classes first
        processMemberClasses(configClass, sourceClass);
    }
    
    // 处理@PropertySource注解。
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
    }

    // 处理@ComponentScan注解。用于扫包。本章节重点分析!    
    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) {
            // 调用ComponentScanAnnotationParser#parse方法完成扫包工作。
            Set<BeanDefinitionHolder> scannedBeanDefinitions =
                    this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                if (bdCand == null) {
                    bdCand = holder.getBeanDefinition();
                }
                // 如果得到的结果中有类是配置类,则需要嵌套的调用parse再对其进行解析。
                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
    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);
        }
    }

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

    processInterfaces(configClass, sourceClass);

    if (sourceClass.getMetadata().hasSuperClass()) {
        String superclass = sourceClass.getMetadata().getSuperClassName();
        if (superclass != null && !superclass.startsWith("java") &&
                !this.knownSuperclasses.containsKey(superclass)) {
            this.knownSuperclasses.put(superclass, configClass);
            return sourceClass.getSuperClass();
        }
    }
    return null;
}

doProcessConfigurationClass通过从configClass类中读取注释、成员和方法,构建一个完整的ConfigurationClass。(该方法可以被多次调用)。方法一开始是对传入configClass是否含有@Component@PropertySource注解的处理。 这种@PropertySource("classpath:xxx.properties")注解可以读取配置文件中的值,然后注入到@Value(xxx)注解标记的对象中。本项目中配置类的注解是@ComponentScan("com.Hodey"),我们重点分析的处理流程就是它。

通过AnnotationConfigUtils.attributesForRepeatable方法获得配置类的各种属性值封装成一个属性对象添加到Set中,然后核心是

Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

它调用ComponentScanAnnotationParser#parse方法完成扫包工作。

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    //// 之前已经提前注册好的org.springframework.beans.factory.support.BeanNameGenerator
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
            componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

    Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
    boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
    scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
            BeanUtils.instantiateClass(generatorClass));
    // 默认是NO,不使用代理模式
    ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
    if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
        scanner.setScopedProxyMode(scopedProxyMode);
    }
    else {
        Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
        scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
    }

    scanner.setResourcePattern(componentScan.getString("resourcePattern"));

    for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {
            scanner.addIncludeFilter(typeFilter);
        }
    }
    for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
        for (TypeFilter typeFilter : typeFiltersFor(filter)) {
            scanner.addExcludeFilter(typeFilter);
        }
    }

    // 如果注解是@Lazy,则再bd中标记对应的lazy属性true
    boolean lazyInit = componentScan.getBoolean("lazyInit");
    if (lazyInit) {
        scanner.getBeanDefinitionDefaults().setLazyInit(true);
    }

    // 获取basePackages,本项目只有一个:com.Hodey,并将其解析后添加到basePackages Set中。
    Set<String> basePackages = new LinkedHashSet<>();    
    String[] basePackagesArray = componentScan.getStringArray("basePackages");
    for (String pkg : basePackagesArray) {
        String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
                ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
        Collections.addAll(basePackages, tokenized);
    }
    for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
        basePackages.add(ClassUtils.getPackageName(clazz));
    }
    if (basePackages.isEmpty()) {
        basePackages.add(ClassUtils.getPackageName(declaringClass));
    }

    // 向ClassPathScanningCandidateComponentProvider#excludeFilters中注册一个ComponentScanAnnotationParser对象
    scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
        @Override
        protected boolean matchClassName(String className) {
            return declaringClass.equals(className);
        }
    });
    // 扫描刚刚解析出来的basePackages,调用org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan
    return scanner.doScan(StringUtils.toStringArray(basePackages));
}

方法可以分为三部分:1.创建scanner,并为scanner装配一些后续需要用到的工具;2.获取basePackages,本项目只有一个:com.Hodey,并将其解析后添加到basePackages Set中;3.扫描刚刚解析出来的basePackages,重点是doScan方法。doScan方法又大体划分为2步骤:1. 从basePackage路径出发,搜寻出该路径下所有需要Spring管理的类并解析为bd返回赋值给candidates集合。2. 再具体的对每个bd进行更多的属性解析。doScan后,传入的basePackage下的所有类就完成了最后的注册。我们将分析重点放在findCandidateComponents方法,接下来一起看看Spring是如何扫包的!

1.2.2 扫包

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        // 寻找@Component注解类并解析成bd
        // 搜寻的路径是classpath*:com/Hodey/**/*.class,然后根据一些列规则确定出扫描的根路径URL是:file:/F:/gitSource/HodeyLab/case/target/classes/com/Hodey/
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);

        // 以下再具体解析各个类的候选对象的bd,然后注册
        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);
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

findCandidateComponents中通过scanCandidateComponents(basePackage);扫描basePackage,将其需要Spring管理的类转换成bd,并放入Set<BeanDefinition>中返回。

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        // packageSearchPath: = classpath*:com/Hodey/**/*.class
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        // Resource 接口是Spring对资源文件的一个基础抽象类。
        // 根据搜索路径找出所有该路径及其子路径下的文件
        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

        // 通过metadataReader挨个解析resources中的文件,通metadataReader读取并按照繁杂的规则解析文件内容存储到metadataReader中。        
        for (Resource resource : resources) {            
            if (resource.isReadable()) {
                try {
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    // 内部通过// AbstractTypeHierarchyTraversingFilter.match方法对文件进行具体的解析。
                    // 1. 首相将配置类自身排除在外,不参与后续解析。
                    // 2. 通过判断该类是否有@Component和注解 或 JSF的ManageBean。如果都没有,则不会将其加入到candidates集合中。
                    if (isCandidateComponent(metadataReader)) {
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        // 以下两条setXX语句非常重要,是后面通过反射实例化bean的依据!
                        sbd.setResource(resource);
                        sbd.setSource(resource);

                        if (isCandidateComponent(sbd)) {                            
                            candidates.add(sbd);
                        }                        
                    }
                }
                catch (Throwable ex) {
                    throw new xxx;
                }
            }
        }
    }
    catch (IOException ex) {
        throw new xxx;
    }
    return candidates;
}

scanCandidateComponents首先将传入的basePackage按照规则:前面加“classpath*:”,后面加“/**/*.class”拼凑成包的搜索 路径。classpath*:com/Hodey/**/*.class。然后通过getResourcePatternResolver().getResources(packageSearchPath);获取扫包结果。本项目结果是,注意该步骤仅仅是返回basePackage路径下所有的类文件。

0 = {FileSystemResource@2461} "file [F:\gitSource\HodeyLab\case\target\classes\com\Hodey\learn\CaseApplication.class]"
1 = {FileSystemResource@2462} "file [F:\gitSource\HodeyLab\case\target\classes\com\Hodey\learn\bean\Adam.class]"
2 = {FileSystemResource@2463} "file [F:\gitSource\HodeyLab\case\target\classes\com\Hodey\learn\bean\Eve.class]"
3 = {FileSystemResource@2464} "file [F:\gitSource\HodeyLab\case\target\classes\com\Hodey\learn\bean\Human.class]"
4 = {FileSystemResource@2466} "file [F:\gitSource\HodeyLab\case\target\classes\com\Hodey\learn\service\GardenofEden.class]"

具体该文件是否需要读取和解析为bd,是后续for循环中做的事情。它的核心是if(isCandidateComponent(metadataReader)),isCandidateComponent方法内部通过AbstractTypeHierarchyTraversingFilter.match方法对文件进行具体的解析。它首先将配置类自身排除在外,不参与后续解析,然后再通过判断该类是否有@Component和注解 或 JSF的ManageBean。判断结果是true后才会进入if语句块。语句块中的setXX语句非常重要,是Spring通过反射进行bean实例化的依据!

ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
// 以下两条setXX语句非常重要,是后面通过反射实例化bean的依据!
sbd.setResource(resource);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {                            
	candidates.add(sbd);
}  

最终注册的bd是

bean\Adam.class
bean\Eve.class
bean\Human.class]"
service\GardenofEden.class

1.2.3 getResources

没想到你这么有耐心,能一路陪我走到这里。既然都来了,我们不妨看看Spring是如何通过basePackage获取该路径下文件的吧。回到scanCandidateComponents中的getResourcePatternResolver().getResources(packageSearchPath),我们现在分析getResources方法。

public Resource[] getResources(String locationPattern) throws IOException {
    if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
        // 先根据classpath*:com/Hodey/**/*.class找出文件的根路径classpath*:com/Hodey/,然后再次嵌套调用本方法,会进入else语句块。
        if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
            return findPathMatchingResources(locationPattern);
        }
        else {
            return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
        }
    }
    else {
        // war包的扫描方法
        doScanWar(...);
    }
}

if是走jar包的分析策略,else是走war包。两个解析方法的思想一样,我们就按照jar包的分析策略来一探究竟。if中判断classpath*:com/Hodey/**/*.class这个路径去掉前缀"classpath*:"中是否有'*' 或者 '?', 如果存在则进入findPathMatchingResources中。

protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
    // rootDirPath = classpath*:com/Hodey/
    String rootDirPath = determineRootDir(locationPattern);
    // subPattern = **/*.class
    String subPattern = locationPattern.substring(rootDirPath.length());

    // 获取rootDirPath路径下的目录和文件
    Resource[] rootDirResources = getResources(rootDirPath);

    Set<Resource> result = new LinkedHashSet<>(16);
    for (Resource rootDirResource : rootDirResources) {
        rootDirResource = resolveRootDirResource(rootDirResource);
        URL rootDirUrl = rootDirResource.getURL();
        if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {
            URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
            if (resolvedUrl != null) {
                rootDirUrl = resolvedUrl;
            }
            rootDirResource = new UrlResource(rootDirUrl);
        }
        if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
            result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
        }
        else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
            result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
        }
        // 
        else {
            result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
        }
    }
    return result.toArray(new Resource[0]);
}        

findPathMatchingResources先根据classpath*:com/Hodey/**/*.class确定本次寻找路径classpath*:com/Hodey/,然后再次递归调用getResources方法,这次传入的rootDirPath是com/Hodey/。因为com/Hodey/不含有'*'或'?',则会进入getResources#findAllClassPathResources方法,实际被调用者是doFindAllClassPathResources方法。该方法通过类加载器的ClassLoader.getResources(path)获得起始扫描路径,将path路径下的文件或目录添加进result集合中,函数调用结束层层弹栈,最后将result的结果返回给findPathMatchingResources#rootDirResources数组。遍历该数组,调用doFindPathMatchingFileResources。首先通过rootDirResource.getFile().getAbsoluteFile();获得当前rootDirResource路径下的目录。

protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern)
        throws IOException {

    File rootDir;
    try {
        // 得到rootDirResource路径下目录
        // 比如得到file:/F:/gitSource/HodeyLab/case/target/classes/com/Hodey    
        rootDir = rootDirResource.getFile().getAbsoluteFile();
    }

    return doFindMatchingFileSystemResources(rootDir, subPattern);
}

然后依次进入到doFindMatchingFileSystemResources-->retrieveMatchingFiles(File rootDir, String pattern)-->doRetrieveMatchingFiles(String fullPattern, File dir, Set<File> result)。具体的类扫描是通过doRetrieveMatchingFiles(x,x,x,)方法递归调用进行解析的。

// 根据指定的路径递归的寻找文件加入到result集合中
protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set<File> result) throws IOException {
    
    // listDirectory是得到dir路径下所有的文件和目录,内部调用的是File.list方法
    for (File content : listDirectory(dir)) {
        // '\' 符合替换为 '/'
        String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");
        // 如果是目录文件则进入目录递归调用本方法继续探寻
        if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {            
            doRetrieveMatchingFiles(fullPattern, content, result);
        }
        if (getPathMatcher().match(fullPattern, currPath)) {
            result.add(content);
        }
    }
}

规则是根据路径得到该路径下的所有文件和目录。如果是文件就添加到result集合中,如果是目录则递归的进入目录并执行相同操作,每次递归都将扫到的file加入result集合中。递归完成后函数一路弹栈返回到doFindMatchingFileSystemResources函数,将得到的Set<File>封装成Set<Resource>再一路返回到findPathMatchingResources函数,将结果添加进findPathMatchingResources#Set<Resource> result,最后将集合转换成数组输出返回到scanCandidateComponents函数的resources数组中。接下来的故事你们都清楚了,遍历 resources对象中的resource,将其转换成bd。

配置类,不管是手动注册的配置类CaseApplication, 还是其他方式声明的配置类,比如@Configuration都将成为包扫描的根路径。然后通过上述流程对该路径下的所有文件进行扫描和转换成bd注册到bean工厂的bdMap中。

至此,Spring扫包流程的介绍就详细介绍完成了。你看累了吗?看看本文的目录,是的,才完成invokeBeanFactoryPostProcessors第一步!休息一下,喝口水再来?processedBeans.contains(ppName)中在第一步主力过程中就添加了,后续过程中该判断值都会为true,相当于滤过了处理。

2. 对实现了Ordered.class接口的BeanFactoryPostProcessor进行回调方法的执行

postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
    if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        processedBeans.add(ppName);
    }
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();

3.  对其他BeanDefinitionRegistryPostProcessors回调方法的执行。

boolean reiterate = true;
while (reiterate) {
    reiterate = false;

    // postProcessorNames: internalConfigurationAnnotationProcessor
    // 同上,因为processedBeans已经注册了internalConfigurationAnnotationProcessor,故不会进行解析。while循环结束。
    postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    for (String ppName : postProcessorNames) {
        if (!processedBeans.contains(ppName)) {
            currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
            processedBeans.add(ppName);
            reiterate = true;
        }
    }
    sortPostProcessors(currentRegistryProcessors, beanFactory);
    registryProcessors.addAll(currentRegistryProcessors);
    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    currentRegistryProcessors.clear();
}

// 注册ImportAwareBeanPostProcessor后置处理器
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);

4. 执行其他BeanFactoryPostProcessor的回调

String[] postProcessorNames =
        beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

// 分类
List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
    //当前processedBeans中仅有internalConfigurationAnnotationProcessor,所以另外3个类都会被执行。
    if (processedBeans.contains(ppName)) {
        // skip - already processed in first phase above
    }
    //同上,分别判断是否实现了PriorityOrdered.class或者Ordered.class接口
    else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
        // propertySourcesPlaceholderConfigurer 属于PriorityOrdered.class。
        // 将放入了bean工厂的singletonFactories的hashmap中。因为PropertySourcesPlaceholderConfigurer还是一个BeanFactoryAware。
        // 最后还将其放入了bean工厂的singletonObjects中。
        priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
    }
    else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
        orderedPostProcessorNames.add(ppName);
    }
    else {
        //ConfigurationBeanFactoryMetadata 和 internalEventListenerProcessor放入了nonOrderedPostProcessorNames中
        nonOrderedPostProcessorNames.add(ppName);
    }
}

// 执行priorityOrderedPostProcessors中各对象的回调,即PropertySourcesPlaceholderConfigurer的回调。
// PropertySourcesPlaceholderConfigurer的作用是根据@Value(xxx)注解,在配置文件中正确解析出要填充的placeHolder值。具体流程在后续bean实例化过程中再细说。
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);

// 暂时没有
List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String postProcessorName : orderedPostProcessorNames) {
    orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
sortPostProcessors(orderedPostProcessors, beanFactory);
invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);

// 将ConfigurationBeanFactoryMetadata 和 internalEventListenerProcessor 实例化对象放入bean工厂的singletonObjects中。
// 然后调用各自BeanFactoryPostProcessor的回调方法postProcessBeanFactory。
// BeanFactoryPostProcessor是用于在bean工厂初始化期间,用来存放@Bean定义元数据的工具类。
// internalEventListenerProcessor对应实例化的对象是:DefaultEventListenerFactory类的对象,用于处理@EventListener注解。
List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String postProcessorName : nonOrderedPostProcessorNames) {
    nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
}
invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

// 打扫卫生
beanFactory.clearMetadataCache();

终于换了种口味,解析BeanFactoryPostProcessor.class的这种BeanFactoryPostProcessor了。它一共扫出了4个具体的实现类。

"org.springframework.context.annotation.internalConfigurationAnnotationProcessor"
"org.springframework.context.event.internalEventListenerProcessor"
"propertySourcesPlaceholderConfigurer"
"org.springframework.boot.context.properties.ConfigurationBeanFactoryMetadata"

第四步具体的执行流程详解见上述代码的注释。


本文介绍了invokeBeanFactoryPostProcessors的相关流程,特别是针对最为关键的ConfigurationClassPostProcessor类后置处理器的回调。该类的回调方法postProcessBeanFactory是Spring启动流程中实现扫包的入口。

如果将00:00-7:00的时刻表映射到Sping的启动流程的话,目前的时间大致应该是在02:30左右。下一节,我们将继续分析refresh方法。这一步骤极为重要。我们下篇文章再见~

猜你喜欢

转载自blog.csdn.net/wuyuwei/article/details/87483509