spring4.1.8 扩展实战之七:控制 bean(BeanPostProcessor 接口)
欢迎访问我的 GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本章是《spring4.1.8 扩展实战》的第七篇,我们来尝试在容器初始化的时候对 bean 实例做设置;
控制 bean 的两种扩展方式
两种方式可以对 bean 做控制(例如修改某个成员变量)
改变 bean 的定义(BeanFactoryPostProcessor 接口) ,可以想象成修改了 class 文件,这样实例化出来的每个对象都变了;
只改变实例化的对象(BeanPostProcessor 接口);
上述两种方式都能控制 bean,第一种请参照《spring4.1.8扩展实战之五:改变bean的定义(BeanFactoryPostProcessor接口)》 ,今天我们来研究第二种:改变实例化的对象;
本章概览
全文由以下部分组成:
spring 源码分析,了解 BeanPostProcessor 接口的实现类如何被注册到 spring 容器;
spring 源码分析,了解已经注册到 spring 环境的 BeanPostProcessor 实现类如何被使用;
实战,开发一个 BeanPostProcessor 实现类,验证是否能用来改变指定的 bean;
源码分析:BeanPostProcessor 接口的实现类如何被注册到 spring 容器
从 spring 容器的初始化代码看起吧,看 AbstractApplicationContext 类的 refresh 方法,如下图所示,红框中的方法负责将 BeanPostProcessor 接口的实现类注册到 spring 容器:
展开 registerBeanPostProcessors 方法,发现是委托 PostProcessorRegistrationDelegate 类的静态方法 registerBeanPostProcessors 来完成注册工作的:
展开 PostProcessorRegistrationDelegate.registerBeanPostProcessors 方法,里面的代码逻辑简洁整齐,意图目的一目了然:
以上代码已加注释,就不多解读了,有一点需要注意:对于实现了 MergedBeanDefinitionPostProcessor 接口的 bean,在前面几次调用 registerBeanPostProcessors 方法的时候已经注册过了,那么在最后执行的==registerBeanPostProcessors(beanFactory, internalPostProcessors)==,岂不是将一个 bean 注册了多次?
为了弄清楚这个问题需要看 registerBeanPostProcessors 方法:
addBeanPostProcessor 方法的代码在 AbstractApplicationContext 类中,如下所示,==先删除再添加,这样反复注册也没有问题==:
至此,我们已经弄清楚了 BeanPostProcessor 实现类的 bean 注册到 spring 容器的逻辑,接下来看看 spring 容器如何使用这些 bean;
源码分析,BeanPostProcessor 实现类如何被使用
要弄清楚 BeanPostProcessor 接口的实现类是在哪里被用到的,还是从负责容器初始化的 AbstractApplicationContext 类的 refresh 方法看起,如下图红框中的 finishBeanFactoryInitialization 方法,就是负责实例化和初始化 bean 的:
从 finishBeanFactoryInitialization 方法到 BeanPostProcessor 的实现类被使用,中间有多层逻辑和调用,篇幅所限就不逐个展开了,直接列出堆栈信息,您可以根据此信息去查看对应源码:
根据上述堆栈信息,直接查看 AbstractAutowireCapableBeanFactory 类的 initializeBean()方法:
如上所示,我们最关注的代码是==applyBeanPostProcessorsBeforeInitialization==和==applyBeanPostProcessorsAfterInitialization==这两个方法,它们分别在 bean 的初始化方法==invokeInitMethods==的前后被执行;
先看看 applyBeanPostProcessorsBeforeInitialization 方法,逻辑非常简单,就是取出所有已注册的 BeanPostProcessor 实现类,执行其 postProcessBeforeInitialization 方法,入参是当前正在做实例化和初始化的 bean 实例:
在 invokeInitMethods 方法执行完毕后会执行 applyBeanPostProcessorsAfterInitialization 方法,代码如下,与 applyBeanPostProcessorsBeforeInitialization 如出一辙,仅有的不同是调用的 beanProcessor 的方法变成了 postProcessAfterInitialization:
以上就是 spring 容器初始化过程中对 BeanPostProcessor 实现类的使用场景,此时还剩一点疑问需要澄清:==在分析注册过程的时候,曾看到实现 MergedBeanDefinitionPostProcessor 接口的 bean 是最后注册的,那么这些 MergedBeanDefinitionPostProcessor 实现类在 spring 容器中是何处使用的呢?==
为了搞清这个问题,来看看 AbstractAutowireCapableBeanFactory 类的 doCreateBean 方法,前面我们分析的 initializeBean 方法就是在这里面被调用的:
如上图所示,红框中就是用所有 MergedBeanDefinitionPostProcessor 实现类去处理当前正在实例化的 bean,然后才会执行绿框中的 initializeBean 方法(里面是我们刚才分析的 bean 的初始化,BeanPostProcessor 实现类被使用的逻辑);
来看看红框中的 applyMergedBeanDefinitionPostProcessors 方法,主要目的是处理特殊的合成 bean 的定义类:
至此,我们对 BeanPostProcessor 有关的源码分析就完成了,小结一下:
初始化时,spring 容器有特别处理,会直接调用 beanFactory.addBeanPostProcessor 进行注册(例如 AbstractApplicationContext 类的 prepareBeanFactory 方法中就有);
找出所有实现了 BeanPostProcessor 接口的 bean,注册到容器,注册顺序如下:第一:实现了 PriorityOrdered 接口的,排序后;第二:实现了 Ordered 接口的,排序后;第三:既没实现 PriorityOrdered 接口,也没有实现 Ordered 接口的;第四:实现了 MergedBeanDefinitionPostProcessor 接口的(这些也按照 PriorityOrdered、Ordered 等逻辑拍过续);第五:实例化一个 ApplicationListenerDetector 对象;
实例化 bean 的时候,对于每个 bean,先用 MergedBeanDefinitionPostProcessor 实现类的 postProcessMergedBeanDefinition 方法处理每个 bean 的定义类;
再用 BeanPostProcessor 的 postProcessBeforeInitialization 方法处理每个 bean 实例;
bean 实例初始化;
用 BeanPostProcessor 的 postProcessAfterInitialization 方法处理每个 bean 实例;
源码分析结束,接下来自定义一个 BeanPostProcessor 实现类,验证我们之前的分析:控制 bean 实例;
实战自定义 BeanPostProcessor 实现类
本次实战的内容是创建一个 springboot 工程,在里面自定义一个 BeanPostProcessor 接口的实现类,如果您不想敲代码,也可以去 github 下载源码,地址和链接信息如下表所示:
这个 git 项目中有多个文件夹,本章源码在文件夹 customizebeanpostprocessor 下,如下图红框所示:
接下来开始实战吧:
基于 maven 创建一个 springboot 的 web 工程,名为 customizebeanpostprocessor,pom.xml 如下:
创建 Utils.java,里面有一些静态的工具方法,例如打印当前堆栈:
创建接口 CalculateService,定义了几个方法:
创建 CalculateService 的实现类 CalculateServiceImpl,并且用 @Service 注解将定义为 spring 的 bean:
注意该类的成员变量 serviceDesc,值已经固定为"desc from class";
创建 Controller 类 HelloController,在响应 web 请求的时候,会用到 calculateService 提供的服务:
如上所示,正常情况下,web 响应为"add result : x, from [desc from class]",这个==desc from class==,就是 CalculateServiceImpl 的成员变量 serviceDesc 的值;
创建 BeanPostProcessor 的实现类 CustomizeBeanPostProcessor,这里面会修改 calculateService 实例的成员变量 serviceDesc 的值,记得用注解 @Component 将其设置为 bean:
启动应用,在启动日志中可以看到 CustomizeBeanPostProcessor 实例的两个方法被调用时打印的堆栈日志,和之前我们分析的源码是一致的:
在浏览器访问地址:http://localhost:8080/add/1/2,可以看到响应如下图所示,calculateService 的成员变量 serviceDesc 的值已经被 CustomizeBeanPostProcessor 改为==desc from CustomizeBeanPostProcessor==:
至此,本次实战就完成了,从 spring 源码分析再到动手实战,我们对 BeanPostProcessor 的扩展有了更深入的认识,也希望这种扩展能帮助您更好的控制 bean 实例为业务需求服务;
欢迎关注 InfoQ:程序员欣宸
版权声明: 本文为 InfoQ 作者【程序员欣宸】的原创文章。
原文链接:【http://xie.infoq.cn/article/4f802986dd6b9fad3fef3cec7】。文章转载请联系作者。
评论