Spring源码深度解析:六、ConfigurationClassPostProcessor
创始人
2024-02-08 14:12:19

在这里插入图片描述

一、前言

文章目录:Spring源码分析:文章目录

ConfigurationClassPostProcessor是非常重要的一个 后处理器。 ConfigurationClassPostProcessor完成了 配置类的解析和保存以及@Component注解、@Import等注解的解析工作 。将所有需要注入的bean解析成BeanDefinition保存到BeanFactory中。
在这里插入图片描述
首先来讲解一下ConfigurationClassPostProcessor的结构图如下。
可见ConfigurationClassPostProcessor接口实现了BeanDefinitionRegistryPostProcessor(BeanFactory的后处理器)
PriorityOrdered(设置自己的优先级为最高) 和各种Aware接口。

Springboot启动后,会通过SpringApplication#createApplicationContext()来创建应用上下文,默认请情况下我们一般创建AnnotationConfigServletWebServerApplicationContext作为应用上下文。而在AnnotationConfigServletWebServerApplicationContext构造函数中会创建AnnotatedBeanDefinitionReader。而在AnnotatedBeanDefinitionReader构造函数中会调用 AnnotationConfigUtils#registerAnnotationConfigProcessors(this.registry);,该方法将一些必要Bean(如ConfigurationClassPostProcessorAutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor等)注入到了容器中。

我们这里重点看的是BeanDefinitionRegistryPostProcessor接口的两个方法:

// 完成对 @Bean 方法的代理
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
// 允许在Spring容器启动后,在下一个阶段开始前,添加BeanDefinition的定义
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

关于这两个方法的调用时机和作用,我们在之前的文章(Spring源码分析五:BeanFactoryPostProcessor的处理 - invokeBeanFactoryPostProcessors)已经讲过,调用过程主要是在 Spring容器刷新的过程中,其中postProcessBeanDefinitionRegistry方法先于 postProcessBeanFactory方法被调用。

通过本篇分析ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()方法。得知了ConfigurationClassPostProcessor解析配置类(这里的配置类不仅仅局限于@Configuration 注解,还包括 @Import、 @ImportResource等注解),将解析到的需要注入到Spring容器中的bean的BeanDefinition保存起来。在后面的bean 初始化都需要BeanDefinition
ConfigurationClassPostProcessor#postProcessBeanFactory方法通过cglib代理配置类,来拦截@Bean修饰的方法。这么做的目的是为了在配置类中多次调用@Bean方法返回的是同一个结果。即在下面的代码中demoController()demoController2()方法中调用的demoService()方法返回的结果是同一个值。避免了单例模式下的多例创建。

二 、入口 - invokeBeanFactoryPostProcessors()

