上一篇文章Spring Ioc源码分析系列--Ioc源码入口分析已经介绍到Ioc容器的入口refresh()
方法,并且分析了refresh()
方法里面的前三个子方法分析了一下。还记得分析了什么麽?估计早忘了分析了什么,可以说是看了个寂寞。但是不要慌,看了忘肯定是正常的,需要回顾复习一下,最好做点笔记记录一下,有自己的沉淀才会印象深刻。最好跟着代码自己调试几遍,纸上得来终觉浅,绝知此事要躬行。
好了,这里回顾一下上篇文章的内容。上篇文章主要分析了三个方法,prepareRefresh()
进行了一些容器启动前的属性设置,obtainFreshBeanFactory()
方法完成了读取配置文件,该方法的实现会针对xml配置创建内部容器,该容器负责bean的创建与管理,会进行BeanDefinition
的注册,prepareBeanFactory(beanFactory)
方法主要注册一些容器中需要使用的系统bean
,例如classloader
,BeanFactoryPostProcessor
等。
喝了鸡汤看了回顾,接下来才是今天这篇文章的正文开始。
思考一个问题,Spring提供了非常良好的扩展性,那么扩展性在哪里体现?这个问题仁者见仁智者见智,但是把场景压缩一下,压缩到上一篇文章我们已经获得了一个加载完成BeanDefinition
的容器上,我在统一的BeanDefinition
加载完成后,我想修改某一个或者增加某一个BeanDefinition
,这时候怎么实现呢?
对Spring熟悉点的读者可能已经猜到,这时候就可以使用BeanFactoryPostProcessor
后置处理器来完成这个操作了,那么这篇文章会解决两个疑问:
第一个疑问会通过一个例子来说明,第二个疑问会通过源码分析来阐述。废话少说,先进行BeanFactoryPostProcessor
介绍,然后还是使用上一篇文章的例子来实现用BeanFactoryPostProcessor
来替换UserService
的实现。
这是一个容器的钩子方法,允许自定义修改应用程序上下文的 bean
定义,调整上下文底层 bean
工厂的 bean
属性值。对于针对用系统管理员的自定义配置文件覆盖应用程序上下文中配置的 bean
属性非常有效。BeanFactoryPostProcessor
可以与 bean
定义交互和修改,但不能与 bean
实例交互。这样做可能会导致过早的 bean
实例化,违反容器并导致意外的副作用。如果需要 bean 实例交互,请考虑改为实现 BeanPostProcessor
。ApplicationContext
自动检测其 bean
定义中的 BeanFactoryPostProcessor
,并在创建任何其他 bean
之前应用它们。 BeanFactoryPostProcessor
也可以通过编程方式注册到 ConfigurableApplicationContext
。
这个类只有一个方法,入参为当前容器,下面来看一下postProcessBeanFactory()
这个方法。
@FunctionalInterface public interface BeanFactoryPostProcessor { /** * Modify the application context's internal bean factory after its standard * initialization. All bean definitions will have been loaded, but no beans * will have been instantiated yet. This allows for overriding or adding * properties even to eager-initializing beans. * * 在标准初始化之后修改应用程序上下文的内部 bean 工厂。 * 所有 bean 定义都将被加载,但还没有 bean 被实例化。 * 这允许覆盖或添加属性,甚至是急切初始化的 bean。 * * @param beanFactory the bean factory used by the application context * @throws org.springframework.beans.BeansException in case of errors */ void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; }
可以看到这里的操作空间是非常大的,直接把当前的容器传入,意味着你可以在当前容器的基础上做任何操作。就比如你天天看着女神,可望而不可即,但是某天通过一个传送门,把女神送到了你家里,你是不是可以为所欲为了,想干什么就干什么,例如你可以让她穿你喜欢的衣服,吃你喜欢吃的东西等等。
那下面就用代码实现一下。还是在上一篇文章Spring Ioc源码分析系列--Ioc源码入口分析的基础上进行添加代码, 所有源码都可以在我的仓库ioc-sourcecode-analysis-code-demo里找到 。
首先新建一个ReplaceUserServiceImpl
类实现UserService
。
/** * @author Codegitz * @date 2022/5/12 15:57 **/ public class ReplaceUserServiceImpl implements UserService { @Override public User getUser(String name, String age) { User user = new User(); user.setId("1"); // 这里更改了赋值 user.setName("ReplaceUser-" + name); user.setAge(age); return user; } }
接着用一个类实现BeanFactoryPostProcessor
。可以看到我这里的逻辑也非常简单,就是判断是否存在一个名为userService
的BeanDefinition
,如果有则把它的实现替换为io.codegitz.service.impl.ReplaceUserServiceImpl
。
/** * @author Codegitz * @date 2022/5/12 16:01 **/ public class ReplaceUserBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { if (beanFactory.containsBeanDefinition("userService")){ BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService"); beanDefinition.setBeanClassName("io.codegitz.service.impl.ReplaceUserServiceImpl"); } } }
后置处理器已经准备好,接下来把后置处理器注册到容器里,我代码例子采用的是xml
方式。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="userService" class="io.codegitz.service.impl.UserServiceImpl"/> <!-- 注册后置处理器 --> <bean id="processor" class="io.codegitz.processor.ReplaceUserBeanFactoryPostProcessor"/> </beans>
到这里一切都完成了,那么就可以启动引导类跑一下看看效果了。可以看到这里的实现已经替换为ReplaceUserServiceImpl
,说明我们的ReplaceUserBeanFactoryPostProcessor
后置处理器是生效了,那么是怎么生效的呢?进入下一节的源码分析。
上面已经通过一个例子实现了功能,那么这个源码里是怎么实现的呢?这里会衔接上一篇文章的源码分析,继续在refresh()
方法里游荡。上一篇已经分析了前三个,这篇会继续往下分析两个方法,分别是postProcessBeanFactory(beanFactory)
和invokeBeanFactoryPostProcessors(beanFactory)
,invokeBeanFactoryPostProcessors(beanFactory)
就是后置处理器的逻辑,衔接了上文的例子。好家伙,互相衔接。
这是一个空方法,留给子类实现,主要是一些web
相关的ApplicationContext
会重写这个方法,传入的是当前容器,操作空间也是非常大的。由于是个空方法,这里不再赘述。
/** * Modify the application context's internal bean factory after its standard * initialization. All bean definitions will have been loaded, but no beans * will have been instantiated yet. This allows for registering special * BeanPostProcessors etc in certain ApplicationContext implementations. * * 在标准初始化之后修改应用程序上下文的内部 bean 工厂。 * 所有 bean 定义都已经被加载,但还没有 bean 被实例化。 * 这允许在某些 ApplicationContext 实现中注册特殊的 BeanPostProcessors 等。 * * @param beanFactory the bean factory used by the application context */ protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { }
接下来就进入了今天的重头戏,跟进代码,可以看到主要的代码实现委托给了PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())
去实现。
/** * Instantiate and invoke all registered BeanFactoryPostProcessor beans, * respecting explicit order if given. * <p>Must be called before singleton instantiation. * * 实例化并调用所有已注册的 BeanFactoryPostProcessor bean,如果给定顺序,则按照顺序去执行 * 所有的后置处理器必须在其他的单例bean实例化之前被调用 */ //实例化并且调用所有BeanFactoryPostProcessor beans protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { // 这个方法会进行两种操作 // 1.把给定的 BeanFactoryPostProcessor 传入执行 // 2.自动扫描容器里的所有的 BeanFactoryPostProcessor 执行 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) // 检测是否有用于类型匹配的临时 ClassLoader 和 LoadTimeWeaver if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) { beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory)); beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader())); } }
跟进PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors())
里面的代码。这个方法比较长,但是逻辑却非常简单,跟着注释看一下理解起来问题不大。
public static void invokeBeanFactoryPostProcessors( ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) { // Invoke BeanDefinitionRegistryPostProcessors first, if any. Set<String> processedBeans = new HashSet<>(); if (beanFactory instanceof BeanDefinitionRegistry) { BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory; // 常规的BeanFactoryPostProcessor后置处理器 List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>(); // 扩展注册BeanDefinition的BeanDefinitionRegistryPostProcessor后置处理器 List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>(); // 根据类型放入不同的后置处理器列表里 for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) { if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) { BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor) postProcessor; registryProcessor.postProcessBeanDefinitionRegistry(registry); 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. // 这里不会初始化FactoryBeans,因为先初始化了的话BeanFactoryPostProcessor就无法对已经初始化的bean生效了 List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>(); // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered. // 首先会调用实现了PriorityOrdered接口的BeanDefinitionRegistryPostProcessors String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered. // 接下来调用实现了Ordered接口的BeanDefinitionRegistryPostProcessors postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false); for (String ppName : postProcessorNames) { if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) { currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class)); processedBeans.add(ppName); } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear. // 最后,调用没有实现上述接口的BeanDefinitionRegistryPostProcessors 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? // 因为这里BeanDefinitionRegistryPostProcessor可能会注册另外的BeanFactoryPostProcessor, // 所以需要循环去迭代,直到当前容器里没有BeanFactoryPostProcessor为止 reiterate = true; } } sortPostProcessors(currentRegistryProcessors, beanFactory); registryProcessors.addAll(currentRegistryProcessors); // 这里可能会继续注册BeanFactoryPostProcessor invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); currentRegistryProcessors.clear(); } // Now, invoke the postProcessBeanFactory callback of all processors handled so far. // 前面已经处理完成了所有的 BeanDefinitionRegistryPostProcessor, // 接下来这两个方法就是处理 BeanFactoryPostProcessor, // 注意这里只是处理了方法传入的 beanFactoryPostProcessors , // 处理完这个后后续的逻辑还是会自动检测当前容器里所有的BeanFactoryPostProcessor,然后分类逐次调用 invokeBeanFactoryPostProcessors(registryProcessors, beanFactory); invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory); } else { // Invoke factory processors registered with the context instance. // 如果给定的beanFactory不是BeanDefinitionRegistry, // 那么就不需要进行前面调用 BeanDefinitionRegistryPostProcessor 的操作, // 直接调用给定的 beanFactoryPostProcessors invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory); } // Do not initialize FactoryBeans here: We need to leave all regular beans // uninitialized to let the bean factory post-processors apply to them! // 这里的逻辑跟上面的 BeanDefinitionRegistryPostProcessor 处理逻辑类似 String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); // Separate between BeanFactoryPostProcessors that implement PriorityOrdered, // Ordered, and the rest. // 按照不同的优先级去分类 BeanFactoryPostProcessor 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. // 首先调用实现了 PriorityOrdered 接口的 BeanFactoryPostProcessor sortPostProcessors(priorityOrderedPostProcessors, beanFactory); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); // Next, invoke the BeanFactoryPostProcessors that implement Ordered. // 其次调用实现了 Ordered 接口的 BeanFactoryPostProcessor 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. // 最后调用没有实现上述接口的 BeanFactoryPostProcessor 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... // 清除缓存的 merged bean definitions 定义,因为后处理器可能已经修改了原始元数据,例如替换值中的占位符... beanFactory.clearMetadataCache(); }
这个方法主要做了以下几件事:
BeanDefinitionRegistry
类型,如果是,则判断给定的beanFactoryPostProcessors
是否存在BeanDefinitionRegistryPostProcessor
类型的后置处理器,如果有则执行postProcessBeanDefinitionRegistry()
方法,进行BeanDefinition
的注册。接着会分别获取当前容器里实现了PriorityOrdered
、Ordered
接口和没有实现排序接口的BeanDefinitionRegistryPostProcessor
,排序后依次执行。BeanDefinitionRegistry
类型,则直接调用invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory)
方法调用所有给定的BeanFactoryPostProcessor
。beanFactoryPostProcessors
,这里会去检测执行容器里存在的BeanFactoryPostProcessor
。这里也会按照PriorityOrdered
和Ordered
接口以及没有实现排序接口的顺序去调用。这个方法的逻辑是比较简单的,就是代码量比较大,需要耐心看完。
接下来看下里面的一些字方法的内容
private static void sortPostProcessors(List<?> postProcessors, ConfigurableListableBeanFactory beanFactory) { // 比较简单,就是获取一个比较器 Comparator ,至于 Comparator 的原理,可以自己去看下相关文章 Comparator<Object> comparatorToUse = null; if (beanFactory instanceof DefaultListableBeanFactory) { comparatorToUse = ((DefaultListableBeanFactory) beanFactory).getDependencyComparator(); } if (comparatorToUse == null) { comparatorToUse = OrderComparator.INSTANCE; } // 排序 postProcessors.sort(comparatorToUse); }
这个方法也比较简单,就是逐个调用一下BeanDefinitionRegistryPostProcessor
后置处理器。
/** * Invoke the given BeanDefinitionRegistryPostProcessor beans. * 调用给定的 BeanDefinitionRegistryPostProcessor bean。 */ private static void invokeBeanDefinitionRegistryPostProcessors( Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) { for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { postProcessor.postProcessBeanDefinitionRegistry(registry); } }
逐个调用一下BeanFactoryPostProcessor
后置处理器。
/** * Invoke the given BeanFactoryPostProcessor beans. * 调用给定的 BeanFactoryPostProcessor bean。 */ private static void invokeBeanFactoryPostProcessors( Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) { for (BeanFactoryPostProcessor postProcessor : postProcessors) { postProcessor.postProcessBeanFactory(beanFactory); } }
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false)
会根据传入的类型获取该类型所有的bean名称。
跟进代码查看,会调用doGetBeanNamesForType()
方法进行获取。
public String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) { // 配置没有冻结 || 类型为空 || 不允许提前初始化 则进入 if (!isConfigurationFrozen() || type == null || !allowEagerInit) { return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit); } Map<Class<?>, String[]> cache = (includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType); String[] resolvedBeanNames = cache.get(type); if (resolvedBeanNames != null) { return resolvedBeanNames; } // 允许提前初始化 resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true); if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) { cache.put(type, resolvedBeanNames); } return resolvedBeanNames; }
跟进doGetBeanNamesForType()
方法,这个方法也比较长,但是逻辑也比较清晰,就是遍历了beanDefinitionNames
判断是否符合类型要求,符合则返回。
private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) { List<String> result = new ArrayList<>(); // Check all bean definitions. // 从所有的 beanDefinitionNames 检查所有符合类型要求的 bean ,加入 result for (String beanName : this.beanDefinitionNames) { // Only consider bean as eligible if the bean name // is not defined as alias for some other bean. // 如果 bean 名称未定义为其他 bean 的别名,则该bean符合要求。 if (!isAlias(beanName)) { try { // 获取 beanName 的 RootBeanDefinition RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // Only check bean definition if it is complete. if (!mbd.isAbstract() && (allowEagerInit || (mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) && !requiresEagerInitForType(mbd.getFactoryBeanName()))) { boolean isFactoryBean = isFactoryBean(beanName, mbd); BeanDefinitionHolder dbd = mbd.getDecoratedDefinition(); // 类型是否匹配 boolean matchFound = false; boolean allowFactoryBeanInit = allowEagerInit || containsSingleton(beanName); boolean isNonLazyDecorated = dbd != null && !mbd.isLazyInit(); // 主要就是调用 isTypeMatch() 方法判断类型是否匹配 if (!isFactoryBean) { if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) { matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit); } } else { if (includeNonSingletons || isNonLazyDecorated || (allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) { matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit); } if (!matchFound) { // In case of FactoryBean, try to match FactoryBean instance itself next. beanName = FACTORY_BEAN_PREFIX + beanName; matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit); } } // 如果匹配则加入 if (matchFound) { result.add(beanName); } } } catch (CannotLoadBeanClassException | BeanDefinitionStoreException ex) { // 省略部分异常处理.. } } } // Check manually registered singletons too. // 检查手动注册的单例bean for (String beanName : this.manualSingletonNames) { try { // In case of FactoryBean, match object created by FactoryBean. if (isFactoryBean(beanName)) { if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) { result.add(beanName); // Match found for this bean: do not match FactoryBean itself anymore. continue; } // In case of FactoryBean, try to match FactoryBean itself next. beanName = FACTORY_BEAN_PREFIX + beanName; } // Match raw bean instance (might be raw FactoryBean). // 匹配则加入 if (isTypeMatch(beanName, type)) { result.add(beanName); } } catch (NoSuchBeanDefinitionException ex) { // 省略部分异常... } } return StringUtils.toStringArray(result); }
到这里BeanFactoryPostProcessor
的源码调用以及相关的逻辑分析完了,是不是比较简单。
这篇文章主要是介绍了BeanFactoryPostProcessor
后置处理器的使用和底层原理。文章开头首先回顾了上一篇文章的内容,然后例子驱动分析,先用简单的例子实现了BeanFactoryPostProcessor
的使用,然后再进行了源码分析,整个文章下来思路是比较清晰的。那么看到这里,有没有解决了文章开头的两个疑问?
如果没有,那不怪你,可能是我写得不好。
今天是汶川地震14周年,生命无常,向遇难的同胞寄托哀思,向参与救援的同胞致敬。
如果有人看到这里,那在这里老话重提。与君共勉,路漫漫其修远兮,吾将上下而求索。