spring4.1.8 扩展实战之五:改变 bean 的定义 (BeanFactoryPostProcessor 接口)
欢迎访问我的 GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览
本章我们继续实战 spring 的扩展能力,通过自定义 BeanFactoryPostProcessor 接口的实现类,来对 bean 实例做一些控制;
BeanFactoryPostProcessor 接口简介
spring 容器初始化时,从资源中读取到 bean 的相关定义后,保存在 beanFactory 的成员变量中(参考 DefaultListableBeanFactory 类的成员变量 beanDefinitionMap),在实例化 bean 的操作就是依据这些 bean 的定义来做的,而在实例化之前,spring 允许我们通过自定义扩展来改变 bean 的定义,定义一旦变了,后面的实例也就变了,而 beanFactory 后置处理器,即==BeanFactoryPostProcessor==就是用来改变 bean 定义的;
源码分析
一起来看看上述功能对应的源码,从 AbstractApplicationContext 类的 refresh 方法看起,这里面对应着容器初始化的基本操作;
如下图所示,红框中的 invokeBeanFactoryPostProcessors 方法用来找出所有 beanFactory 后置处理器,并且调用这些处理器来改变 bean 的定义:
打开 invokeBeanFactoryPostProcessors 方法,如下所示,实际操作是委托 PostProcessorRegistrationDelegate 去完成的:
在调用 PostProcessorRegistrationDelegate 类的 invokeBeanFactoryPostProcessors 方法时,注意第二个入参是 getBeanFactoryPostProcessors()方法,该方法返回的是 applicationContext 的成员变量 beanFactoryPostProcessors,该成员变量的值是哪里设置的呢?查找后发现,来自 AbstractApplicationContext.addBeanFactoryPostProcessor 方法被调用的时候:
AbstractApplicationContext.addBeanFactoryPostProcessor 方法是留给业务扩展时调用的,例如在 springboot 初始化时,ConfigurationWarningsApplicationContextInitializer 类的 initialize 方法中就有调用:
看过了如何添加 BeanFactoryPostProcessor,再回到 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors 方法,看看如何处理这些 BeanFactoryPostProcessor,整个 invokeBeanFactoryPostProcessors 太大,不在此粘贴所有代码了,主要是分成七段来看分析这个方法:
第一段,入参中的 BeanFactoryPostProcessor,按照是否实现了 BeanDefinitionRegistryPostProcessor,分别放入两个集合:registryProcessors 和 regularPostProcessors;
第二段,找出所有实现了 BeanDefinitionRegistryPostProcessor 接口和 PriorityOrdered 接口的 bean,放入 registryProcessors 集合,放入根据 PriorityOrdered 接口来排序,然后这些 bean 会被 invokeBeanDefinitionRegistryPostProcessors 方法执行;
第三段,找出所有实现了 BeanDefinitionRegistryPostProcessor 接口和 Ordered 接口的 bean,放入 registryProcessors 集合,放入根据 PriorityOrdered 接口来排序,然后这些 bean 会被 invokeBeanDefinitionRegistryPostProcessors 方法执行;
第四段,对于那些实现了 BeanDefinitionRegistryPostProcessor 接口,但是没有实现 PriorityOrdered 和 Ordered 的 bean 也被找出来,然后这些 bean 会被 invokeBeanDefinitionRegistryPostProcessors 方法执行;
第五段,入参中的 BeanFactoryPostProcessor,没有实现 BeanDefinitionRegistryPostProcessor 的那些 bean,被 invokeBeanDefinitionRegistryPostProcessors;
第六段,接下来的代码需要重点关注:==找出实现了 BeanFactoryPostProcessor 接口的 bean,注意这里已将面实现了 BeanDefinitionRegistryPostProcessor 接口的 bean 给剔除了,将这些 bean 分为三类:实现了 PriorityOrdered 接口的放入 priorityOrderedPostProcessors,实现了 Ordered 接口的放入 orderedPostProcessorNames,其他的放入 nonOrderedPostProcessorNames,这段代码是关键,因为我们自定义的实现 BeanFactoryPostProcessor 接口的 bean 就会在此处被找出来==,如下:
第七段,priorityOrderedPostProcessors 和 orderedPostProcessorNames 这两个集合,都是先做排序再调用 invokeBeanDefinitionRegistryPostProcessors 方法,最后是 nonOrderedPostProcessorNames 集合,也被传入 invokeBeanDefinitionRegistryPostProcessors 方法;
从上面的分析可以发现,所有实现了 BeanFactoryPostProcessor 接口的 bean,都被作为入参,然后调用了 invokeBeanDefinitionRegistryPostProcessors 或者 invokeBeanFactoryPostProcessors 方法去处理,来看看这两个方法:
如上所示,两个方法都很简单,对每个 BeanFactoryPostProcessor 接口的实现类,都调用了其接口方法,不同的是,对于实现了 BeanDefinitionRegistryPostProcessor 接口的 bean,调用其 postProcessBeanDefinitionRegistry 方法的时候,入参是 BeanDefinitionRegistry,而非 BeanFactory,因此,实现了 BeanDefinitionRegistryPostProcessor 接口的 bean,其 postProcessBeanDefinitionRegistry 在被调用时,可以通过入参 BeanDefinitionRegistry 来做更多和 bean 的定义有关的操作,例如注册 bean;
至此,对 BeanFactoryPostProcessor 的处理流程就全部分析完了,这里小结一下:
ApplicationContext 扩展类可以调用 AbstractApplicationContext.addBeanFactoryPostProcessor 方法,将自定义的 BeanFactoryPostProcessor 实现类保存到 ApplicationContext 中;
spring 容器初始化时,上一步中被加入到 ApplicationContext 的 bean 会被优先调用其 postProcessBeanFactory 方法;
自定义的 BeanFactoryPostProcessor 接口实现类,也会被找出来,然后调用其 postProcessBeanFactory 方法;
postProcessBeanFactory 方法被调用时,beanFactory 会被作为参数传入,自定义类中可以使用该参数来处理 bean 的定义,达到业务需求;
此时的 spring 容器还没有开始实例化 bean,因此自定义的 BeanFactoryPostProcessor 实现类不要做与 bean 实例有关的操作,而是做一些与 bean 定义有关的操作,例如修改某些字段的值,这样后面实例化的 bean 的就会有相应的改变;
实战 BeanFactoryPostProcessor 接口的实现类
本次实战的内容是创建一个 springboot 工程,在里面自定义一个 BeanFactoryPostProcessor 接口的实现类,如果您不想敲代码,也可以去 github 下载源码,地址和链接信息如下表所示:
这个 git 项目中有多个文件夹,本章源码在文件夹 customizebeanfactorypostprocessor 下,如下图红框所示:
接下来开始实战吧:
基于 maven 创建一个 springboot 的 web 工程,名为 customizebeanfactorypostprocessor,pom.xml 如下:
定义一个服务接口 CalculateService:
创建 CalculateService 接口的实现类 CalculateServiceImpl,==注意要在 Service 注解上明确写入 bean 的名称==:
创建一个 BeanFactoryPostProcessor 接口的实现类 CustomizeBeanFactoryPostProcessor,并且用注解 Component 将其定义为 spring 环境中的 bean:
上述代码的功能很简单,找到名为"calculateService"的 bean 的定义对象,通过调用 addPropertyValue 方法,将定义中的 desc 属性值改为"Desc is changed from bean factory post processor",这样等名为"calculateService"的 bean 被实例化出来后,其成员变量 desc 的值就是"Desc is changed from bean factory post processor";
创建启动类 CustomizebeanfactorypostprocessorApplication:
启动应用,浏览器输入地址:http://localhost:8080/add/1/2,看到的响应如下图,红框中就是 CustomizeBeanFactoryPostProcessor 对名为 calculateService 的 bean 的定义对象修改后导致的结果:
至此,BeanFactoryPostProcessor 的源码分析和扩展实战就结束了,通过本次实战,除了对 spring 扩展的认识加深,又掌握了一种控制 bean 的方式;
欢迎关注 InfoQ:程序员欣宸
版权声明: 本文为 InfoQ 作者【程序员欣宸】的原创文章。
原文链接:【http://xie.infoq.cn/article/dcd147bfe050d9732bfecd62e】。文章转载请联系作者。
评论