Spring配置类以及扫描过程源码解析

书接上文

文章目录

    • 一、 拾遗
      • 1. 回顾
      • 2. 源码分析
    • 二、 配置类扫描源码分析
      • 1. 源码分析
      • 2. BeanDefinition覆盖问题
      • 3. full配置类和lite配置类的区别

一、 拾遗

1. 回顾

前面我们分析了Spring框架器启动过程要做的事情,着重分析了ApplicationContext的refresh方法。但前面一节我们还遗留了refresh的两个重要方法还没有分析,分别是 invokeBeanFactoryPostProcessors(beanFactory)registerBeanPostProcessors(beanFactory),第一个方法的主要作用是扫描BeanDefinition和注册BeanDefinition,第二个方法的作用主要是注册BeanPostProcessor,都是为后序创建Bean做准备。

2. 源码分析

首先分析invokeBeanFactoryPostProcessors(beanFactory),在执行这句代码之前,spring主要是帮我们创建了一个BeanFactory,以及准备一些核心的Bean。在这之前先介绍一下BeanFactoryPostProcessor

BeanFactoryPostProcessor和BeanPostProcessor都是在Spring容器启动时进行一些额外处理的接口,但它们在容器生命周期中的阶段和用途上有一些区别。

  • BeanFactoryPostProcessor:

阶段: 在Spring容器实例化任何bean之前执行。
作用: 主要用于修改容器中的bean定义,例如修改bean的属性值、添加新的bean定义等。
接口方法: 只有一个方法postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)。

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {// 在这里可以修改bean的定义// 例如,修改属性值BeanDefinition bd = beanFactory.getBeanDefinition("myBean");bd.getPropertyValues().add("propertyName", "newValue");}
}

上面代码就给BeanDefinition添加了一个属性。

  • BeanPostProcessor:

阶段: 在容器实例化bean后,但在调用bean的初始化方法前后执行。
作用: 主要用于在bean初始化过程中执行一些定制的操作,例如代理、属性注入等。
接口方法: 有两个方法,postProcessBeforeInitialization和postProcessAfterInitialization。

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {// 在初始化方法调用前执行return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {// 在初始化方法调用后执行return bean;}
}

总体而言,BeanFactoryPostProcessor用于在bean实例化前修改bean定义,而BeanPostProcessor用于在bean初始化过程中执行定制操作

BeanFactoryPostProcessor有一个子接口名字为BeanDefinitionRegistryPostProcessor,该接口扩展了BeanFactoryPostProcessor接口,提供了一个postProcessBeanFactory,允许我们向容器中注册BeanDefinition。

@Component
public class CService implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {}@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws //注册BeanDefinitionbeanFactory.registerDependentBean(....);BeansException {}
}

ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,而ConfigurationClassPostProcessor这个类是就是扫描和注册beanDeifnition的核心。

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware {}

进入invokeBeanFactoryPostProcessors(beanFactory)方法源码:

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {// 重点PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)// 关于LoadTimeWeaver看这篇文章了解即可,https://www.cnblogs.com/wade-luffy/p/6073702.htmlif (!NativeDetector.inNativeImage() && beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}}

下面代码就拿到一些BeanFacotryPostProcessor(),但它不会扫描二是拿到容器中已经添加好的BeanFacotryPostProcessor(),比如我们使用·ApplicationContext.addBeanFactoryPostProcessor(自己定义的BeanFacotryPostProcessor)就会在这里被拿到。

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

进入invokeBeanFactoryPostProcessors方法。

//List<BeanFactoryPostProcessor>就包括两种类型,一种是BeanFactoryPostProcessor,另一种是BeanDefinitionRegistryPostProcessor,beanFactoryPostProcessors就是我们手动在refresh之前add的beanFactoryPostProcessors
public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {   // BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor// Invoke BeanDefinitionRegistryPostProcessors first, if any.Set<String> processedBeans = new HashSet<>();if (beanFactory instanceof BeanDefinitionRegistry) {BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();// beanFactoryPostProcessors集合一般情况下都是空的,除非我们手动调用容器的addBeanFactoryPostProcessor方法添加了// beanFactoryPostProcessors中可能包含了:普通BeanFactoryPostProcessor对象和BeanDefinitionRegistryPostProcessor对象// 对于BeanDefinitionRegistryPostProcessor对象,会执行自己的postProcessBeanDefinitionRegistry()方法for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {//如果beanFactoryPostProcessors中添加的BeanFactoryPostProcessor有BeanDefinitionRegistryPostProcessor类型的,就首先执行其postProcessBeanDefinitionRegistry方法,然后添加到registryProcessors集合中。BeanDefinitionRegistryPostProcessor registryProcessor =(BeanDefinitionRegistryPostProcessor) postProcessor;registryProcessor.postProcessBeanDefinitionRegistry(registry);registryProcessors.add(registryProcessor);}else {//如果是BeanFactoryPostProcessor类型,添加到regularPostProcessors集合中,进行分类处理regularPostProcessors.add(postProcessor);}}//定义一个BeanDefinitionRegistryPostProcessor类型的list集合List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();// 执行扫描出来的BeanDefinitionRegistryPostProcessor,这里会拿出所有的`ConfigurationClassPostProcessor`,因为从spring启动的逻辑可以看出,ConfigurationClassPostProcessor这个类的beandefinition已经被spring提前放到容器中了String[] postProcessorNames =beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);for (String ppName : postProcessorNames) {//判断当前的BeanDefinitionRegistryPostProcessor是否实现了PriorityOrdered接口if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {//创建这个beancurrentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);}}// 升序排序sortPostProcessors(currentRegistryProcessors, beanFactory);registryProcessors.addAll(currentRegistryProcessors);//执行postProcessBeanDefinitionRegistry方法invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup());currentRegistryProcessors.clear();// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);for (String ppName : postProcessorNames) {// processedBeans表示该beanFactoryPostProcessor的postProcessBeanDefinitionRegistry()方法已经执行过了,不再重复执行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, beanFactory.getApplicationStartup());currentRegistryProcessors.clear();// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.// 执行哪些没有实现了PriorityOrdered或Ordered接口的普通BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法// 在这个过程中可能会向BeanFactory中注册另外的BeanDefinitionRegistryPostProcessor,所以需要while,直到确定所有的BeanDefinitionRegistryPostProcessor都执行完了// 在这个过程中注册的BeanDefinitionRegistryPostProcessor,所实现的PriorityOrdered或Ordered接口可能会不按顺序执行// 比如 A注册了B和C,B又注册了D和E,那么B和C会按顺序执行,D和E也会按顺序执行,但是B、C、D、E整体不能保证是顺序执行boolean reiterate = true;while (reiterate) {reiterate = false;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, beanFactory.getApplicationStartup());currentRegistryProcessors.clear();}// Now, invoke the postProcessBeanFactory callback of all processors handled so far.// 执行完BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法后,// 再执行BeanDefinitionRegistryPostProcessor的postProcessBeanFactory()方法invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);// 执行手动添加的普通BeanFactoryPostProcessor的postProcessBeanFactory()方法invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);}else {// Invoke factory processors registered with the context instance.invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);}// 执行扫描出来的普通BeanFactoryPostProcessor// Do not initialize FactoryBeans here: We need to leave all regular beans// uninitialized to let the bean factory post-processors apply to them!String[] postProcessorNames =beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,// Ordered, and the rest.List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();List<String> orderedPostProcessorNames = new ArrayList<>();List<String> nonOrderedPostProcessorNames = new ArrayList<>();// 先进行分类for (String ppName : postProcessorNames) {if (processedBeans.contains(ppName)) {// skip - already processed in first phase above}else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));}else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {orderedPostProcessorNames.add(ppName);}else {nonOrderedPostProcessorNames.add(ppName);}}// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.sortPostProcessors(priorityOrderedPostProcessors, beanFactory);invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);// Next, invoke the BeanFactoryPostProcessors that implement Ordered.List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());for (String postProcessorName : orderedPostProcessorNames) {orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));}sortPostProcessors(orderedPostProcessors, beanFactory);invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);// Finally, invoke all other BeanFactoryPostProcessors.List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());for (String postProcessorName : nonOrderedPostProcessorNames) {nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));}invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);// Clear cached merged bean definitions since the post-processors might have// modified the original metadata, e.g. replacing placeholders in values...beanFactory.clearMetadataCache();}

上面代码可以总结为下面的流程:

  • 执行通过ApplicationContext添加进来的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
  • 执行BeanFactory中实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor的 postProcessBeanDefinitionRegistry()方法
  • 执行BeanFactory中实现了Ordered接口的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法
  • 执行BeanFactory中其他的BeanDefinitionRegistryPostProcessor的 postProcessBeanDefinitionRegistry()方法
  • 执行上面所有的BeanDefinitionRegistryPostProcessor的postProcessBeanFactory()方法
  • 执行通过ApplicationContext添加进来的BeanFactoryPostProcessor的 postProcessBeanFactory()方法
  • 执行BeanFactory中实现了PriorityOrdered接口的BeanFactoryPostProcessor的 postProcessBeanFactory()方法
  • 执行BeanFactory中实现了Ordered接口的BeanFactoryPostProcessor的 postProcessBeanFactory()方法
  • 执行BeanFactory中其他的BeanFactoryPostProcessor的postProcessBeanFactory()方法

下面再分析一下registerBeanPostProcessors(beanFactory);,前面说到这个方法的主要作用就是将扫描到的BeanPostProcessors实例化并排序,并添加到BeanFactory的beanPostProcessors属性中去。我们看看它源码是怎么做的:

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);}public static void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {//从bean工厂中拿到所有BeanPostProcessor.class类型的beandefinitionString[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);// beanProcessorTargetCount表示BeanFactory中所有的BeanPostProcessor数量,+1表示BeanPostProcessorCheckerint beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));//初始化一系列集合List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();List<String> orderedPostProcessorNames = new ArrayList<>();List<String> nonOrderedPostProcessorNames = new ArrayList<>();//对所有的BeanPostProcessor按照类型进行分类for (String ppName : postProcessorNames) {if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);priorityOrderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {orderedPostProcessorNames.add(ppName);}else {nonOrderedPostProcessorNames.add(ppName);}}// 升序排序sortPostProcessors(priorityOrderedPostProcessors, beanFactory);//添加到专门存储BeanPostProcessor的集合List<BeanPostProcessor> postProcessors中registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);// Next, register the BeanPostProcessors that implement Ordered.List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());for (String ppName : orderedPostProcessorNames) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);orderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}sortPostProcessors(orderedPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, orderedPostProcessors);// Now, register all regular BeanPostProcessors.List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());for (String ppName : nonOrderedPostProcessorNames) {BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);nonOrderedPostProcessors.add(pp);if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);}}registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);// Finally, re-register all internal BeanPostProcessors.// MergedBeanDefinitionPostProcessor排在最后sortPostProcessors(internalPostProcessors, beanFactory);registerBeanPostProcessors(beanFactory, internalPostProcessors);// Re-register post-processor for detecting inner beans as ApplicationListeners,// moving it to the end of the processor chain (for picking up proxies etc).// ApplicationListenerDetector放在所有BeanPostProcessor之后,注意ApplicationListenerDetector的equals()方法实现beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));}

上面代码的主要逻辑就是先拿出所有的BeanPostProcessor的BeanDefinition,然后按照实现了PriorityOrdered接口,实现Ordered接口以及普通的BeanPostProcessor的顺序加入到List<BeanPostProcessor> postProcessors这个集合中,这个集合是工厂中专门用于存放BeanPostProcessor的集合。这就是控制BeanPostProcessor的执行顺序的机制。注意还有一个特殊的地方:

if (pp instanceof MergedBeanDefinitionPostProcessor) {internalPostProcessors.add(pp);
}

上面代码在registerBeanPostProcessors函数中多次出现,它的意思是如果某个BeanPostProcessor实现了MergedBeanDefinitionPostProcessor接口就会放入internalPostProcessors集合中,无论你是否实现了PriorityOrdered接口还是实现了Ordered接口。然后在函数最后会执行下面代码:

sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, internalPostProcessors);

