简单分析BeanPostProcessor
1. 什么是BeanPostProcessor
BeanPostProcessor是一个接口,有两个方法,分别是:Object postProcessBeforeInitialization(Object var1, String var2) throws BeansException 和 Object postProcessAfterInitialization(Object var1, String var2) throws BeansException;
Spring Bean的生命周期中,在为Bean实例化,装配好属性后,会调用上下文中所有的BeanPostProcessor对象的两个方法为其初始化;
2. 一个小例子
分别创建三个类,分别是接口类、接口类的实现类,和BeanPostProcessor的实现类。
package com.khlin.my.test;
public interface WelcomeService {
void welcome();
}
package com.khlin.my.test;
import org.springframework.beans.factory.InitializingBean;
public class WelcomeServiceImpl implements WelcomeService, InitializingBean {
public void init() {
System.out.println("init.");
}
public void welcome() {
System.out.println("Welcome to Spring.");
}
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet.");
}
}
package com.khlin.my.test; import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor; public class LoginProcessor implements BeanPostProcessor { public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
System.out.println("login successfully.");
return o;
} public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
System.out.println("logout successfully.");
return o;
}
}
在applicationContext.xml里实例化对应的Bean.
<bean id="welcomeService" class="com.khlin.my.test.WelcomeServiceImpl" init-method="init"/>
<bean id="loginProcessor" class="com.khlin.my.test.LoginProcessor"/>
再写一个启动类
package com.khlin.my.test; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class IOCTest { public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
System.out.println("context 启动成功");
WelcomeService messageService = context.getBean(WelcomeService.class);
messageService.welcome();
}
}
启动main方法,输出如下:

调用顺序分别为:
BeanPostProcessor的postProcessBeforeInitialization
InitializingBean的afterPropertiesSet
init方法
BeanPostProcessor的postProcessAfterInitialization
3. 实现原理
我们来看一下Spring启动一个上下文的时候,都做了啥。这里不作详细的源码解读。

可以看到上下文ApplicationContext持有一个BeanFactory。在第一个红框,将所有的BeanPostProcessor注册到BeanFactory。
调试代码,可以看到注册后保存在BeanFactory的beanPostProcessors集合里。

再来看第二个红框,finishBeanFactoryInitialization() 这个方法会对Bean进行初始化。
在AbstractAutowireCapableBeanFactory这个类的protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd)方法里,可以看到invokeInitMethods方法被夹在BeanPostProcessor两个方法的中间。

在applyBeanPostProcessorsBeforeInitialization和applyBeanPostProcessorsAfterInitialization方法中,会遍历所有注册的BeanBeanProcessor并调用方法。
每个BeanPostProcessor可以返回处理后的对象,如果返回null,会导致遍历中断,可能有些BeanPostProcessor无法处理,这点要注意。

InitMethod有两种方式,一种是在配置文件中加上init-method属性并指定对应的方法,另一种是实现InitializingBean接口的afterPropertiesSet()方法。
可以看到是优先调用afterPropertiesSet()方法,再调用init-method指定的方法,这与我们的输出顺序一致。