首先Spring容器的启动我们debug进入的是Spring的容器刷新方法:refresh(),接着我们F7进入子方法invokeBeanFactoryPostProcessors()该方法就是激活各种BeanFactory后处理器(调用在上下文中注册为bean的工厂处理器(BeanFactoryPostProcessor))。具体代码如下:
AbstractApplicationContext#invokeBeanFactoryPostProcessors()

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {// getBeanFactoryPostProcessors方法获取了所有硬编码的bean工厂处理器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)// 默认情况下这里判断不会为空,在refresh方法调用的prepareBeanFactory方法内已经执行过这段代码了if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {// 添加bean后置处理器,负责调用实现了LoadTimeWeaverAware接口setLoadTimeWeaver方法的beanbeanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));// 添加一个临时类加载器beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}}

invokeBeanFactoryPostProcessors()>>> PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors()

public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) {// Invoke BeanDefinitionRegistryPostProcessors first, if any.// 记录所有即将或已经处理的beanName,用于防止重复处理Set processedBeans = new HashSet<>();// 对BeanDefinitionRegistry类型的处理,这里是交由BeanDefinitionRegistryPostProcessor来处理// 这里判断BeanFactory如果是BeanDefinitionRegistry子类 则需要进行BeanDefinitionRegistryPostProcessor的处理,否则直接按照BeanFactoryPostProcessor处理即可。// 关于为什么BeanDefinitionRegistry比较特殊上面也说过,因为BeanDefinitionRegistryPostProcessor只能处理BeanDefinitionRegistry的子类,所以这里需要区分是否是BeanDefinitionRegistry类型if (beanFactory instanceof BeanDefinitionRegistry) {// 下面逻辑看似复杂,其实就两步:// 1. 获取所有硬编码的BeanDefinitionRegistryPostProcessor类型,激活postProcessBeanDefinitionRegistry方法// 2. 获取所有配置的BeanDefinitionRegistryPostProcessor,激活postProcessBeanDefinitionRegistry方法BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;// 记录通过硬编码方式注册的BeanFactoryPostProcessor类型的处理器List regularPostProcessors = new ArrayList<>();// 记录所有注册的BeanDefinitionRegistryPostProcessor类型的处理器(含有硬编码注册的和配置注入注册的)List registryProcessors = new ArrayList<>();// 遍历硬编码注册的后处理器(都保存AbstractApplicationContext#beanFactoryPostProcessors中,这里通过参数beanFactoryPostProcessors传递过来)for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {BeanDefinitionRegistryPostProcessor registryProcessor =(BeanDefinitionRegistryPostProcessor) postProcessor;registryProcessor.postProcessBeanDefinitionRegistry(registry);// 将硬编码注册BeanDefinitionRegistryPostProcessor放到registryProcessors中registryProcessors.add(registryProcessor);}else {regularPostProcessors.add(postProcessor);}}// Do not initialize FactoryBeans here: We need to leave all regular beans// uninitialized to let the bean factory post-processors apply to them!// Separate between BeanDefinitionRegistryPostProcessors that implement// PriorityOrdered, Ordered, and the rest.// 一个临时变量,记录通过配置方式注册的BeanDefinitionRegistryPostProcessor类型的处理器List currentRegistryProcessors = new ArrayList<>();// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.// 获取所有的实现了BeanDefinitionRegistryPostProcessor的beanNameString[] postProcessorNames =beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);// 筛选出实现了PriorityOrdered接口的实现类,优先执行for (String ppName : postProcessorNames) {if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {// 记录到currentRegistryProcessors中currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));processedBeans.add(ppName);}}// 进行排序sortPostProcessors(currentRegistryProcessors, beanFactory);// 以配置方式注册的且实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessor集合全部放到registryProcessors中registryProcessors.addAll(currentRegistryProcessors);// 激活 postProcessBeanDefinitionRegistry 方法invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);// 清空临时变量currentRegistryProcessors的内容currentRegistryProcessors.clear();// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);// 筛选出实现了Ordered接口的实现类,第二执行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);// 以配置方式注册的且实现了Ordered接口的BeanDefinitionRegistryPostProcessor集合全部放到registryProcessors中registryProcessors.addAll(currentRegistryProcessors);// 激活invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);// 清空临时变量currentRegistryProcessors的内容currentRegistryProcessors.clear();// Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.// 最后获取No-Ordered(没有实现排序)接口的 BeanDefinitionRegistryPostProcessor ,进行激活。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);// 将配置方式注册的且没有实现排序(No-Ordered)接口的BeanDefinitionRegistryPostProcessor集合全部放到registryProcessors中registryProcessors.addAll(currentRegistryProcessors);// 激活invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);// 清空临时变量currentRegistryProcessors的内容currentRegistryProcessors.clear();}// Now, invoke the postProcessBeanFactory callback of all processors handled so far.// 到这里,所有的 BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry()方法都已经激活结束。// 开始激活postProcessBeanFactory方法// 因为BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子类,所有这里激活的是BeanDefinitionRegistryPostProcessor的postProcessBeanFactory()方法invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);// regularPostProcessors中记录的是硬编码注入的BeanFactoryPostProcessorinvokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);}else {// Invoke factory processors registered with the context instance.// 如果 beanFactory instanceof BeanDefinitionRegistry = false,那么BeanDefinitionRegistryPostProcessor.postProcessBeanDefinitionRegistry()方法并不生效,就直接激活postProcessBeanFactory()方法即可。// 激活硬编码注册的BeanFactoryPostProcessor.postProcessBeanFactory()方法invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);}// 到这一步,所有的硬编码方式注入的后处理器都处理完毕; 配置注入的BeanDefinitionRegistryPostProcessor后处理器的postProcessBeanDefinitionRegistry()和postProcessBeanFactory()方法都已经激活。// 下面开始处理配置注入的BeanFactoryPostProcessor的postProcessBeanFactory后处理器。// Do not initialize FactoryBeans here: We need to leave all regular beans// uninitialized to let the bean factory post-processors apply to them!// 获取所有后处理器的beanName,用于后面处理String[] postProcessorNames =beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,// Ordered, and the rest.// 创建几个保存不同排序的集合,按照实现的排序接口调用// 筛选出实现了排序接口PriorityOrdered的类List priorityOrderedPostProcessors = new ArrayList<>();// 筛选出实现了排序接口Ordered的类List orderedPostProcessorNames = new ArrayList<>();// 筛选出未实现排序接口的类List 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.// 排序和激活(实现了PriorityOrdered接口的后处理器)sortPostProcessors(priorityOrderedPostProcessors, beanFactory);invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);// Next, invoke the BeanFactoryPostProcessors that implement Ordered.// 排序和激活(实现了Ordered接口的后处理器)List orderedPostProcessors = new ArrayList<>();for (String postProcessorName : orderedPostProcessorNames) {orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));}sortPostProcessors(orderedPostProcessors, beanFactory);invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);// Finally, invoke all other BeanFactoryPostProcessors.// 排序和激活(没有实现排序接口的后处理器)List nonOrderedPostProcessors = new ArrayList<>();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();}

从代码中我们可以看到激活 postProcessBeanDefinitionRegistry() 方法
invokeBeanFactoryPostProcessors() >>> invokeBeanDefinitionRegistryPostProcessors()

private static void invokeBeanDefinitionRegistryPostProcessors(Collection postProcessors, BeanDefinitionRegistry registry) {for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {postProcessor.postProcessBeanDefinitionRegistry(registry);}}

下面我们就来解析一下这个主要方法postProcessBeanDefinitionRegistry()

三、ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()

ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()

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

可以看到postProcessBeanDefinitionRegistry()方法中并没有处理什么逻辑,真正逻辑在其调用的 processConfigBeanDefinitions方法中

1、解析配置类的定义 - processConfigBeanDefinitions

processConfigBeanDefinitions 方法完成了关于配置类的所有解析。

需要注意的是,到达这一步的时候, Springboot 启动类已经被解析成BeanDefinition 注册到容器中。

具体代码如下:
ConfigurationClassPostProcessor#processConfigBeanDefinitions()

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {List configCandidates = new ArrayList<>();// 获取已经解析的BeanName。这里需要注意的是,Springboot的话,启动类已经被注册。具体的注册时机是在Springboot启动时候的SpringApplication#prepareContext方法中。String[] candidateNames = registry.getBeanDefinitionNames();// 遍历BeanNamefor (String beanName : candidateNames) {// 获取BeanDefinitionBeanDefinition beanDef = registry.getBeanDefinition(beanName);// 如果bean被解析过(Bean被解析后会在beanDef中设置属性CONFIGURATION_CLASS_ATTRIBUTE),if条件成立,这里是为了防止重复解析if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {if (logger.isDebugEnabled()) {logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);}}// 1. ConfigurationClassUtils.checkConfigurationClassCandidate 解析了当前bean是否是配置类,关于其详细内容,后面解析 需要注意的是,// 本文所说的配置类即使满足 full 或 lite 条件的类,而不仅仅是被 @Configuration 修饰的类。else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {// 添加到配置类集合中configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));}}// Return immediately if no @Configuration classes were found// 如果没有找到配置类,则直接返回,不需要下面的解析if (configCandidates.isEmpty()) {return;}// Sort by previously determined @Order value, if applicable// 按照@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 context// 判断如果是registry是SingletonBeanRegistry类型,则从中获取beanName生成器(BeanNameGenerator)。// 实际上这里是register类型是DefaultListableBeanFactory。是SingletonBeanRegistry的子类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;}}}// 如果环境变量为空则指定一个标准环境,这里是StandardServletEnvironment类型,在前面的启动篇我们可以知道。if (this.environment == null) {this.environment = new StandardEnvironment();}// Parse each @Configuration class// 下面开始解析每一个配置类// 准备配置类的解析类ConfigurationClassParserConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment,this.resourceLoader, this.componentScanBeanNameGenerator, registry);// 用来保存尚未解析的配置类Set candidates = new LinkedHashSet<>(configCandidates);// 用来保存已经解析的配置类Set alreadyParsed = new HashSet<>(configCandidates.size());// do..while 循环解析。因为一个配置类可能引入另一个配置类,需要循环解析,直至没有其他需要解析的类。do {// 2. 开始解析。后面详细分析parser.parse(candidates);// 3. 这里的校验规则是如果是被@Configuration修饰且proxyBeanMethods属性为true,则类不能为final。如果@Bean修饰的方法,则必须是可覆盖的.// 因为@Configuration(proxyBeanMethods = true) 是需要cglib代理的,所以不能为终态, @Bean所修饰的方法也有一套约束规则,下面详细讲// 是否需要代理是根据 类或方法上的 @Scope 注解指定的,默认都是不代理parser.validate();// configClasses保存这次解析出的配置类。此时这些ConfigurationClass中保存了解析出来的各种属性值,等待最后构建BeanDefinitionSet 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());}// 4. 注册beanthis.reader.loadBeanDefinitions(configClasses);alreadyParsed.addAll(configClasses);candidates.clear();// if条件如果成立,说明有新的bean注册了,则需要解析新的beanif (registry.getBeanDefinitionCount() > candidateNames.length) {// 获取新的beanNameString[] newCandidateNames = registry.getBeanDefinitionNames();Set oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));Set alreadyParsedClasses = new HashSet<>();for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}for (String candidateName : newCandidateNames) {if (!oldCandidateNames.contains(candidateName)) {// 过滤出未解析的bean检测是否是未解析过的配置类BeanDefinition bd = registry.getBeanDefinition(candidateName);if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&!alreadyParsedClasses.contains(bd.getBeanClassName())) {// 如果是未解析的配置类,则保存到candidates中candidates.add(new BeanDefinitionHolder(bd, candidateName));}}}candidateNames = newCandidateNames;}}// 如果 candidates 不为空,则说明有未被解析的配置类,循环解析。while (!candidates.isEmpty());// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes// 到这里已经把配置类解析完毕了。// 将ImportRegistry  注册为 bean,以支持ImportAware @Configuration 类if (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();}}

这里简单总结一下流程;

  1. 获取已经注册的Bean, 并筛选出配置类,按照@Order进行排序,得到配置类集合configCandidates
  2. 调用parser.parse(candidates);对配置类进行解析
  3. 调用this.reader.loadBeanDefinitions(configClasses);进行配置类的注册
  4. 检验registry.getBeanDefinitionCount() > candidateNames.length是否成立。这里由于第三步会将新解析出来的bean进行注册,如果这里成立,则说明有新的配置类完成了注册,获取到新注册的配置类candidateNames。循环从第二步重新解析,直到没有新注入的配置类。

上面解释的可能比较乱,因为我们下面详细去分析几个方法。

1.1 校验是否是配置类 - checkConfigurationClassCandidate()

ConfigurationClassUtils#checkConfigurationClassCandidate()

processConfigBeanDefinitions方法中。判断一个类是否是配置类就是通过checkConfigurationClassCandidate方法来判断的,那么我们需要看看这个方法中是怎么实现的。

在这个方法里,关键的部分是 给BeanDefinition设置了CONFIGURATION_CLASS_ATTRIBUTE为 full 或者 lite 设置这两个属性标识,如果一个类满足full或 lite的条件,则会被认为是配置类。需要注意的是,本文所说的配置类即使满足 full 或 lite 条件的类,而不仅仅是被@Configuration修饰的类

首先需要注意的是,在checkConfigurationClassCandidate()方法中,配置类的类型分为两种,Full 和 Lite,即完整的配置类和精简的配置类。

full 和 lite 设置的规则如下:

  • Full : 即类被 @Configuration 注解修饰 && proxyBeanMethods属性为true (默认为 true)
  • Lite : 被 @Component、@ComponentScan、@Import、@ImportResource 修饰的类 或者 类中有被@Bean修饰的方法。

Full 配置类就是我们常规使用的配置类

Lite 配置类就是一些需要其他操作引入一些bean 的类

下面我们来看具体代码:
ConfigurationClassUtils#checkConfigurationClassCandidate()

public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {// 获取classNameString className = beanDef.getBeanClassName();//如果beanDef名字为空,或者beanDef中工厂方法名字factoryMethodName不为空,则直接返回falseif (className == null || beanDef.getFactoryMethodName() != null) {return false;}// 解析关于当前被解析类的 注解元数据AnnotationMetadata metadata;// 如果当前BeanDefinition是AnnotatedBeanDefinition(相较于一般的BeanDefinition,他多了一些注解信息的解析)类型。直接获取注解元数据即可if (beanDef instanceof AnnotatedBeanDefinition &&className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {// Can reuse the pre-parsed metadata from the given BeanDefinition...//获取beanDef注解元数据metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();}else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {// Check already loaded Class if present...// since we possibly can't even load the class file for this Class./*** 	排除我们启动时注册的几个spring内部类(BeanFactoryPostProcessor、BeanPostProcessor、AopInfrastructureBean、EventListenerFactory),这几个内部类均为下面几个接口的实现或者扩展* 	第一次检查配置类会排除spring内部类,之后我们自定义的符合这几个接口的类不会进入逻辑,一般是AnnotatedBeanDefinition的实现* 	所有不会被排除*/Class beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();// 获取注解元数据metadata = new StandardAnnotationMetadata(beanClass, true);}else {// 按照默认规则解析try {MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);metadata = metadataReader.getAnnotationMetadata();}catch (IOException ex) {if (logger.isDebugEnabled()) {logger.debug("Could not find class file for introspecting configuration annotations: " + className, ex);}return false;}}/*** 该版本代码的和其他版本代码有点不同,我这边写出来了* // 获取Configuration注解信息* Map config = metadata.getAnnotationAttributes(Configuration.class.getName());*//*** 	// 1、如果该beanDef存在@Configuration,并且@Configuration中proxyBeanMethods为true,则设置为CONFIGURATION_CLASS_FULL:full,即全注解* 	// 2、proxyBeanMethods默认为true,即对于全注解配置类中的@Bean方法生成bean时会通过代理子类也即cglib来生成bean,而不是直接调用@Bean方法,主要时在子类中首先判断是否已经生成了该bean,防止生成多个bean* 	if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {* 		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);*  }*/if (isFullConfigurationCandidate(metadata)) {// 设置 CONFIGURATION_CLASS_ATTRIBUTE 为 fullbeanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);}/*** 执行这一步存在以下几种情况*  1 config != null但是该配置类不存在@Bean修饰的方法*  2 config == null 但是符合isConfigurationCandidate条件如下*  	2.1 不是接口*  	2.2 被下面几个注解修饰*  		candidateIndicators.add(Component.class.getName());* 		    candidateIndicators.add(ComponentScan.class.getName());* 		    candidateIndicators.add(Import.class.getName());* 		    candidateIndicators.add(ImportResource.class.getName());* 		 2.3 存在@Bean修饰的方法*   符合2.1且满足2.2 其中一个或者2.3则该类是设置为CONFIGURATION_CLASS_LITE:lite即使半注解配置类**/else if (isLiteConfigurationCandidate(metadata)) {// 设置 CONFIGURATION_CLASS_ATTRIBUTE 为 litebeanDef.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);// 如果存在order则可以设置该属性,以后可以进行排序设定执行顺序if (order != null) {beanDef.setAttribute(ORDER_ATTRIBUTE, order);}return true;}

主要是检测有没有对应的注解@Configurable@Component@ComponentScan@Import@ImportResource@Bean标注的方法,并根据相应的注解和条件设置属性为full:全注解和lite:半注解。

1.1.1 是否是半注解 - isLiteConfigurationCandidate()

ConfigurationClassUtils#isLiteConfigurationCandidate()

在上面的代码中,我们看到判断是否是 Lite 的关键方法是isLiteConfigurationCandidate()。其代码如下:

public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {// Do not consider an interface or an annotation...// 不能是接口if (metadata.isInterface()) {return false;}// Any of the typical annotations found?// 被 candidateIndicators 中的注解修饰。其中 candidateIndicators(@Component、@ComponentScan、@Import、@ImportResource) 注解在静态代码块中加载了for (String indicator : candidateIndicators) {if (metadata.isAnnotated(indicator)) {return true;}}// Finally, let's look for @Bean methods...try {// 类中包含被 @Bean 注解修饰的方法return metadata.hasAnnotatedMethods(Bean.class.getName());}catch (Throwable ex) {if (logger.isDebugEnabled()) {logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);}return false;}}

1.2 开始解析 - parser.parse(candidates);

ConfigurationClassParser#parse(Set configCandidates)

上面解析了如何判断一个类是否是配置类。也完成了配置类的筛选。那么开始进行配置类的解析,在 processConfigBeanDefinitions()方法中,对配置类的解析也只是一句话完成:

	parser.parse(candidates);

parser.parse(candidates); 的作用是:

  1. 将所有的配置类保存到ConfigurationClassParser.configurationClasses集合中
	private final Map configurationClasses = new LinkedHashMap<>();
  1. 解析注解并赋值给每个ConfigurationClass对应的属性。如解析@Import注解,并通过如下语句将结果保存到ConfigurationClass.importBeanDefinitionRegistrars集合中。
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());

同样的还有 将@ImportResource注解保存到ConfigurationClass.importedResources中,将@Bean修饰的方法和接口静态方法保存到ConfigurationClass.beanMethods中。
而在之后的this.reader.loadBeanDefinitions(configClasses);中才进行了这些属性的进一步处理

下面我们来具体看代码,其代码如下:

public void parse(Set configCandidates) {this.deferredImportSelectors = new LinkedList<>();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);}}processDeferredImportSelectors();}