这里对internalPostProcessors集合进行了排序,然后放到了postProcessors集合中,这就告诉我们,如果你的BeanPostProcessor实现了MergedBeanDefinitionPostProcessor接口,它一定是最后一批执行的BeanPostProcessor。到此registerBeanPostProcessors的逻辑就执行完了。

二、 配置类扫描源码分析

1. 源码分析

回到invokeBeanFactoryPostProcessors方法,的整个过程中它其实就会拿到ConfigurationClassPostProcessor这个类,然后执行它的postProcessBeanDefinitionRegistry方法和postProcessBeanFactory方法。前面我们说过ConfigurationClassPostProcessor是一个有关BeanDefinition的扫描和注册非常核心的方法,我们看看该类的这两个方法是在干什么:

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,PriorityOrdered, ResourceLoaderAware, ApplicationStartupAware, BeanClassLoaderAware, EnvironmentAware {
....
@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {int registryId = System.identityHashCode(registry);if (this.registriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);}if (this.factoriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry);}this.registriesPostProcessed.add(registryId);// 解析配置类processConfigBeanDefinitions(registry);}@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {int factoryId = System.identityHashCode(beanFactory);if (this.factoriesPostProcessed.contains(factoryId)) {throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + beanFactory);}this.factoriesPostProcessed.add(factoryId);if (!this.registriesPostProcessed.contains(factoryId)) {// BeanDefinitionRegistryPostProcessor hook apparently not supported...// Simply call processConfigurationClasses lazily at this point then.processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);}// 增强配置类,代理加了Configuration注解的配置类enhanceConfigurationClasses(beanFactory);beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));}....
}

首先看postProcessBeanDefinitionRegistry方法:

@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {int registryId = System.identityHashCode(registry);if (this.registriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);}if (this.factoriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry);}this.registriesPostProcessed.add(registryId);// 解析配置类processConfigBeanDefinitions(registry);}