4. 总结
Spring Bean的生命周期,在初始化阶段的调用顺序为:
BeanPostProcessor的postProcessBeforeInitialization
InitializingBean的afterPropertiesSet
init方法
BeanPostProcessor的postProcessAfterInitialization
如果有一个BeanPostProcessor返回null,会导致遍历的中断,可能有些BeanPostProcessor无法调用。因此一般不返回null.
简单分析BeanPostProcessor的更多相关文章
- AbstractRoutingDataSource 实现动态数据源切换原理简单分析
AbstractRoutingDataSource 实现动态数据源切换原理简单分析 写在前面,项目中用到了动态数据源切换,记录一下其运行机制. 代码展示 下面列出一些关键代码,后续分析会用到 数据配置 ...
- 简单分析JavaScript中的面向对象
初学JavaScript的时候有人会认为JavaScript不是一门面向对象的语言,因为JS是没有类的概念的,但是这并不代表JavaScript没有对象的存在,而且JavaScript也提供了其它的方 ...
- CSipSimple 简单分析
简介 CSipSimple是一款可以在android手机上使用的支持sip的网络电话软件,可以在上面设置使用callda网络电话.连接使用方式最好是使用wifi,或者3g这样上网速度快,打起电话来效果 ...
- C#中异常:“The type initializer to throw an exception(类型初始值设定项引发异常)”的简单分析与解决方法
对于C#中异常:“The type initializer to throw an exception(类型初始值设定项引发异常)”的简单分析,目前本人分析两种情况,如下: 情况一: 借鉴麒麟.NET ...
- 透过byte数组简单分析Java序列化、Kryo、ProtoBuf序列化
序列化在高性能网络编程.分布式系统开发中是举足轻重的之前有用过Java序列化.ProtocolBuffer等,在这篇文章这里中简单分析序列化后的byte数组观察各种序列化的差异与性能,这里主要分析Ja ...
- 简单分析Java的HashMap.entrySet()的实现
关于Java的HashMap.entrySet(),文档是这样描述的:这个方法返回一个Set,这个Set是HashMap的视图,对Map的操作会在Set上反映出来,反过来也是.原文是 Returns ...
- Ffmpeg解析media容器过程/ ffmpeg 源代码简单分析 : av_read_frame()
ffmpeg 源代码简单分析 : av_read_frame() http://blog.csdn.net/leixiaohua1020/article/details/12678577 ffmpeg ...
- FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分
===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...
- FFmpeg资料来源简单分析:libswscale的sws_getContext()
===================================================== FFmpeg库函数的源代码的分析文章: [骨架] FFmpeg源码结构图 - 解码 FFmp ...
随机推荐
- ArcGIS Server“无法创建站点,计算机不具有有效的的许可”
问题描述 ArcGIS Server10.5安装过程中,所有授权和破解均已完成,但是最后一步创建站点的时候显示创建失败,会出现如下图所示的问题:既“无法创建站点,计算机不具有有效的的许可…”,经历了卸 ...
- docker常用指令
1.查看docker信息 docker system df 2.删除镜像 docker rmi --删除镜像 docker image prune --删除虚悬镜像 3.守护态运行 docker ru ...
- KVM——以桥接的方式搭建虚拟机网络配置
以桥接的方式搭建虚拟机网络,其优势是可以将网络中的虚拟机看作是与主机同等地位的服务器. 在原本的局域网中有两台主机,一台是win7(IP: 192.168.0.236),一台是CentOS7(IP: ...
- recyclerView 嵌套 recyclerView 点击被拦截
上层recyclerView.setLayoutFrozen(true);下层可以接收到点击事件.
- Python3中_和__的用途和区别
访问可见性问题 对于上面的代码,有C++.Java.C#等编程经验的程序员可能会问,我们给Student对象绑定的name和age属性到底具有怎样的访问权限(也称为可见性).因为在很多面向对象编程语言 ...
- Qt编写自定义控件23-广告轮播控件
一.前言 广告轮播这个控件做的比较早,是很早以前定制一个电信客户端时候用到的,该客户端需要在首页展示轮播预先设定好的图片,图片的路径可以自由设定,然后轮播的间隔速度可以自由控制,同时该控件还需要提供两 ...
- javascript两个数组内容合并
需求: ,,]; ,,]; 最终结果: [,,,,,] 代码: Array.prototype.addAll= function(arr) { this.push.apply(this, arr); ...
- LODOP中用ADD_PRINT_IMAGE缩放非图片超文本
LODOP中HTML,URL,带有img标签的IMAGE,TBALE等打印项都属于超文本.关于LODOP中的纯文本和超文本,可查看本博客相关博文:LODOP中的纯文本和超文本打印项 ADD_PRINT ...
- golang web框架设计4:日志设计
beego的日志设计思路来自于seelog,根据不同的level来记录日志,beego设计的日志是一个轻量级的,采用系统log.Logger接口,默认输出到os.Stdout,用户可以实现这个接口然后 ...
- python-Web-flask-数据库
3 数据库: Flask-SQLAlchemy 安装及连接 pip install flask-sqlalchemy pip install flask-mysqldb # 数据库链接地址 app.c ...