前言
- 上篇博客spring 5.0.x源码学习系列五: AnnotationConfigApplicationContext类refresh方法之invokeBeanFactoryPostProcessor(一)主要解释的是invokeBeanFactoryPostProcessor方法的大致执行流程, 为了精通spring, 咱们除了要了解后置处理器的执行顺序外,还得明白这其中的一个特殊后置处理器(ConfigurationClassPostProcessor)的作用。下面将开始对ConfigurationClassPostProcessor的第一个重要的身份
BeanDefinitionRegistryPostProcessor
进行讲解(第二个身份BeanFactoryPostProcessor
在下一篇博客进行总结)
一、ConfigurationClassPostProcessor之BeanDefinitionRegistryPostProcessor身份
- 它的这个身份起到了非常重要的作用: 处理配置类并解析它们。 这句话可能有点难理解, 我们根据下面的篇幅慢慢理解。
二、流程图
-
这里提供一张处理配置类流程图, 结合项目demo一起看
三、项目demo
3.1 项目全景图
3.1.1 EnableProxy类
3.1.2 ImportEugene类
3.1.3 UserDaoImpl类
3.1.4 ImportEugeneImportSelector类
3.1.5 MyImportBeanDefinitionRegistrar类
3.1.6 ImportEugeneBeanFactoryProcessor类
3.1.7 JDKProxyPostProcessor类
3.1.8 MyInvocationHandler类
3.1.9 ProxyUtil类
3.1.10 UserServiceImpl类
3.1.11 UserService类
3.1.12 AppConfig类
3.1.13 Entry类
3.1.14 TestBeanInAppConfig类
3.1.15 TestDaoInUserDaoImpl类
3.2 demo运行结果
-
AppConfig类
存在
@ImportEugene和 @EnableProxy注解时, 运行结果如下:
=> 打印了7句 ========ImportEugene========的原因是有7个bean要创建, 分别为如下7个bean:AppConfig, UserServiceImpl, UserDaoImpl, ImportEugeneBeanFactoryProcessor, JDKProxyPostProcessor, TestBeanInAppConfig, TestDaoInUserDaoImpl
-
AppConfig类
去除
@ImportEugene和 @EnableProxy注解时, 运行结果如下:
3.4 Demo运行结果总结
本demo利用了spring的两个大扩展点: BeanPostProcessor和 @Import注解。其中自定义注解 @EnableProxy和 @ImportEugene利用了 @Import注解扩展点的两种类型:
ImportSelector
和ImportBeanDefinitionRegistrar
来实现-
关于上述的三个点
BeanPostProcessor
,ImportSelector
,ImportSelector
的功能将以如下表格来阐述扩展点 提供api 作用 使用示例 BeanPostProcessor beanName和当前bean对象 可以动态修改bean 本案例中的为UserServiceImpl对象生成代理对象 ImportSelector AnnotationMetadata 能获取到被导入的那个类的信息, 可以根据自定义的注解来动态写逻辑, 返回的字符串数组为类的全类名, spring会把他们当成bean去实例化 本案例中的ImportEugeneBeanFactoryProcessor, 动态的添加指定bean ImportBeanDefinitionRegistrar AnnotationMetadata和BeanDefinitionRegistry 拥有ImportSelector的api, 同时还能获取到BeanDefinitionRegister 本案例中的MyImportBeanDefinitionRegistrar, 动态的添加beanDefinition
四、运行原理
4.1 前言
- 本demo中的演示只有几个部分和ConfigurationClassPostProcessor的BeanDefinitionRegistryPostProcessor身份有关, 只要涉及到bean的创建和bean的执行顺序, 都不属于本篇博客的内容, 添加一个demo演示是为了更好的说明问题, 现在开始总结原理
4.2 执行原理
4.2.1 在上篇博客中有总结到invokeBeanFactoryPostProcessors方法执行后置处理器的几个顺序。我们现在将从调用ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法开始
-
postProcessBeanDefinitionRegistry方法源码
@Override 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); // 开始处理配置类, registry为bean工厂 processConfigBeanDefinitions(registry); }
-
processConfigBeanDefinitions处理配置类
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { // 存储配置类的数据结构: 很重要, 后续将解析此集合 List
configCandidates = new ArrayList<>(); // 获取bean工厂的所有beanDefinition的名称, 在本次demo中, 一共有7个beanDefinition // 为spring内置的6个beanDefinition + AppConfig beanDefinition String[] candidateNames = registry.getBeanDefinitionNames(); // 遍历beanDefinition for (String beanName : candidateNames) { // 根据beanName到bean工厂中拿到beanDefinition BeanDefinition beanDef = registry.getBeanDefinition(beanName); // 判断是否为全注解或者部分注解 => 这里正常的spring流程下, 应该都没标注 // 除非程序员自己利用扩展点修改了配置类对应的标识 // eg: 标识它为全配置类 // 配置类对应的beanDefinition.setAttribute( // "org.springframework.context.annotation. // ConfigurationClassPostProcessor.configurationClass", "full"); // eg: 标识它为部分配置类 // 配置类对应的beanDefinition.setAttribute( // "org.springframework.context.annotation. // ConfigurationClassPostProcessor.configurationClass", "lite"); if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) || ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) { if (logger.isDebugEnabled()) { logger.debug("Bean definition has already been processed as a configuration class: " + beanDef); } } // 正常情况下会走else, // ConfigurationClassUtils.checkConfigurationClassCandidate的核心逻辑应该为如下代码 // if (isFullConfigurationCandidate(metadata)) { // beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL); // } // else if (isLiteConfigurationCandidate(metadata)) { // beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE); // } // 若当前遍历的beanDefinition是一个配置类或者全配置类则给他一个标识, 并返回true // 进而将当前的beanDefinition添加到configCandidates数据结构中 // 这里总结下什么叫全配置类什么叫部分配置类 // 全配置类: 加了@Configuration注解 // 部分配置类: 类中有@Component、@Import、@ImportResource、@ComponentScan注解及方法中有@Bean注解的类 else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } // Return immediately if no @Configuration classes were found // 若bean工厂中无配置类, 那么将结束解析配置类的流程 if (configCandidates.isEmpty()) { return; } // Sort by previously determined @Order value, if applicable 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 // 还未总结到它的具体作用 SingletonBeanRegistry sbr = null; if (registry instanceof SingletonBeanRegistry) { sbr = (SingletonBeanRegistry) registry; if (!this.localBeanNameGeneratorSet) { BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR); if (generator != null) { this.componentScanBeanNameGenerator = generator; this.importBeanNameGenerator = generator; } } } if (this.environment == null) { this.environment = new StandardEnvironment(); } // Parse each @Configuration class // 生成一个配置类的解析器, 将使用它来对配置类进行解析 ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); // 存放在解析配置类过程中得到的新的配置类, eg: 在解析@ComponentScan注解的扫描路径时, // 有可能扫描到其他的配置类 Set candidates = new LinkedHashSet<>(configCandidates); // 将register方法中或者使用spring扩展点手动添加到bean工厂的配置类添加到存放解析完毕的数据结构中, // 为什么这么做? 因为后续将挨个去解析candidates的配置类, 并将新扫描出来或者import进去的 // 配置类也添加到candidates里面去了, 不需要再解析一遍 Set alreadyParsed = new HashSet<>(configCandidates.size()); do { // 解析配置类, 执行到此, candidates中只有一个元素, // 因为在执行这个步骤的时候只有AppConfig对应的beanDefinition在bean工厂中 parser.parse(candidates); parser.validate(); // 拿到配置类解析器得到的配置类 Set configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); // 移出已经解析的配置类 configClasses.removeAll(alreadyParsed); // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } // 加载扫描出来的所有beanDefinition, 并在此将它们挨个注册到spring bean工厂中 // 所以执行到这里时, configClasses中存储元素内容应该为: // 根据candidates中的配置类解析出来的所有配置类 // (包括@Component注解的类、@Import注解导入的普通类、@Configuration的类) this.reader.loadBeanDefinitions(configClasses); alreadyParsed.addAll(configClasses); candidates.clear(); // 这里还会校验这样一种情况,因为所有配置类是存到candidates变量中 // 而上述this.reader.loadBeanDefinitions(configClasses);代码 // 只是将配置类中导入的类注册到bean工厂中去,而此时有可能 // 这些导入的类内部也会导入其他的类,所以还需要比较下当前解析的配置类 // 中导入的类的数量和原来获取的配置类的数量。将多出来的配置类数量进行 // 汇总,然后再统一处理它们 if (registry.getBeanDefinitionCount() > candidateNames.length) { String[] 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)) { BeanDefinition bd = registry.getBeanDefinition(candidateName); if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) { candidates.add(new BeanDefinitionHolder(bd, candidateName)); } } } candidateNames = newCandidateNames; } } while (!candidates.isEmpty()); // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes 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(); } } -
解析器ConfigurationClassParser之parser方法
public void parse(Set
configCandidates) { this.deferredImportSelectors = new LinkedList<>(); for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); try { // 这里会根据当前配置类的beanDefinition进入不同的解析逻辑, // 通过Register方法注册的beanDefinition类型统一为AnnotatedBeanDefinition // 这个在之前的博客中有总结过 if (bd instanceof AnnotatedBeanDefinition) { // 所以解析配置类的时候,是进入这个if分支 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(); } -
解析器ConfigurationClassParser之processConfigurationClass方法 => 会将处理的当前配置类存入解析器的configurationClasses集合中
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) { return; } ConfigurationClass existingClass = this.configurationClasses.get(configClass); if (existingClass != null) { 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 { // 处理完当前配置类后, 会在方法中return null => 表示当前配置类被解析完成 // 进而进入下面的逻辑, 将当前配置添加到configurationClasses中 // 并在最外部 // org.springframework.context.annotation.ConfigurationClassPostProcessor类 // 中的processConfigBeanDefinitions方法中获取configurationClasses, 并 // 解析它们(这里的解析不仅仅是注册beanDefinition, 还包括当前配置类中的 // @Bean方法、@Import注解导入的类等等) sourceClass = doProcessConfigurationClass(configClass, sourceClass); } while (sourceClass != null); // 上面每次解析完配置类就添加到当前对象的configurationClasses属性中 // 当前对象 => 就是在 // org.springframework.context.annotation.ConfigurationClassPostProcessor类中 // 的processConfigBeanDefinitions方法中创建出来的配置类解析器 this.configurationClasses.put(configClass, configClass); }
-
解析器ConfigurationClassParser之doProcessConfigurationClass方法 => 处理@PropertySource, @ComponentScan, @Import, @ImportResource, @Bean注解
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); // Process any @PropertySource annotations // 处理@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 // 处理@ComponentScan注解, sourceClass为当前解析的配置类, 即: AppConfig, 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 immediately // 扫描得到所有拥有@Component注解的beanDefinition, 并在 // 内部(this.componentScanParser.parse)将它们注册到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 for (BeanDefinitionHolder holder : scannedBeanDefinitions) { BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition(); if (bdCand == null) { bdCand = holder.getBeanDefinition(); } // 在此校验扫描出来的@Component注解对应的beanDefinition, 因为有可能它们也被添加了配置类相关的注解, // 所以也把它们当做配置类来解析 // 又因为@Component注解标识的类属于部分配置类, 所以肯定会将它们当做配置类再解析一遍 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) { // 这里又调用了解析配置类逻辑, 递归调用 parse(bdCand.getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations // 处理当前配置类的@Import注解 // 该方法的主要逻辑为如下: // 获取@Import注解的值, 并挨个遍历它们 /*for (SourceClass candidate : importCandidates) { // 若导入的类是ImportSelector的类型 if (candidate.isAssignable(ImportSelector.class)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class> candidateClass = candidate.loadClass(); // 使用反射创建对象, 为了调用ImportSelector的方法 ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); ParserStrategyUtils.invokeAwareMethods( selector, this.environment, this.resourceLoader, this.registry); // 判断是否为延迟导入, 默认为null, 所有走else if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) { this.deferredImportSelectors.add( new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector)); } else { // 调用ImportSelector的selectImports方法, 得到返回的数组 String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata()); // 将类对应的全路径转成Collection 类型, 为了下面的递归调用 Collection importSourceClasses = asSourceClasses(importClassNames); // 针对获取到的类的全路径, 把它们当做Import注解导入的类进行处理, 递归调用 processImports(configClass, currentSourceClass, importSourceClasses, false); } } // 处理类型为ImportBeanDefinitionRegistrar的类 else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) { // Candidate class is an ImportBeanDefinitionRegistrar -> // delegate to it to register additional bean definitions Class> candidateClass = candidate.loadClass(); // 使用反射创建对象 ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class); ParserStrategyUtils.invokeAwareMethods( registrar, this.environment, this.resourceLoader, this.registry); // 将导入的ImportBeanDefinitionRegistrar类型的类添加到当前配置类存放ImportBeanDefinitionRegistrar // 类型的集合中, 方便后面处理配置类时能获取到它们 configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata()); } else { // 非ImportSelector和ImportSelector的selectImports类型的类, 把它当成配置类处理 // 在递归调用处理配置类逻辑processConfigurationClass // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as an @Configuration class this.importStack.registerImport( currentSourceClass.getMetadata(), candidate.getMetadata().getClassName()); processConfigurationClass(candidate.asConfigClass(configClass)); } }*/ processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations // 基于注解的方式的spring, 很少使用此注解, 所以这块没有总结到 AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); if (importResource != null) { String[] resources = importResource.getStringArray("locations"); Class extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods // 处理配置类中的方法存在@Bean注解的情况, 挨个遍历存放到当前配置类的数据结构中 // 方便在外部处理配置类(loadBeanDefinition)时将它们获取出来 Set beanMethods = retrieveBeanMethodMetadata(sourceClass); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process default methods on interfaces // 没总结到, 暂时忽略 processInterfaces(configClass, sourceClass); // Process superclass, if any // 没总结到, 暂时忽略 if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } } // No superclass -> processing is complete return null; } -
加载配置类之loadBeanDefinitions方法
public void loadBeanDefinitions(Set
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; } // 处理被@Import注解导入的普通类 if (configClass.isImported()) { registerBeanDefinitionForImportedConfigurationClass(configClass); } // 处理当前配置类中的所有@Bean标识的方法, 并将它注册到bean工厂 for (BeanMethod beanMethod : configClass.getBeanMethods()) { loadBeanDefinitionsForBeanMethod(beanMethod); } // 加载@ImportedResources注解导入的资源 loadBeanDefinitionsFromImportedResources(configClass.getImportedResources()); // 将@Import注解导入的ImportBeanDefinitionRegistrar类型的bean注册到bean工厂 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars()); }
4.2.2 ConfigurationClassPostProcessor做为BeanDefinitionRegistryPostProcessor后置处理器的执行结果
-
执行结果和统计
BeanDefinition Name | 注册渠道 |
---|---|
org.springframework.context.annotation.internalConfigurationAnnotationProcessor | AnnotationConfigApplicationContext无参构造方法 |
org.springframework.context.event.internalEventListenerFactory | AnnotationConfigApplicationContext无参构造方法 |
userServiceImpl | 解析@ComponentScan注解 |
testDaoInUserDaoImpl | @Bean注解 |
testDao | AppConfig类的@Bean注解 |
org.springframework.context.event.internalEventListenerProcessor | AnnotationConfigApplicationContext无参构造方法 |
org.springframework.context.annotation.internalAutowiredAnnotationProcessor | AnnotationConfigApplicationContext无参构造方法 |
org.springframework.context.annotation.internalCommonAnnotationProcessor | AnnotationConfigApplicationContext无参构造方法 |
appConfig | register方法 |
userDaoImpl | 解析@ComponentScan注解 |
JDKProxyPostProcessor | 解析@Import注解 |
org.springframework.context.annotation.internalRequiredAnnotationProcessor | AnnotationConfigApplicationContext无参构造方法 |
com.eugene.sumarry.csdn.invokeBeanFactoryPostProcessor2.postprocessor.ImportEugeneBeanFactoryProcessor | 解析@Import注解 |
五、小结
5.1 黑箱理论
- ConfigurationClassPostProcessor之BeanDefinitionRegistryPostProcessor身份的主要作用为:
扫描并解析register方法注册的配置类, 解析完成后, 所有能被扫描出来的 bean全部都以beanDefinition的形式存在于bean工厂中,为后续执行扫描 出来的后置处理器和创建bean提供了条件
5.2 建议
- 本篇博客提供的大多数为源码注释, 最好是能自己手动搭建与本次demo一样的项目结构, 结合提供的注释和运行结果一步一步的去了解ConfigurationClassPostProcessor作为BeanDefinitionRegistryPostProcessor身份的作用
- I am a slow walker, but I never walk backwards.