spring4.1.8 扩展实战之六:注册 bean 到 spring 容器 (BeanDefinitionRegistryPostProcessor 接口)
欢迎访问我的 GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本章是《spring4.1.8 扩展实战》系列的第六篇,目标是学习如何通过自己写代码的方式,向 spring 容器中注册 bean;
关于注册 bean 到容器
我们开发的类,如果想注册到 spring 容器,让 spring 来完成实例化,常用方式如下:
xml 中通过 bean 节点来配置;
使用 @Service、@Controller、@Conponent 等注解;
其实,除了以上方式,spring 还支持我们通过代码来将指定的类注册到 spring 容器中,也就是今天我们要实践的主要内容,接下来就从 spring 源码开始,先学习源码再动手实战;
本章概要
本章由以下几部分组成:
了解 BeanDefinitionRegistryPostProcessor 接口;
分析 spring 容器如何使用 BeanDefinitionRegistryPostProcessor 接口;
实战,开发 BeanDefinitionRegistryPostProcessor 接口的实现类,验证通过代码注册 bean 的功能;
了解 BeanDefinitionRegistryPostProcessor 接口
实现注册 bean 功能的关键是 BeanDefinitionRegistryPostProcessor 接口,来看看这接口的继承关系,如下图:
BeanDefinitionRegistryPostProcessor 继承了 BeanFactoryPostProcessor 接口,关于 BeanFactoryPostProcessor 我们在上一章《spring4.1.8扩展实战之五:改变bean的定义(BeanFactoryPostProcessor接口)》已做了详细的分析和实战,知道 BeanFactoryPostProcessor 的实现类在其 postProcessBeanFactory 方法被调用时,可以对 bean 的定义进行控制,因此 BeanDefinitionRegistryPostProcessor 的实现类一共要实现以下两个方法:
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException:该方法的实现中,主要用来对 bean 定义做一些改变,这些在上一章《spring4.1.8扩展实战之五:改变bean的定义(BeanFactoryPostProcessor接口)》有详细说明;
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException:该方法用来注册更多的 bean 到 spring 容器中,详细观察入参 BeanDefinitionRegistry 接口,看看这个参数能带给我们什么能力:
从上图可以看到,为了能让我们通过代码将 bean 注册到 spring 环境,BeanDefinitionRegistry 提供了丰富的方法来操作 bean 定义,判断、注册、反注册等方法都准备好了,我们在编写 postProcessBeanDefinitionRegistry 方法的内容时,就能直接使用入参 registry 的这些方法来完成判断和注册、反注册等操作;
分析 spring 容器如何使用 BeanDefinitionRegistryPostProcessor 接口
来看看 BeanDefinitionRegistryPostProcessor 接口的实现类,是在哪里被 spring 容器使用的:
如下图所示,红框中的 invokeBeanFactoryPostProcessors 方法用来找出所有 beanFactory 后置处理器,并且调用这些处理器来改变 bean 的定义:
打开 invokeBeanFactoryPostProcessors 方法,如下所示,实际操作是委托 PostProcessorRegistrationDelegate 去完成的:
继续看 PostProcessorRegistrationDelegate 类的 invokeBeanFactoryPostProcessors 方法,该方法内容太丰富,我们只看重点,第一个重点如下图红框所示,==当前的 beanFactory 是否实现了接口 BeanDefinitionRegistry==:
为了搞清楚这个问题,我们应该看看当前 beanFactory 的继承和实现,以 springboot 中的应用为例,当前 beanFactory 的类型是 DefaultListableBeanFactory,来看看它的类图:
从上图红框可见,beanFactory 实现了 BeanDefinitionRegistry 接口,因此我们的关注点是 if 条件满足后的执行逻辑;
继续看 PostProcessorRegistrationDelegate 类的 invokeBeanFactoryPostProcessors 方法,以下片段就是操作 BeanDefinitionRegistryPostProcessor 的核心逻辑:
如上述代码所示,所有实现了 BeanDefinitionRegistryPostProcessor 接口的 bean,其 postProcessBeanDefinitionRegistry 方法都会调用,然后再调用其 postProcessBeanFactory 方法,这样一来,我们如果自定义了 BeanDefinitionRegistryPostProcessor 接口的实现类,那么我们开发的 postProcessBeanDefinitionRegistry 和 postProcessBeanFactory 方法都会被执行一次;
到这里,我们的源码学习部分就完成了,接下来看开始实战吧;
实战,开发 BeanDefinitionRegistryPostProcessor 接口的实现类
本次实战的内容是创建一个 springboot 工程,在里面自定义一个 BeanDefinitionRegistryPostProcessor 接口的实现类,如果您不想敲代码,也可以去 github 下载源码,地址和链接信息如下表所示:
这个 git 项目中有多个文件夹,本章源码在文件夹 customizebeandefinitionregistrypostprocessor 下,如下图红框所示:
接下来开始实战吧:
基于 maven 创建一个 springboot 的 web 工程,名为 customizebeandefinitionregistrypostprocessor,pom.xml 如下:
定义一个服务接口 CalculateService:
创建 CalculateService 接口的实现类 CalculateServiceImpl,==注意,不要将其声明为 spring 的 bean==:
创建工具类 Utils,里面是些工具方法,例如在日志中打印出当前线程的执行堆栈:
创建 Controller 类,用于验证我们通过代码注册的 bean,==注意要给成员变量 calculateService 添加注解 @Autowired(required = false),否则有的 IDEAL 上会有红叉提示==:
创建 BeanDefinitionRegistryPostProcessor 的实现类 CustomizeBeanDefinitionRegistryPostProcessor:
上面 CustomizeBeanDefinitionRegistryPostProcessor 就是我们扩展出来的 BeanDefinitionRegistryPostProcessor 实现类,用来将 CalculateServiceImpl 注册到 spring 容器,名称为 calculateService;
启动类 CustomizebeandefinitionregistrypostprocessorApplication:
启动应用,在启动日志中可以看到 CustomizeBeanDefinitionRegistryPostProcessor 的方法被调用时的堆栈情况:
在浏览器输入:http://localhost:8080/add/1/2,如下图可以看到网页正常响应,controller 可以正常使用 calculateService 实例:
去掉 CustomizeBeanDefinitionRegistryPostProcessor 的注释 @Component,重启应用,再去访问 http://localhost:8080/add/1/2,可以看到网页提示错误如下图:
看后台日志,是因为 calculateService 这个 bean 为空,导致 HelloController 在响应请求的时候出现了空指针异常,这也再次证明了 CustomizeBeanDefinitionRegistryPostProcessor 的注册操作是有效的:
至此,自定义 bean 注册的实战就结束了,其实除了注册 bean,我们还能利用 BeanDefinitionRegistry 这个入参做些其他事情,例如查找 bean,反注册 bean 等,帮助我们实现一些 bean 操作的业务需求;
欢迎关注 InfoQ:程序员欣宸
版权声明: 本文为 InfoQ 作者【程序员欣宸】的原创文章。
原文链接:【http://xie.infoq.cn/article/bc1935d99cfcf5123239099d7】。文章转载请联系作者。
评论