processConfigBeanDefinitions(registry);该方法就完成了spring配置类的解析。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {List<BeanDefinitionHolder> configCandidates = new ArrayList<>();//拿到当前容器中所有已经注册好的BeanDefinition,此时我们自定义的BeanDefinition是拿不到的,因为现在还没开始BeanDifinition的扫描过程,String[] candidateNames = registry.getBeanDefinitionNames();for (String beanName : candidateNames) {BeanDefinition beanDef = registry.getBeanDefinition(beanName);if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {if (logger.isDebugEnabled()) {logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);}}// 什么是配置类?else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));}}// Return immediately if no @Configuration classes were foundif (configCandidates.isEmpty()) {return;}// Sort by previously determined @Order value, if applicable// 通过@Order可以排序,升序排序,order越小越靠前configCandidates.sort((bd1, bd2) -> {int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());return Integer.compare(i1, i2);});// Detect any custom bean name generation strategy supplied through the enclosing application contextSingletonBeanRegistry sbr = null;if (registry instanceof SingletonBeanRegistry) {sbr = (SingletonBeanRegistry) registry;if (!this.localBeanNameGeneratorSet) {// 可以预先往单例池中添加一个CONFIGURATION_BEAN_NAME_GENERATOR的BeanNameGenerator类型的bean// 可以用来作为扫描得到的Bean和import导入进来的Bean的beanNameBeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);if (generator != null) {this.componentScanBeanNameGenerator = generator;this.importBeanNameGenerator = generator;}}}if (this.environment == null) {this.environment = new StandardEnvironment();}// Parse each @Configuration classConfigurationClassParser 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());// 递归解析配置类,有可能通过解析一个配置类,得到了其他的配置类,比如扫描和Importtdo {StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");// 解析配置类,会把每个BeanDefinitionHolder首先封装为ConfigurationClass// 在这个过程中会进行扫描、导入等步骤,从而会找到其他的ConfigurationClass// 解析配置类的结果是什么?parser.parse(candidates);  // AppConfig.class--->BeanDefinitionparser.validate();// configClasses相当于就是解析之后的结果Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// Read the model and create bean definitions based on its contentif (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}// 把所有的ConfigurationClass加载成BeanDefinition,通过情况下一个配置类会对应一个BeanDefinition,不过也有可能一个配置类对应多个BeanDefinition// 比如一个配置类中有多个@Bean,一个配置配置了@ImportResourcethis.reader.loadBeanDefinitions(configClasses);alreadyParsed.addAll(configClasses);processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();// candidates中存的是BeanDefinition,configClasses中存的是ConfigurationClasscandidates.clear();// 如果发现BeanDefinition增加了,则有可能增加了配置类if (registry.getBeanDefinitionCount() > candidateNames.length) {String[] newCandidateNames = registry.getBeanDefinitionNames();Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));Set<String> alreadyParsedClasses = new HashSet<>();for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}for (String candidateName : newCandidateNames) {if (!oldCandidateNames.contains(candidateName)) {BeanDefinition bd = registry.getBeanDefinition(candidateName);// 检查多出来的BeanDefinition是不是配置类,需不需要解析if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&!alreadyParsedClasses.contains(bd.getBeanClassName())) {candidates.add(new BeanDefinitionHolder(bd, candidateName));}}}candidateNames = newCandidateNames;}}while (!candidates.isEmpty());// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classesif (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());}if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {// Clear cache in externally provided MetadataReaderFactory; this is a no-op// for a shared cache since it'll be cleared by the ApplicationContext.((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();}}

上面代码首先执行String[] candidateNames = registry.getBeanDefinitionNames();,拿到当前容器中所有已经注册好了的BeanDefinition,包括前面在讲Spring启动时帮我们注册的一些核心BeanDefinition以及我们在容器refresh之前调用application.register注册的类。相信大家也注意到了applicationContext.register(AppConfig.class);这个类也被我们注册到了容器中,而AppCongif.class就是我们的配置类,所以上面这句代码是可以拿到我们的配置类的信息的。

AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
applicationContext.register(AppConfig.class);

拿到所有的BeanDefinition后就会执行ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)来判断拿到的拿个类是配置类。然后执行configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));将所有的配置类加入到configCandidates这个候选配置类集合中。

public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {// @Bean定义的配置类Bean是不起作用的String className = beanDef.getBeanClassName();if (className == null || beanDef.getFactoryMethodName() != null) {return false;}// AnnotationMetadata表示某个类的注解信息,但是并一定要加载这个类AnnotationMetadata metadata;// 如果AnnotatedBeanDefinition,则直接取AnnotationMetadataif (beanDef instanceof AnnotatedBeanDefinition &&className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {// Can reuse the pre-parsed metadata from the given BeanDefinition...metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();}// 如果是AbstractBeanDefinition,则解析beanClass得到AnnotationMetadataelse 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();if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||BeanPostProcessor.class.isAssignableFrom(beanClass) ||AopInfrastructureBean.class.isAssignableFrom(beanClass) ||EventListenerFactory.class.isAssignableFrom(beanClass)) {return false;}metadata = AnnotationMetadata.introspect(beanClass);}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;}}Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());// 存在@Configuration,并且proxyBeanMethods不为false(为true或为null)时,就是Full配置类if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);}// 存在@Configuration,并且proxyBeanMethods为false时,是lite配置类// 或者不存在@Configuration,但是只要存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类// 或者不存在@Configuration,只要存在@Bean注解了的方法,就是lite配置类else if (config != null || isConfigurationCandidate(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.Integer order = getOrder(metadata);if (order != null) {beanDef.setAttribute(ORDER_ATTRIBUTE, order);}return true;}

上面代码中 Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());这一句就拿到了当前BeanDefinition的@Configuration注解中的属性,然后执行下面代码:

// 存在@Configuration,并且proxyBeanMethods不为false(为true或为null)时,就是Full配置类if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);}// 存在@Configuration,并且proxyBeanMethods为false时,是lite配置类// 或者不存在@Configuration,但是只要存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类// 或者不存在@Configuration,只要存在@Bean注解了的方法,就是lite配置类else if (config != null || isConfigurationCandidate(metadata)) {beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);}else {return false;}

如果存在@Configuration,并且proxyBeanMethods不为false(为true或为null)时,就是Full配置类。这个就是第一个if语句的作用。

对于就是第二个else if的作用,它里面有一句isConfigurationCandidate(metadata),我们看看这个方法是干嘛

public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {//如果当前类是一个接口,说明就不是配置类if (metadata.isInterface()) {return false;}// Any of the typical annotations found?// 只要存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类for (String indicator : candidateIndicators) {if (metadata.isAnnotated(indicator)) {return true;}}// Finally, let's look for @Bean methods...// 只要存在@Bean注解了的方法,就是lite配置类return hasBeanMethods(metadata);}

candidateIndicators是一个集合,我们看看集合中有什么东西,

	private static final Set<String> candidateIndicators = new HashSet<>(8);static {candidateIndicators.add(Component.class.getName());candidateIndicators.add(ComponentScan.class.getName());candidateIndicators.add(Import.class.getName());candidateIndicators.add(ImportResource.class.getName());}

这就说明了如果当前类伤有@Component、@ComponentScan、@Import、@ImportResource注解中的任何一个注解,就会返回true,此时的注解为lite配置类。return hasBeanMethods(metadata);就是判断有没有@Bean注解,这说明只要该类内部有@Bean注解,它也是一个lite配置类。经过上面的代码就可以拿到我们的配置类了。继续回到processConfigBeanDefinitions方法。

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

如果获取的配置类集合为空,就直接返回了。

// 通过@Order可以排序,升序排序,order越小越靠前configCandidates.sort((bd1, bd2) -> {int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());return Integer.compare(i1, i2);});

如果有配置类,就会按照order对这些配置类进行排序,order越小的配置类越靠前。

ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment,this.resourceLoader, this.componentScanBeanNameGenerator, registry);

上面代码就是利用前面设置的一些信息去构建一个配置类的解析器。下面就开始真正的解析配置类了。