里面的 parse 方法殊途同归,最终都会调用 processConfigurationClass()方法,所以我们直接进入该方法:

ConfigurationClassParser#processConfigurationClass(ConfigurationClass configClass)

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {// 判断是否应该跳过当前类的解析。这里面解析了 @Conditional 注解if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {return;}// 判断是否已经解析过。configurationClasses 中保存着已经解析过的配置类。在下面解析过的类都会被保存到 configurationClasses 中// 这里应该是 注入的配置类优先级高于引入的配置类// 如果配置类被多次引入则合并属性ConfigurationClass existingClass = this.configurationClasses.get(configClass);if (existingClass != null) {// 一个类被重复解析,那么可能被重复引入了,可能是通过 @Import 注解或者嵌套在其他配置类中被引入。如果这两者都是通过这种方式被引入,那么则进行引入合并// 如果当前配置类和之前解析过的配置类都是引入的,则直接合并if (configClass.isImported()) {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);do {sourceClass = doProcessConfigurationClass(configClass, sourceClass);}while (sourceClass != null);// 保存解析过的 配置类this.configurationClasses.put(configClass, configClass);}

这里需要注意的是配置类的重复引入优先级的问题 :
一般来说,Spring有一个自己的规则 :自身注入方式 优先于 引入方式。这里的引入方式指的被 @Import 或者其他配置类引入。当一个类被多次引入时,会使用自身注入的方式的bean 替代 被引入方式的bean。如果二者都是引入方式,则进行合并(在 ConfigurationClass 类中有一个importedBy 集合,将新引入的来源保存到 importedBy 中)

ConfigurationClass#mergeImportedBy(ConfigurationClass otherConfigClass)

public void mergeImportedBy(ConfigurationClass otherConfigClass) {this.importedBy.addAll(otherConfigClass.importedBy);}

看了这么久的源码,也知道了Spring的套路,方法名以do开头的才是真正做事的方法, 所以我们来看 doProcessConfigurationClass() 方法。

ConfigurationClassParser#doProcessConfigurationClass()

@Nullableprotected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)throws IOException {// Recursively process any member (nested) classes first// 1. 处理 @Component 注解processMemberClasses(configClass, sourceClass);// Process any @PropertySource annotations// 2. 处理 @PropertySource 注解for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);}else {logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");}}// Process any @ComponentScan annotations// 3. 处理 @ComponentScan注解Set 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 immediatelySet 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();}if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}// Process any @Import annotations// 4. 处理 @Import 注解processImports(configClass, sourceClass, getImports(sourceClass), true);// Process any @ImportResource annotations// 5. 处理 @ImportResource 注解AnnotationAttributes importResource =AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);if (importResource != null) {String[] resources = importResource.getStringArray("locations");Class readerClass = importResource.getClass("reader");for (String resource : resources) {String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);configClass.addImportedResource(resolvedResource, readerClass);}}// Process individual @Bean methods// 6. 处理 @Bean修饰的方法Set beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}// Process default methods on interfaces// 7. 处理其他默认接口方法processInterfaces(configClass, sourceClass);// Process superclass, if any// 处理父类,如果存在if (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;}

doProcessConfigurationClass方法中的逻辑很清楚,因为他把大部分的逻辑直接封装成了方法。下面我们就来一个一个分析。

1.2.1 处理@Component注解 - processMemberClasses()

这里对@Component的处理其实是处理配置类的内部类,即如果当前类是被 @Component修饰,则需要判断其内部类是否需要解析。

processMemberClasses()方法的代码如下:
代码逻辑也很简单。即如果配置类中有内部类,则判断其内部类是否是配置类,如果是则递归去解析新发现的内部配置类。
ConfigurationClassParser#processMemberClasses()

private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {// 获取内部类Collection memberClasses = sourceClass.getMemberClasses();if (!memberClasses.isEmpty()) {// 如果有内部类,则遍历内部类,判断内部类是否是配置类,如果是,则添加到 candidates 集合中。List candidates = new ArrayList<>(memberClasses.size());for (SourceClass memberClass : memberClasses) {// 这里判断的是是否是lite 类型的配置类if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {candidates.add(memberClass);}}// 进行排序OrderComparator.sort(candidates);for (SourceClass candidate : candidates) {// importStack 用来缓存已经解析过的内部类,这里处理循环引入问题。if (this.importStack.contains(configClass)) {// 打印循环引用异常this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));}else {// 解析前入栈,防止循环引入this.importStack.push(configClass);try {// 递归去解析新发现的配置类processConfigurationClass(candidate.asConfigClass(configClass));}finally {// 解析完毕出栈this.importStack.pop();}}}}}

注:

  1. 判断内部类是否是配置类,使用的方法是 ConfigurationClassUtils.isConfigurationCandidate,这里是检测内部类是否满足lite 的配置类规则,并未校验 full的规则。
  2. 代码中使用了this.importStack 来防止递归引入。避免了A引入B,B又引入A这种无限循环的情况。

1.2.2 处理 @PropertySource 注解

@PropertySource注解可以引入配置文件使用。在这里进行@PropertySource注解的解析,将引入的配置文件加载到环境变量中

	// 去重后遍历 PropertySource 注解所指向的属性。注意这里有两个注解@PropertySources 和 @PropertySource。for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {// 解析PropertySource  注解processPropertySource(propertySource);}else {logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");}}

processPropertySource代码如下,在这里解析每一个@PropertySource注解属性 :
ConfigurationClassParser#processPropertySource()

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {// 获取 @PropertySource 注解的各个属性String name = propertySource.getString("name");if (!StringUtils.hasLength(name)) {name = null;}String encoding = propertySource.getString("encoding");if (!StringUtils.hasLength(encoding)) {encoding = null;}// 获取指向的文件路径String[] locations = propertySource.getStringArray("value");Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");Class factoryClass = propertySource.getClass("factory");PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));// 遍历文件路径for (String location : locations) {try {// 根据路径获取到资源文件并保存到environment 中String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);// 解决占位符,获取真正路径Resource resource = this.resourceLoader.getResource(resolvedLocation);//保存 PropertySource 到 environment 中addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));}catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException ex) {// Placeholders not resolvable or resource not found when trying to open itif (ignoreResourceNotFound) {if (logger.isInfoEnabled()) {logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());}}else {throw ex;}}}}

1.2.3 处理 @ComponentScan、@ComponentScans 注解

@componentScans指定自动扫描的路径。

		// 这里会将 @ComponentScans 中的多个 @ComponentScan 也解析出来封装成一个个AnnotationAttributes对象Set componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);// 如果当前配置类被 @componentScans 或 @componentScan 注解修饰 && 不应跳过if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {// 遍历 @ComponentScans、 @ComponentScanfor (AnnotationAttributes componentScan : componentScans) {// The config class is annotated with @ComponentScan -> perform the scan immediately// 直接执行扫描,根据指定路径扫描出来bean。Set scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if needed// 遍历扫描出来的beanfor (BeanDefinitionHolder holder : scannedBeanDefinitions) {// 获取原始的bean的定义BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}// 检测如果是配置类,则递归调用 parse 解析。if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}

这里需要注意 :

  1. this.componentScanParser.parse()方法完成了指定路径下的bean的扫描,这里不再具体分析。

  2. 这里校验是否是配置类调用的是checkConfigurationClassCandidate方法,即校验了 full或lite的规则,和 处理 @Component中的内部类的规则并不相同。

  3. 没错,又是递归,如果扫描到的bean中发现了新的配置类,则递归去解析。

  4. 之前的我们提过,Springboot 在启动过程中将 启动类注册到了容器中,那么在这里进行递归遍历的时候就会通过启动类指定的默认路径来进行遍历, 完成了Springboot的启动注册。

1.2.4 处理 @Import、ImportSelector、ImportBeanDefinitionRegistrar

processImports(configClass, sourceClass, getImports(sourceClass), filter, true); 该方法处理的包括@Import、ImportSelector、 ImportBeanDefinitionRegistrar。这三个注解或接口都可以完成Bean的引入功能。

@Import:可以通过 @Import(XXX.class)的方式,将指定的类注册到容器中
ImportSelector : Spring会将 ImportSelector#selectImports())方法返回的内容通过反射加载到容器中
ImportBeanDefinitionRegistrar : 可以通过registerBeanDefinitions()方法声明BeanDefinition 并自己注册到Spring容器中 比如 : MyBatis 中的 AutoConfiguredMapperScannerRegistrar@Mapper修饰类的注册过程
需要注意的是,这里解析的ImportSelector、ImportBeanDefinitionRegistrar都是通过 @Import注解引入的。如果不是通过@Import引入(比如直接通过@ComponentImportSelector、ImportBeanDefinitionRegistrar 注入)的类则不会被解析。

注意 getImports(sourceClass)方法的作用是解析@Import注解
我们直接来看 processImports 方法,注释都比较清楚 :

ConfigurationClassParser#processImports()

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,Collection importCandidates, boolean checkForCircularImports) {// importCandidates 是通过getImports() 方法解析 @Import 注解而来, 如果为空则说明没有需要引入的直接返回if (importCandidates.isEmpty()) {return;}// 检测是否是循环引用。if (checkForCircularImports && isChainedImportOnStack(configClass)) {this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));}else {// 解析前先入栈,防止循环引用this.importStack.push(configClass);try {for (SourceClass candidate : importCandidates) {// 判断是否是ImportSelector类型。ImportSelector 则需要调用selectImports 方法来获取需要注入的类。if (candidate.isAssignable(ImportSelector.class)) {// Candidate class is an ImportSelector -> delegate to it to determine importsClass candidateClass = candidate.loadClass();ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);ParserStrategyUtils.invokeAwareMethods(selector, this.environment, this.resourceLoader, this.registry);if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {this.deferredImportSelectors.add(new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));}else {// 调用 selectImports 方法获取需要引入的类,并递归再次处理。String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());Collection importSourceClasses = asSourceClasses(importClassNames);// 递归解析processImports(configClass, currentSourceClass, importSourceClasses, false);}}// 如果是 ImportBeanDefinitionRegistrar 类型,则委托它注册其他bean定义else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {// Candidate class is an ImportBeanDefinitionRegistrar ->// delegate to it to register additional bean definitionsClass candidateClass = candidate.loadClass();ImportBeanDefinitionRegistrar registrar =BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);ParserStrategyUtils.invokeAwareMethods(registrar, this.environment, this.resourceLoader, this.registry);configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());}else {// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->// process it as an @Configuration classthis.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());// 否则递归处理需要引入的类。processConfigurationClass(candidate.asConfigClass(configClass));}}}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();}}}

1.2.5 处理 @ImportResource 注解

@ImportResource就显得很简单了,直接保存到configClass 中

	// 5. 处理 @ImportResource 注解AnnotationAttributes importResource =AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);if (importResource != null) {String[] resources = importResource.getStringArray("locations");Class readerClass = importResource.getClass("reader");for (String resource : resources) {String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);configClass.addImportedResource(resolvedResource, readerClass);}}

1.2.6 处理 @Bean修饰的方法

@Bean也很简单了,直接保存到 configClass 的中

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

1.2.7 处理接口默认方法

这里是 检测 配置类实现的接口中的默认方法是否被@Bean修饰,如果被修饰则也需要保存到 configClass

private void processInterfaces(ConfigurationClass configClass, SourceClass sourceClass) throws IOException {for (SourceClass ifc : sourceClass.getInterfaces()) {Set beanMethods = retrieveBeanMethodMetadata(ifc);for (MethodMetadata methodMetadata : beanMethods) {if (!methodMetadata.isAbstract()) {// A default method or other concrete method on a Java 8+ interface...configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}}processInterfaces(configClass, ifc);}}

1.2.8 处理父类

如果存在父类,则将父类返回,对父类进行解析。

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

这里这么处理是解析到最上层的父类。这里理一下调用顺序:parse -> processConfigurationClass -> doProcessConfigurationClass 。而 doProcessConfigurationClass有如下一个循环,只有sourceClass = null才会跳出循环。当configClass没有满足上面判断条件的父类时,才会返回null

	SourceClass sourceClass = asSourceClass(configClass, filter);do {sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);}while (sourceClass != null);this.configurationClasses.put(configClass, configClass);

1.3 parser.validate();

到了 这一步,是对解析出来的配置类进行进一步的校验,确保没有问题

这里我们看看其代码如下:
ConfigurationClassParser#validate()

public void validate() {for (ConfigurationClass configClass : this.configurationClasses.keySet()) {configClass.validate(this.problemReporter);}}

这里可以看到是调用每个 ConfigurationClass 类的 validate 方法进行校验,我们进去看看ConfigurationClass#validate的代码 :

	public void validate(ProblemReporter problemReporter) {// A configuration class may not be final (CGLIB limitation) unless it declares proxyBeanMethods=false// 获取 @Configuration 注解的属性信心Map attributes = this.metadata.getAnnotationAttributes(Configuration.class.getName());// 如果 @Configuration 存在(attributes != null)  && attributes.get("proxyBeanMethods") == true 才进行进一步的校验if (attributes != null && (Boolean) attributes.get("proxyBeanMethods")) {// 如果配置类 是  final 修饰,即终态类,则是错误,因为无法动态代理if (this.metadata.isFinal()) {problemReporter.error(new FinalConfigurationProblem());}// 对配置类中的 @Bean 注解修饰的方法进行校验for (BeanMethod beanMethod : this.beanMethods) {beanMethod.validate(problemReporter);}}}

这里我们再来看看@Bean方法的校验 BeanMethod#validate如下:

	@Overridepublic void validate(ProblemReporter problemReporter) {// 如果是静态方法没有约束规则,直接返回。if (getMetadata().isStatic()) {// static @Bean methods have no constraints to validate -> return immediatelyreturn;}// 校验该方法所属的类是否被 @Configuration 修饰。if (this.configurationClass.getMetadata().isAnnotated(Configuration.class.getName())) {// 判断是否可重写。cglib代理需要方法可重写。不可重写则错误if (!getMetadata().isOverridable()) {// instance @Bean methods within @Configuration classes must be overridable to accommodate CGLIBproblemReporter.error(new NonOverridableMethodError());}}}

1.4 this.reader.loadBeanDefinitions(configClasses);

上面也说了,在parser.parse(candidates); 方法中,将各种注解的属性值都解析了出来,并保存到了 configClass的各种属性中。而在 this.reader.loadBeanDefinitions(configClasses); 中才真正处理了这些属性。所以我们接下来看看loadBeanDefinitions 的处理流程。

loadBeanDefinitions遍历了每一个ConfigurationClass,通过loadBeanDefinitionsForConfigurationClass()方法处理。

ConfigurationClassBeanDefinitionReader#loadBeanDefinitions()

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

所以我们来看看loadBeanDefinitionsForConfigurationClass的实现。可很清楚的看到,每个部分的解析都封装到了不同的方法中。
loadBeanDefinitions() >>> ConfigurationClassBeanDefinitionReader#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;}// 1. 如果配置是被引入的(被 @Import 或者其他配置类内部引入)if (configClass.isImported()) {registerBeanDefinitionForImportedConfigurationClass(configClass);}// 2. 遍历配置类中的所有 BeanMethod方法for (BeanMethod beanMethod : configClass.getBeanMethods()) {loadBeanDefinitionsForBeanMethod(beanMethod);}// 3. 加载 通过 @ImportResource 的 获取的beanloadBeanDefinitionsFromImportedResources(configClass.getImportedResources());// 4. 加载 通过 @Import 的 获取的beanloadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());}

下面 我们来详细看看每个方法。

1.4.1 registerBeanDefinitionForImportedConfigurationClass

这一步的工作很简单,就是将引入的配置类注册为 BeanDefinition。

private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {AnnotationMetadata metadata = configClass.getMetadata();AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);configBeanDef.setScope(scopeMetadata.getScopeName());String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);// 创建代理,根据 scopeMetadata 的代理模式。默认不创建代理。definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);// 注册了BeanBeanDefinition 。这里将BeanDefinition保存到了 DefaultListableBeanFactory#beanDefinitionMap 中this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());configClass.setBeanName(configBeanName);if (logger.isDebugEnabled()) {logger.debug("Registered bean definition for imported class '" + configBeanName + "'");}}

这里需要注意的是 AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); 根据scopeMetadata的代理模式创建了代理。代理模式有四种,分别为

  • DEFAULT : 默认模式。默认等同于NO
  • NO : 不使用代理
  • INTERFACES : Jdk 动态代理
  • TARGET_CLASS : Cglib代理

applyScopedProxyMode方法中 通过获取ScopeMetadata.getScopedProxyMode()来判断使用什么代理方式。而ScopeMetadata的代理方式 是在创建scopeMetadata的过程中,获取类上面的@ScopeproxyMode属性来指定的。

ScopeMetadata scopeMetadata =  scopeMetadataResolver.resolveScopeMetadata(configBeanDef);

resolveScopeMetadata方法如下
ScopeMetadataResolver#resolveScopeMetadata()

	ScopeMetadata resolveScopeMetadata(BeanDefinition definition);

AnnotationScopeMetadataResolver#resolveScopeMetadata()

@Overridepublic ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {ScopeMetadata metadata = new ScopeMetadata();if (definition instanceof AnnotatedBeanDefinition) {AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;// 获取 @Scope 注解AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(annDef.getMetadata(), this.scopeAnnotationType);if (attributes != null) {metadata.setScopeName(attributes.getString("value"));// 获取 @Scope 的proxyMode属性ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");if (proxyMode == ScopedProxyMode.DEFAULT) {proxyMode = this.defaultProxyMode;}// 设置 scopedProxyMode 属性,后面根据此属性判断使用什么代理方式metadata.setScopedProxyMode(proxyMode);}}return metadata;}

1.4.2 loadBeanDefinitionsForBeanMethod

具体代码如下,基本上就是解析各种注解,创建对应的 BeanDefinition 并注册。

	private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {ConfigurationClass configClass = beanMethod.getConfigurationClass();MethodMetadata metadata = beanMethod.getMetadata();String methodName = metadata.getMethodName();// Do we need to mark the bean as skipped by its condition?// 是否应该跳过if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {configClass.skippedBeanMethods.add(methodName);return;}if (configClass.skippedBeanMethods.contains(methodName)) {return;}// 获取被 @Bean修饰的方法AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);Assert.state(bean != null, "No @Bean annotation attributes");// Consider name and any aliases// 获取别名List names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));String beanName = (!names.isEmpty() ? names.remove(0) : methodName);// Register aliases even when overridden// 注册别名for (String alias : names) {this.registry.registerAlias(beanName, alias);}// Has this effectively been overridden before (e.g. via XML)?// 判断是否已经被定义过if (isOverriddenByExistingDefinition(beanMethod, beanName)) {if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +"' clashes with bean name for containing configuration class; please make those names unique!");}return;}// 定义配置类的  BeanDefinitionConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);beanDef.setResource(configClass.getResource());beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));// 处理静态 @Bean 方法和 非静态 @Bean 方法if (metadata.isStatic()) {// static @Bean methodif (configClass.getMetadata() instanceof StandardAnnotationMetadata) {beanDef.setBeanClass(((StandardAnnotationMetadata) configClass.getMetadata()).getIntrospectedClass());}else {beanDef.setBeanClassName(configClass.getMetadata().getClassName());}// 设置唯一工厂方法名称beanDef.setUniqueFactoryMethodName(methodName);}else {// instance @Bean method// 指定要使用的工厂bean(如果有)。这是用于调用指定工厂方法的bean的名称beanDef.setFactoryBeanName(configClass.getBeanName());// 设置唯一工厂方法名称,内部调用了 setFactoryMethodName(name); 保存 FactoryMethodNamebeanDef.setUniqueFactoryMethodName(methodName);}if (metadata instanceof StandardMethodMetadata) {beanDef.setResolvedFactoryMethod(((StandardMethodMetadata) metadata).getIntrospectedMethod());}// 设置构造模式 构造注入beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);// 设置跳过属性检查beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);// 处理通用的注解: @Lazy、@Primary、@DependsOn、@Role、@Description。设置到 BeanDefinition 中AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);// 获取注解的其他属性并设置到 BeanDefinitionAutowire autowire = bean.getEnum("autowire");if (autowire.isAutowire()) {beanDef.setAutowireMode(autowire.value());}boolean autowireCandidate = bean.getBoolean("autowireCandidate");if (!autowireCandidate) {beanDef.setAutowireCandidate(false);}String initMethodName = bean.getString("initMethod");if (StringUtils.hasText(initMethodName)) {beanDef.setInitMethodName(initMethodName);}String destroyMethodName = bean.getString("destroyMethod");beanDef.setDestroyMethodName(destroyMethodName);// Consider scopingScopedProxyMode proxyMode = ScopedProxyMode.NO;// 处理方法上的 @Scope 注解AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);if (attributes != null) {beanDef.setScope(attributes.getString("value"));proxyMode = attributes.getEnum("proxyMode");if (proxyMode == ScopedProxyMode.DEFAULT) {proxyMode = ScopedProxyMode.NO;}}// Replace the original bean definition with the target one, if necessary// 如果有必要(代理模式不同),替换掉旧的BeanDefinitionBeanDefinition beanDefToRegister = beanDef;if (proxyMode != ScopedProxyMode.NO) {BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(new BeanDefinitionHolder(beanDef, beanName), this.registry,proxyMode == ScopedProxyMode.TARGET_CLASS);beanDefToRegister = new ConfigurationClassBeanDefinition((RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata);}if (logger.isTraceEnabled()) {logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",configClass.getMetadata().getClassName(), beanName));}// 注册BeanDefinitionthis.registry.registerBeanDefinition(beanName, beanDefToRegister);}

这里特意提一下下面几句的功能

// 设置引入该bean 的配置类的类名
beanDef.setFactoryBeanName(configClass.getBeanName());
// 设置 引入bean 的类名
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
// 设置在配置类中引入该bean 的方法名
beanDef.setUniqueFactoryMethodName(methodName);

这里会为 @Bean修饰的方法创建出一个ConfigurationClassBeanDefinition注册到Spring容器中,ConfigurationClassBeanDefinition是特指用于表示从配置类(而不是其他任何配置源)创建了Bean定义。在需要确定是否在外部创建bean定义的bean覆盖情况下使用。在后面的Bean实例化过程中,会有多次使用。比如在 AbstractAutowireCapableBeanFactory#resolveBeforeInstantiation()

		// 在 determineTargetType 方法中根据  factoryMethodName 是否为空,判断bean注入方式,来获取注入的 Class类型Class targetType = determineTargetType(beanName, mbd)

以及会在AbstractAutowireCapableBeanFactory#createBeanInstance()方法中有如下两句。
@Bean的修饰的方法会调用instantiateUsingFactoryMethod()方法,通过反射调用方法,并将反射结果注入到Spring容器中,完成@Bean注解的功能。

1.4.3 loadBeanDefinitionsFromImportedResources

loadBeanDefinitionsFromImportedResources从导入的资源加载Bean定义。即通过解析 @ImportResource注解引入的资源文件,获取到BeanDefinition并注册。

private void loadBeanDefinitionsFromImportedResources(Map> importedResources) {Map, BeanDefinitionReader> readerInstanceCache = new HashMap<>();// 遍历引入的资源文件importedResources.forEach((resource, readerClass) -> {// Default reader selection necessary?if (BeanDefinitionReader.class == readerClass) {// 处理 .groovy 类型文件if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {// When clearly asking for Groovy, that's what they'll get...readerClass = GroovyBeanDefinitionReader.class;}else {// Primarily ".xml" files but for any other extension as well// 这里使用 XmlBeanDefinitionReader 类型来解析readerClass = XmlBeanDefinitionReader.class;}}// 从缓冲中获取BeanDefinitionReader reader = readerInstanceCache.get(readerClass);// 如果缓存中没有,则创建一个 reader 用于 resource 的解析。if (reader == null) {try {// Instantiate the specified BeanDefinitionReaderreader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);// Delegate the current ResourceLoader to it if possibleif (reader instanceof AbstractBeanDefinitionReader) {AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);abdr.setResourceLoader(this.resourceLoader);abdr.setEnvironment(this.environment);}readerInstanceCache.put(readerClass, reader);}catch (Throwable ex) {throw new IllegalStateException("Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");}}// TODO SPR-6310: qualify relative path locations as done in AbstractContextLoader.modifyLocations// 解析resource资源中的内容reader.loadBeanDefinitions(resource);});}

1.4.4 loadBeanDefinitionsFromRegistrars

loadBeanDefinitionsFromRegistrars方法注册了了@Import 注解引入的内容。这里很简单,将@Import引入的内容注入到容器中。

	private void loadBeanDefinitionsFromRegistrars(Map registrars) {registrars.forEach((registrar, metadata) ->registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));}

四、总结

从目前我看到的来说(虽然也没看过几个),有两个后处理器非常重要:

  • ConfigurationClassPostProcessor : 即本文解析的这个后处理器。虽然仅仅是上篇,但是其作用已经非常清楚了。ConfigurationClassPostProcessor 解析配置类(这里的配置类不仅仅局限于@Configuration 注解,还包括 @Import@ImportResource 等注解),将解析到的需要注入到Spring容器中的bean的BeanDefinition保存起来。在后面的bean 初始化都需要BeanDefinition

  • AutowiredAnnotationBeanPostProcessor : 之前解析过。完成了Bean所依赖的属性的注入。 解析bean中的 需要自动注入的bean @Autowired@Inject @Value注解。
    简单来说ConfigurationClassPostProcessor完成了 Bean的扫描与解析, AutowiredAnnotationBeanPostProcessor完成了Bean 属性的注入

本文只分析了ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()方法,对于ConfigurationClassPostProcessor#postProcessBeanFactory方法的分析则放在后篇,这里简单的说明ConfigurationClassPostProcessor#postProcessBeanFactory()方法通过cglib动态代理,完成了 对@Bean修饰方法的代理,以确保其正确语义。

以上:内容部分参考
《Spring源码深度解析》
如有侵扰,联系删除。 内容仅用于自我记录学习使用。如有错误,欢迎指正

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...