do {StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");// 解析配置类,会把每个BeanDefinitionHolder首先封装为ConfigurationClass// 在这个过程中会进行扫描、导入等步骤,从而会找到其他的ConfigurationClass// 解析配置类的结果是什么?parser.parse(candidates);  // AppConfig.class--->BeanDefinitionparser.validate();// configClasses相当于就是解析之后的结果Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// Read the model and create bean definitions based on its contentif (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}// 把所有的ConfigurationClass加载成BeanDefinition,通过情况下一个配置类会对应一个BeanDefinition,不过也有可能一个配置类对应多个BeanDefinition// 比如一个配置类中有多个@Bean,一个配置配置了@ImportResourcethis.reader.loadBeanDefinitions(configClasses);alreadyParsed.addAll(configClasses);processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();// candidates中存的是BeanDefinition,configClasses中存的是ConfigurationClasscandidates.clear();// 如果发现BeanDefinition增加了,则有可能增加了配置类if (registry.getBeanDefinitionCount() > candidateNames.length) {String[] newCandidateNames = registry.getBeanDefinitionNames();Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));Set<String> alreadyParsedClasses = new HashSet<>();for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}for (String candidateName : newCandidateNames) {if (!oldCandidateNames.contains(candidateName)) {BeanDefinition bd = registry.getBeanDefinition(candidateName);// 检查多出来的BeanDefinition是不是配置类,需不需要解析if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&!alreadyParsedClasses.contains(bd.getBeanClassName())) {candidates.add(new BeanDefinitionHolder(bd, candidateName));}}}	candidateNames = newCandidateNames;}}while (!candidates.isEmpty());

parser.parse(candidates); 这句代码将配置类传入到解析器中进行解析。

public void parse(Set<BeanDefinitionHolder> configCandidates) {for (BeanDefinitionHolder holder : configCandidates) {BeanDefinition bd = holder.getBeanDefinition();try {// 解析BeanDefinition所对应的类if (bd instanceof AnnotatedBeanDefinition) {parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());}else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());}else {parse(bd.getBeanClassName(), holder.getBeanName());}}catch (BeanDefinitionStoreException ex) {throw ex;}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);}}// 处理deferredImportSelectors,表示当前所有配置类解析完了之后才执行// deferredImportSelector表示推迟的ImportSelector,正常的ImportSelector是在解析配置类的过程中执行的this.deferredImportSelectorHandler.process();}

上面代码首先遍历配置类集合configCandidates中的每一个配置类,然后调用真正的解析方法parse开始解析每一个配置类。

	protected final void parse(@Nullable String className, String beanName) throws IOException {Assert.notNull(className, "No bean class name for configuration class bean definition");//拿到配置类的一些元信息MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);processConfigurationClass(new ConfigurationClass(reader, beanName), DEFAULT_EXCLUSION_FILTER);}

上面代码首先获取配置类的一些元信息,然后构建一个ConfigurationClass配置类作为参数调用processConfigurationClass开始真正解析配置类,我们进入该方法。

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {// 条件注解,就是看有没有类上是否有@Conditional注解,如果有,则进行条件匹配if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {return;}ConfigurationClass existingClass = this.configurationClasses.get(configClass);if (existingClass != null) {if (configClass.isImported()) {// OrderService导入了AccountService,UserService也导入了AccountService,就会符合这个条件if (existingClass.isImported()) {existingClass.mergeImportedBy(configClass);}// Otherwise ignore new imported config class; existing non-imported class overrides it.return;}else {// Explicit bean definition found, probably replacing an import.// Let's remove the old one and go with the new one.this.configurationClasses.remove(configClass);this.knownSuperclasses.values().removeIf(configClass::equals);}}// Recursively process the configuration class and its superclass hierarchy.SourceClass sourceClass = asSourceClass(configClass, filter);do {sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);}while (sourceClass != null);// ConfigurationClass重写了equals方法,只要两个ConfigurationClass对应的className相等就可以this.configurationClasses.put(configClass, configClass);}

上面方法的下面一段代码,其实递归解析父类,如果配置类继承了某个父类,它就会递归去解析父类。调用的核心方法是doProcessConfigurationClass

SourceClass sourceClass = asSourceClass(configClass, filter);
do {sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
}
while (sourceClass != null);@Nullableprotected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)throws IOException {//处理@Component注解if (configClass.getMetadata().isAnnotated(Component.class.getName())) {// Recursively process any member (nested) classes first// 处理内部类// 在解析一个配置类时,如果类上有@Component,则会判断内部类是不是lite配置类并进行解析,并且会记录为被导入的processMemberClasses(configClass, sourceClass, filter);}//处理@PropertySource注解//这段代码就是拿到我们的配置文件,然后解析配置文件放到SPring的环境变量中for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);}else {logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");}}// Process any @ComponentScan annotations// 会进行扫描,得到的BeanDefinition会注册到Spring容器中,并且会检查是不是配置类并进行解析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// 这里就会进行扫描,得到的BeanDefinition会注册到Spring容器中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 neededfor (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}// 检查扫描出来的BeanDefinition是不是配置类(full和lite)if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}// Process any @Import annotations// getImports(sourceClass)会拿到@Import导入的类// 如果导入的是普通类,那么会直接把它当做配置类来解析// 如果导入的是普通ImportSelector,那么会将返回的类再次调用processImports()// 如果导入的是特殊ImportSelector,DeferredImportSelector,那么暂时不会处理,会在解析完所有当前这轮配置类后进行导入,将返回的类再次调用processImports()// 如果导入的是ImportBeanDefinitionRegistrar,那么暂时不会处理,会在解析完所有当前这轮配置类后,将配置类解析成为BeanDefinition之后进行调用processImports(configClass, sourceClass, getImports(sourceClass), filter, true);// Process any @ImportResource annotationsAnnotationAttributes 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// 解析配置类中的@Bean,但并没有真正处理@Bean,只是暂时找出来Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}// Process default methods on interfaces// 解析配置类所实现的接口中的@Bean,但并没有真正处理@Bean,只是暂时找出来processInterfaces(configClass, sourceClass);// Process superclass, if anyif (sourceClass.getMetadata().hasSuperClass()) {String superclass = sourceClass.getMetadata().getSuperClassName();if (superclass != null && !superclass.startsWith("java") &&!this.knownSuperclasses.containsKey(superclass)) {this.knownSuperclasses.put(superclass, configClass);// Superclass found, return its annotation metadata and recursereturn sourceClass.getSuperClass();}}// No superclass -> processing is completereturn null;}

我们下面开始详细分析上面的代码,看看我们的配置类在Spring的底层到底是怎么解析的。

@Component
public class AppConfig {}

上面我们的配置类只加了一个@Component注解,在上面方法中由下面代码解析这个注解。

if (configClass.getMetadata().isAnnotated(Component.class.getName())) {// Recursively process any member (nested) classes first// 处理内部类// 在解析一个配置类时,如果类上有@Component,则会判断内部类是不是lite配置类并进行解析,并且会记录为被导入的processMemberClasses(configClass, sourceClass, filter);}

上面代码首先判断当前配置类有没有@Component注解,如果有就会调用processMemberClasses方法。

private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,Predicate<String> filter) throws IOException {//首先获取内部类Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();if (!memberClasses.isEmpty()) {List<SourceClass> candidates = new ArrayList<>(memberClasses.size());for (SourceClass memberClass : memberClasses) {// 判断内部类是不是lite配置类,如果是就将配置类加入到candidates集合中等待解析if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {candidates.add(memberClass);}}//重新对配置类候选集合candidates进行排序OrderComparator.sort(candidates);//这个for循环就是解决了外部类和内部类循环Import的一种情况for (SourceClass candidate : candidates) {// AppConfig中有一个内部类A, A上用@Import导入AppConfig.class,就出现了循环importif (this.importStack.contains(configClass)) {// 就是直接抛异常this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));}else {this.importStack.push(configClass);try {processConfigurationClass(candidate.asConfigClass(configClass), filter);}finally {this.importStack.pop();}}}}}

根据上面代码我们可以知道,如果一个类加了@Component注解,Spring在解析配置类时,会拿出这种类的所有内部类,然后按照前面判断一个类是否时内部类的标准去判断内部类是否时配置类。所以这里就可以衍生出配置类的一种新的写法。

@Component
public class AppConfig {@Configurationclass user{}
}

回到doProcessConfigurationClass方法。

for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);}else {logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");}}

上面代码就是用来处理@PropertySource注解,如果一个配置类上有这个注解,他就是拿出对应的配置文件,然后解析配置文件,将一些配置导入到Spring的环境变量Enviroment中。这里的解析配置文件的底层细节我们就不看了,继续看doProcessConfigurationClass的代码。

// 会进行扫描,得到的BeanDefinition会注册到Spring容器中,并且会检查是不是配置类并进行解析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// 这里就会进行扫描,得到的BeanDefinition会注册到Spring容器中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 neededfor (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}// 检查扫描出来的BeanDefinition是不是配置类(full和lite)if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}

上面代码就是重头戏,处理@ComponentScan注解,BeanDefinition的扫描过程就是这里面完成的。它会调用this.componentScanParser.parse这句代码,即扫描器去扫描所有的BeanDefinition,底层调用的方法就是scan方法,scan方法里面调用了doScan方法,这两个方法我们在前面Bean的生命周期中源码已经看过了,这里就不看了。然后扫描后会得到一个scannedBeanDefinitions,存放BeanDefinition的集合,类型是BeanDefinitionHolder,前面说吗这个累其实就是BeanDefinition和BeanName绑定到一起了。然后会判断扫描出来的BeanDefinition是不是配置类。如果是配置类我们会调用parse方法去解析,过程和上面一样。所以我们又可以衍生一种配置类写法。

@Component
public class BService {@Beanpublic AService aService(){return new AService();}
}

继续看doProcessConfigurationClass的代码。然后就是下面者句代码

// Process any @Import annotations// getImports(sourceClass)会拿到@Import导入的类// 如果导入的是普通类,那么会直接把它当做配置类来解析// 如果导入的是普通ImportSelector,那么会将返回的类再次调用processImports()// 如果导入的是特殊ImportSelector,DeferredImportSelector,那么暂时不会处理,会在解析完所有当前这轮配置类后进行导入,将返回的类再次调用processImports()// 如果导入的是ImportBeanDefinitionRegistrar,那么暂时不会处理,会在解析完所有当前这轮配置类后,将配置类解析成为BeanDefinition之后进行调用processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

上面这句代码就是用来解析@Import注解的。首先我们调用getImports方法,拿到注解中的信息。然后调用
processImports

	private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {Set<SourceClass> imports = new LinkedHashSet<>();Set<SourceClass> visited = new LinkedHashSet<>();collectImports(sourceClass, imports, visited);return imports;}private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection<SourceClass> importCandidates, Predicate<String> exclusionFilter,boolean checkForCircularImports) {if (importCandidates.isEmpty()) {return;}if (checkForCircularImports && isChainedImportOnStack(configClass)) {this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));}else {this.importStack.push(configClass);try {//遍历import注解中导入的每一个类for (SourceClass candidate : importCandidates) {// 如果import的类实现了ImportSelector接口if (candidate.isAssignable(ImportSelector.class)) {// Candidate class is an ImportSelector -> delegate to it to determine importsClass<?> candidateClass = candidate.loadClass();ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,this.environment, this.resourceLoader, this.registry);Predicate<String> selectorFilter = selector.getExclusionFilter();if (selectorFilter != null) {exclusionFilter = exclusionFilter.or(selectorFilter);}// 如果import的是DeferredImportSelector,表示推迟导入//if (selector instanceof DeferredImportSelector) {this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);} else {// 如果import的是普通的ImportSelectorString[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());// 继续处理selectImports()所返回的类Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);}}// 如果import的类实现了ImportBeanDefinitionRegistrar接口else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {// Candidate class is an ImportBeanDefinitionRegistrar ->// delegate to it to register additional bean definitionsClass<?> candidateClass = candidate.loadClass();ImportBeanDefinitionRegistrar registrar =ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,this.environment, this.resourceLoader, this.registry);configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());}// 如果import的类就是普通的类else {// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->// process it as an @Configuration classthis.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());// 注意,在asConfigClass方法中,不仅会将candidate生成一个ConfigurationClass,还会记录一下candidate是被哪个类导入的importedByprocessConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);}}}catch (BeanDefinitionStoreException ex) {throw ex;}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to process import candidates for configuration class [" +configClass.getMetadata().getClassName() + "]", ex);}finally {this.importStack.pop();}}}

上面就是解析@Import注解的源码,在解析之前它会拿到我们@Import注解中导入的所有类的信息,然后遍历,在处理这些导入类的时候,它分为了三中情况:

  • 类实现了ImportSelector接口
  • 类实现了ImportBeanDefinitionRegistrar接口
  • 类只是一个普通类

考虑下面这一种情况:

@Import(AService.class)
public class AppConfig {}public class AService  {@Beanpublic BService aService(){return new BService();}
}

上面@Import导入的就是一个普通的类,所以会进入下面这段代码进行处理:

else {this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
// 注意,在asConfigClass方法中,不仅会将candidate生成一个ConfigurationClass,还会记录一下candidate是被哪个类导入的importedByprocessConfigurationClass(candidate.asConfigClass(configClass), exclusionFilter);}

上面代码首先调用candidate.asConfigClass(configClass)将我们导入进来的类直接作为配置类,然后传递给processConfigurationClass,作为参数进行调用。

然后考虑下面这一种情况:

@Import(AService.class)
public class AppConfig {}public class AService  implements ImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {return new String[0];}
}

由于导入的类实现了ImportSelector接口,所以它会执行下面的逻辑:

if (candidate.isAssignable(ImportSelector.class)) {// Candidate class is an ImportSelector -> delegate to it to determine importsClass<?> candidateClass = candidate.loadClass();ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,this.environment, this.resourceLoader, this.registry);Predicate<String> selectorFilter = selector.getExclusionFilter();if (selectorFilter != null) {exclusionFilter = exclusionFilter.or(selectorFilter);}// 如果import的是DeferredImportSelector,表示推迟导入//if (selector instanceof DeferredImportSelector) {this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);} else {// 如果import的是普通的ImportSelectorString[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());// 继续处理selectImports()所返回的类Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, exclusionFilter);processImports(configClass, currentSourceClass, importSourceClasses, exclusionFilter, false);}}

上面代码首先会调用ImportSelector selector = ParserStrategyUtils.instantiateClass (candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry);将我们导入的类进行实例化然后转换成一个ImportSelector类型的实例对象。然后执行重写的selectImports方法。然后将该方法返回的类型,封装成一个importClassNames集合,然后递归调用processImports方法,再次进行处理。(实际上就是将selectImports方法返回的类作为导入进来的类,实现了这个接口的类可以作为批量导入的导入点)。

分析最后一种情况:

@Import(AService.class)
public class AppConfig {}
public class AService  implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {//该方法也可以用来注册BeanDefinition(Mybatis用到了这个)}
}

那么在processImports方法会执行下面这段逻辑:

else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {// Candidate class is an ImportBeanDefinitionRegistrar ->// delegate to it to register additional bean definitionsClass<?> candidateClass = candidate.loadClass();ImportBeanDefinitionRegistrar registrar =ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,this.environment, this.resourceLoader, this.registry);configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());}

上面代码同样将我们的导入类进行实例化然后转换为一个ImportBeanDefinitionRegistrar对象,但这里并没有调用重写方法,而是configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata())通过这句代码,将这个对象加入到当前配置类的importBeanDefinitionRegistrars属性集合上。

void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);}

上面代码@Import的处理过程就分析完了。接着回到doProcessConfigurationClass方法。

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

处理完@Import注解就开始处理@ImportSource注解。使用这个注解我们可以直接导入一个xml文件,这里只是拿到了那个xml路径,然后加入到了当前配置类的importedResources属性中,并没有真正解析xml文件。然后继续看doProcessConfigurationClass方法。

// Process individual @Bean methods// 解析配置类中的@Bean,但并没有真正处理@Bean,只是暂时找出来Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}

上面就拿到了配置类中所有加了@Bean注解的方法,然后将这些方法封装到了当前配置类的beanMethod属性中。

// 解析配置类所实现的接口中的@Bean,但并没有真正处理@Bean,只是暂时找出来processInterfaces(configClass, sourceClass);// Process superclass, if anyif (sourceClass.getMetadata().hasSuperClass()) {String superclass = sourceClass.getMetadata().getSuperClassName();if (superclass != null && !superclass.startsWith("java") &&!this.knownSuperclasses.containsKey(superclass)) {this.knownSuperclasses.put(superclass, configClass);// Superclass found, return its annotation metadata and recursereturn sourceClass.getSuperClass();}}

最后就是解析当前配置类实现的接口中是否有@Bean注解,如果有父类,就会返回这个父类(因为前面收到在解析配置类的过程中会递归解析父类),如果没有直接返回null即可,至此doProcessConfigurationClass方法就执行完毕了。

在这里插入图片描述

回到processConfigBeanDefinitions方法,前面我们调用parser.parse(candidates);将所有配置类已经解析完毕了

do {StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");// 解析配置类,会把每个BeanDefinitionHolder首先封装为ConfigurationClass// 在这个过程中会进行扫描、导入等步骤,从而会找到其他的ConfigurationClass// 解析配置类的结果是什么?parser.parse(candidates);  // AppConfig.class--->BeanDefinitionparser.validate();// configClasses相当于就是解析之后的结果Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// Read the model and create bean definitions based on its contentif (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}// 把所有的ConfigurationClass加载成BeanDefinition,通过情况下一个配置类会对应一个BeanDefinition,不过也有可能一个配置类对应多个BeanDefinition// 比如一个配置类中有多个@Bean,一个配置配置了@ImportResourcethis.reader.loadBeanDefinitions(configClasses);alreadyParsed.addAll(configClasses);processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();// candidates中存的是BeanDefinition,configClasses中存的是ConfigurationClasscandidates.clear();// 如果发现BeanDefinition增加了,则有可能增加了配置类if (registry.getBeanDefinitionCount() > candidateNames.length) {String[] newCandidateNames = registry.getBeanDefinitionNames();Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));Set<String> alreadyParsedClasses = new HashSet<>();for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}for (String candidateName : newCandidateNames) {if (!oldCandidateNames.contains(candidateName)) {BeanDefinition bd = registry.getBeanDefinition(candidateName);// 检查多出来的BeanDefinition是不是配置类,需不需要解析if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&!alreadyParsedClasses.contains(bd.getBeanClassName())) {candidates.add(new BeanDefinitionHolder(bd, candidateName));}}}	candidateNames = newCandidateNames;}}while (!candidates.isEmpty());

解析完毕后,所有配置类的解析结果会放在Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());这个集合中。然后我们分析一下这句代码this.reader.loadBeanDefinitions(configClasses);,这句代码的作用是加载BeanDefininition,但我们知道在doProcessConfigurationClass方法中处理@ComponentScan注解的时候,我们意见解析完了所有Bean,但这里为上面还有加载bean,大家是否还记得前面我们在解析Import的时候,将解析到的ImportBeanDefinitionRegistrars对象放入到了当前配置类的importBeanDefinitionRegistrars集合中,以及在解析@importReSrouce注解时,将一个xml配置文件记录到了当前配置类的importedResources中,以及在解析@Bean注解时,将所有的BeanMethod加入到了当前配置类的beanMethod,这三个属性是都有可能产生额外的bean的,所以我们需要对这些额外的bean进行加载,而这个过程就是在this.reader.loadBeanDefinitions这个方法中实现的。

	public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();for (ConfigurationClass configClass : configurationModel) {loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);}}

上面方法就会遍历已经解析过的配置类,然后调用loadBeanDefinitionsForConfigurationClass方法。

private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {if (trackedConditionEvaluator.shouldSkip(configClass)) {String beanName = configClass.getBeanName();if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {this.registry.removeBeanDefinition(beanName);}this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());return;}if (configClass.isImported()) {// 将被导入的类生成BeanDefinition并注册到Spring容器中// @Component的内部类,@Import所导入的类都是被导入的类registerBeanDefinitionForImportedConfigurationClass(configClass);}// @Bean生成BeanDefinition并注册for (BeanMethod beanMethod : configClass.getBeanMethods()) {loadBeanDefinitionsForBeanMethod(beanMethod);}// 处理@ImportResource("spring.xml")loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());// 处理ImportBeanDefinitionRegistrar,调用registerBeanDefinitions()方法loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());}

首先执行下面这段代码:

for (BeanMethod beanMethod : configClass.getBeanMethods()) {loadBeanDefinitionsForBeanMethod(beanMethod);}

这里就是获得beanmethod的属性,然后执行loadBeanDefinitionsForBeanMethod方法,调用beanmethod去注册beandefinition到容器中。

同样loadBeanDefinitionsFromImportedResources就是解析xml,将所有的beandefinition注册到容器中,loadBeanDefinitionsFromRegistrars就会拿到ImportBeanDefinitionRegistrars方法,然后调用它的registerBeanDefinitions方法。但我们注意一个问题,我们新导入的beandefinition会不会也是配置类,所以回到processConfigBeanDefinitions方法中的下面这段代码:

do {StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");// 解析配置类,会把每个BeanDefinitionHolder首先封装为ConfigurationClass// 在这个过程中会进行扫描、导入等步骤,从而会找到其他的ConfigurationClass// 解析配置类的结果是什么?parser.parse(candidates);  // AppConfig.class--->BeanDefinitionparser.validate();// configClasses相当于就是解析之后的结果Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// Read the model and create bean definitions based on its contentif (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}// 把所有的ConfigurationClass加载成BeanDefinition,通过情况下一个配置类会对应一个BeanDefinition,不过也有可能一个配置类对应多个BeanDefinition// 比如一个配置类中有多个@Bean,一个配置配置了@ImportResourcethis.reader.loadBeanDefinitions(configClasses);alreadyParsed.addAll(configClasses);processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();// candidates中存的是BeanDefinition,configClasses中存的是ConfigurationClasscandidates.clear();// 如果发现BeanDefinition增加了,则有可能增加了配置类if (registry.getBeanDefinitionCount() > candidateNames.length) {String[] newCandidateNames = registry.getBeanDefinitionNames();Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));Set<String> alreadyParsedClasses = new HashSet<>();for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}for (String candidateName : newCandidateNames) {if (!oldCandidateNames.contains(candidateName)) {BeanDefinition bd = registry.getBeanDefinition(candidateName);// 检查多出来的BeanDefinition是不是配置类,需不需要解析if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&!alreadyParsedClasses.contains(bd.getBeanClassName())) {candidates.add(new BeanDefinitionHolder(bd, candidateName));}}}	candidateNames = newCandidateNames;}}while (!candidates.isEmpty());

if (registry.getBeanDefinitionCount() > candidateNames.length)会判断是不是新增加了BeanDefinition,如果是ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactor这句代码就判断新增加的BeanDefinition是不是新的配置类,如果是就加入到candidates这个未解析的配置类集合中(这个集合前面已经清空了,避免重复解析),然后再次执行上面的所有逻辑,解析配置类,这也是为什么这里用do…while循环的原因。这里分析一个奇怪的现象,我先给出一个代码:


public class AppConfig {@Beanpublic AppConfig1 appConfig1() {return new AppConfig1();}
}public class AppConfig1 {@Beanpublic AService aService(){return new AService();}
}public static void main(String[] args) {AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();applicationContext.register(AppConfig.class);applicationContext.refresh();Object aService = applicationContext.getBean("aService");applicationContext.close();}

在这里插入图片描述
我们发现拿不到AService这个bean,如果我们把AppConfig1改为配置文件xml定义就可以拿到,这是为什么呢。其实这是Spring底层偷了个懒,ConfigurationClassUtils.checkConfigurationClassCandidate这个函数,检查新增的bean是否是配置类时,在获取@Bean而新增bean得名字时会为null,所以checkConfigurationClassCandidate方法中的这句代码,就会直接返回false,判断当前bean不为配置类。

	// @Bean定义的配置类Bean是不起作用的String className = beanDef.getBeanClassName();if (className == null || beanDef.getFactoryMethodName() != null) {return false;}

上面时补充的一个当前spring版本的一个缺陷。至此ConfigurationClassPostProcessorpostProcessBeanDefinitionRegistry方法就分析完了,配置类的解析,以及BeanDefinition的扫描和注册源码就解析完了。

2. BeanDefinition覆盖问题

  • @Component名字相同
@Component("Aservice")
public class AService  {}@Component("Aservice")
public class BService {}

在这里插入图片描述

直接报错,这里我们在看Bean生命周期源码的时候,在分析doScan方法时讨论过BeanDefiniiton的兼容性问题。

  • @Bean注解名字相同
@ComponentScan("com.zhouyu")
@EnableScheduling
@PropertySource("classpath:spring.properties")
@EnableTransactionManagement
public class AppConfig {@Beanpublic AService aService(){return new AService();}@Beanpublic AService aService(BService bService){return new AService();}}

不会报错,这里前面在分析methodbean得时候,在解析methodbean生成beanDefinition,它只会使用一个beanMethod生成BeanDefinition,其它的都不会生效。

  • @Bean和@Component名字相同

这里会覆盖,由于@Component先解析,然后解析@Bean,分析源码可以看到@Bean的会覆盖@Component(如果当前Spring是允许覆盖的)。

3. full配置类和lite配置类的区别

前面ConfigurationClassPostProcessorpostProcessBeanDefinitionRegistry方法已经分析完了,现在我们进入ConfigurationClassPostProcessorpostProcessBeanFactory方法。

@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {int factoryId = System.identityHashCode(beanFactory);if (this.factoriesPostProcessed.contains(factoryId)) {throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + beanFactory);}this.factoriesPostProcessed.add(factoryId);if (!this.registriesPostProcessed.contains(factoryId)) {// BeanDefinitionRegistryPostProcessor hook apparently not supported...// Simply call processConfigurationClasses lazily at this point then.processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);}// 增强配置类,代理加了Configuration注解的配置类enhanceConfigurationClasses(beanFactory);beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));}

上面enhanceConfigurationClasses就增强了我们的配置类。

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {StartupStep enhanceConfigClasses = this.applicationStartup.start("spring.context.config-classes.enhance");Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<>();// 遍历,找出配置类for (String beanName : beanFactory.getBeanDefinitionNames()) {BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);Object configClassAttr = beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE);AnnotationMetadata annotationMetadata = null;MethodMetadata methodMetadata = null;if (beanDef instanceof AnnotatedBeanDefinition) {AnnotatedBeanDefinition annotatedBeanDefinition = (AnnotatedBeanDefinition) beanDef;annotationMetadata = annotatedBeanDefinition.getMetadata();methodMetadata = annotatedBeanDefinition.getFactoryMethodMetadata();}if ((configClassAttr != null || methodMetadata != null) && beanDef instanceof AbstractBeanDefinition) {// Configuration class (full or lite) or a configuration-derived @Bean method// -> eagerly resolve bean class at this point, unless it's a 'lite' configuration// or component class without @Bean methods.AbstractBeanDefinition abd = (AbstractBeanDefinition) beanDef;// 没有加载类就去加载if (!abd.hasBeanClass()) {boolean liteConfigurationCandidateWithoutBeanMethods =(ConfigurationClassUtils.CONFIGURATION_CLASS_LITE.equals(configClassAttr) &&annotationMetadata != null && !ConfigurationClassUtils.hasBeanMethods(annotationMetadata));if (!liteConfigurationCandidateWithoutBeanMethods) {try {abd.resolveBeanClass(this.beanClassLoader);}catch (Throwable ex) {throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);}}}}if (ConfigurationClassUtils.CONFIGURATION_CLASS_FULL.equals(configClassAttr)) {if (!(beanDef instanceof AbstractBeanDefinition)) {throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +beanName + "' since it is not stored in an AbstractBeanDefinition subclass");}else if (logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {logger.info("Cannot enhance @Configuration bean definition '" + beanName +"' since its singleton instance has been created too early. The typical cause " +"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +"return type: Consider declaring such methods as 'static'.");}configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);}}if (configBeanDefs.isEmpty() || NativeDetector.inNativeImage()) {// nothing to enhance -> return immediatelyenhanceConfigClasses.end();return;}// 生成代理类,并设置到BeanDefinition中  Full ConfigConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {AbstractBeanDefinition beanDef = entry.getValue();// If a @Configuration class gets proxied, always proxy the target classbeanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);// Set enhanced subclass of the user-specified bean classClass<?> configClass = beanDef.getBeanClass();  // Appconfig// 生成代理类Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);if (configClass != enhancedClass) {if (logger.isTraceEnabled()) {logger.trace(String.format("Replacing bean definition '%s' existing class '%s' with " +"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));}beanDef.setBeanClass(enhancedClass);}}enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end();}

上面代码的核心逻辑就是将full 配置类生成一个代理对象,这时候full配置类在容器中对应的bean对象都会是代理对象,在我们使用beanmethod实际去创建bean对象的时候,就会由代理对象去调用这些beanmethod去创建bean。在doCreatebean创建bean的时候,@Bean的beanMethod创建bean时会由一个factoryBean去创建这个factoryBean就是full配置类代理的那个bean。

在这里插入图片描述
下面给出一个小的案例更好的理解:

@Configuration(proxyBeanMethods = false) //此时是lite
public class AppConfig {@Beanpublic AService aService(){System.out.println(bService());System.out.println(bService());return new AService();}@Beanpublic BService bService(){return new BService();}}

此时aService打印的两个bService不同
在这里插入图片描述
这是为什么啊,这是因为在lite模式下,当前的配置类没有代理对象,所以aService直接调用了bService方法创建了两个不同的对象。但是在full模式下,在代理对象执行某个方法的时候,会有一个拦截器拦截判断当前执行的方法是否是一个创建bean的方法,显然在System.out.println(bService());执行这句代码的时候,不是在创建bean对象,所以代理逻辑会让它直接getbean()而是调用bService()去创建一个新的对象。而当前确实是工厂方法在调用bService创建对象,它就会真正的创建对象。这里还是比较难理解的,感兴趣可以结合源码分析一下。

@Configuration(proxyBeanMethods = true) //此时是lite
public class AppConfig {@Beanpublic AService aService(){System.out.println(bService());System.out.println(bService());return new AService();}@Beanpublic BService bService(){return new BService();}}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/610913.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

乐鑫ESP32与SD NAND的协同应用|MK-米客方德

SD NAND在乐鑫ESP32上的作用 SD NAND是贴片式TF卡&#xff0c;可以用于存储数据&#xff0c;比如视频图片或者代码 乐鑫ESP32一颗具有双核处理器的嵌入式系统芯片&#xff0c;有丰富的外设接口&#xff0c;包括Wi-Fi、蓝牙、UART、SPI、I2C等&#xff0c;使其适用于各种物联网…

加工零件的题解

目录 原题描述&#xff1a; 题目描述 输入格式 输出格式 样例 #1 样例输入 #1 样例输出 #1 样例 #2 样例输入 #2 样例输出 #2 提示 题目大意&#xff1a; 主要思路&#xff1a; 但是我们怎么才能判断出x走到1时L是偶数还是奇数呢&#xff1f; 初始化&#xff1a;…

vim编辑器

1.vi编辑器介绍 vim是全屏幕纯屏幕纯文本编辑器, 是vi编辑器的增强版. 可以利用别名让输入vi命令时, 实际上执行vim编辑器, 例如: alias vi "vim" #定义别名 这样定义的别名是临时生效, 如果需要永久生效, 请放入环境变量配置文件(~/.bashrc) 2.vim基本的使用 …

如何压缩照片?分享4种全面的方法!

随着智能手机的普及&#xff0c;我们每个人都成为了摄影爱好者。然而&#xff0c;随着拍摄的照片越来越多&#xff0c;手机内存也逐渐变得捉襟见肘。那么&#xff0c;如何有效地压缩照片&#xff0c;释放手机内存呢&#xff1f;今天&#xff0c;我们就来为你揭秘那些可以轻松压…

前端浏览器滚动条炫酷美化

一、文章引导 #mermaid-svg-2qRndEP8CFzlUhap {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-2qRndEP8CFzlUhap .error-icon{fill:#552222;}#mermaid-svg-2qRndEP8CFzlUhap .error-text{fill:#552222;stroke:#55222…

多模态+SNN个人学习历程和心得

祖传开头 这次想写一个一直深藏心中的研究方向&#xff0c;那就是多模态方向。其实当初在实验室那会儿&#xff0c;最先接触的就是多模态的工作&#xff0c;因此这是我科研之路的起点。只不过&#xff0c;后来经历了一些波折&#xff0c;导致个人没有往这个方向深挖&#xff0…

PayPal账号被关联!跨境卖家如何自救?关于PayPal防关联你不得不知道的事!

很多跨境卖家的支付平台都会选择 PayPal&#xff0c;但是 PayPal 账号在使用过程中也经常会遇见关联&#xff0c;风控等问题&#xff0c;这时候为了保护我们的 PayPal 账号安全&#xff0c;防关联就很重要。今天龙哥就分享一些关于 PayPal 防关联的内容&#xff0c;希望能够帮助…

重新认识canvas,掌握必要的联结密码

查看专栏目录 canvas示例教程100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

C++ continue语句

作用&#xff1a;在循环语句中&#xff0c;跳过本次循环中余下尚未执行的语句&#xff0c;继续执行下一次循环 与break的区别在于&#xff0c;如果把上述continue换位break&#xff0c;则该程序执行到break则会直接退出for循环&#xff0c;不再执行后面的代码和以后的循环。 实…

Web实战丨基于django+html+css+js的电子商务网站

文章目录 写在前面实验目标需求分析实验内容安装依赖库1.登陆界面2.注册界面3.电子商城界面4.其他界面 运行结果写在后面 写在前面 本期内容&#xff1a;基于DjangoHTMLCSSJS的电子商务网站 实验环境&#xff1a; vscode或pycharmpython(3.11.4)django 代码下载地址&#x…

【Kafka-3.x-教程】-【七】Kafka 生产调优、Kafka 压力测试

【Kafka-3.x-教程】专栏&#xff1a; 【Kafka-3.x-教程】-【一】Kafka 概述、Kafka 快速入门 【Kafka-3.x-教程】-【二】Kafka-生产者-Producer 【Kafka-3.x-教程】-【三】Kafka-Broker、Kafka-Kraft 【Kafka-3.x-教程】-【四】Kafka-消费者-Consumer 【Kafka-3.x-教程】-【五…

观成科技-加密C2框架EvilOSX流量分析

工具简介 EvilOSX是一款开源的&#xff0c;由python编写专门为macOS系统设计的C2工具&#xff0c;该工具可以利用自身释放的木马来实现一系列集成功能&#xff0c;如键盘记录、文件捕获、浏览器历史记录爬取、截屏等。EvilOSX主要使用HTTP协议进行通信&#xff0c;通信内容为特…

蓝凌EIS pdf.aspx 任意文件读取漏洞

漏洞描述&#xff1a; 蓝凌EIS智慧协同平台是一个简单、高效的工作方式专为成长型企业打造的沟通、协同、社交的移动办公平台&#xff0c;覆盖OA、沟通、客户、人事、知识等管理需求&#xff0c;集合了非常丰富的模块&#xff0c;满足组织企业在知识、项目管理系统建设等需求的…

jmeter循环控制器

1.循环控制器 简单粗暴 写几次 循环几次 经常结合自定义变量使用 2.foreach控制器 搭配 变量一起使用的循环 一般变量的值是一个集合或者 是2个及2个以上的内容

[中阶]1月29-2月2晚8点-软件需求设计方法学全程实例剖析

建模方法学包含以下技能&#xff1a; A-业务建模——定位需要改进的目标组织&#xff08;人群或机构&#xff09;以及该组织接下来最需要改进的问题。 B-需求——描述为了改进组织的问题&#xff0c;所引入的信息系统必须具有的表现。 C-分析——提炼为了满足功能需求&#…

《ORANGE’S:一个操作系统的实现》读书笔记(二十七)文件系统(二)

上一篇文章我们记录了如何操作硬盘&#xff0c;并且编写了简单的硬盘驱动程序用于获取一些硬盘的参数。这篇文章就在上一篇文章的基础上记录文件系统&#xff0c;完善硬盘驱动程序。 文件系统 现在我们该仔细考虑如何构建一个文件系统了。这并不是我们第一次接触文件系统&…

python 工作目录 与 脚本所在目录不一致

工作目录&#xff1a;执行脚本的地方 我以为工作目录会是当前执行脚本的目录位置&#xff0c;但其实不是&#xff0c;例如&#xff1a; 图中红色文件为我执行的脚本文件&#xff0c;但是实际的工作目录是PYTHON LEARNING 可以用如下代码查询当前工作目录&#xff1a; import os…

dubbo的springboot集成

1.什么是dubbo&#xff1f; Apache Dubbo 是一款 RPC 服务开发框架&#xff0c;用于解决微服务架构下的服务治理与通信问题&#xff0c;官方提供了 Java、Golang 等多语言 SDK 实现。使用 Dubbo 开发的微服务原生具备相互之间的远程地址发现与通信能力&#xff0c; 利用 Dubbo …

【三】把Python Tk GUI打包exe可执行程序,移植到其他机器可用

背景 这是一个系列文章。上一篇【【二】为Python Tk GUI窗口添加一些组件和绑定一些组件事件-CSDN博客】 使用python脚本写一个小工具。因为命令行运行的使用会有dos窗口&#xff0c;交互也不是很方便&#xff0c;开发环境运行也不方便分享给别人用&#xff0c;所以想到…

ubantu中的docker安装

1.Ubuntu Docker 安装 | 菜鸟教程 (runoob.com) 我就是看这个教程进行操作的 2.执行下面两步&#xff0c;就算是安装完成了 3.启动&#xff0c;并检查是否安装成功&#xff1a; 4.安装之后&#xff0c;怎么用&#xff0c;那就是自己随便探索咯&#xff0c;可以看博客&#xf…