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的更多相关文章

  1. AbstractRoutingDataSource 实现动态数据源切换原理简单分析

    AbstractRoutingDataSource 实现动态数据源切换原理简单分析 写在前面,项目中用到了动态数据源切换,记录一下其运行机制. 代码展示 下面列出一些关键代码,后续分析会用到 数据配置 ...

  2. 简单分析JavaScript中的面向对象

    初学JavaScript的时候有人会认为JavaScript不是一门面向对象的语言,因为JS是没有类的概念的,但是这并不代表JavaScript没有对象的存在,而且JavaScript也提供了其它的方 ...

  3. CSipSimple 简单分析

    简介 CSipSimple是一款可以在android手机上使用的支持sip的网络电话软件,可以在上面设置使用callda网络电话.连接使用方式最好是使用wifi,或者3g这样上网速度快,打起电话来效果 ...

  4. C#中异常:“The type initializer to throw an exception(类型初始值设定项引发异常)”的简单分析与解决方法

    对于C#中异常:“The type initializer to throw an exception(类型初始值设定项引发异常)”的简单分析,目前本人分析两种情况,如下: 情况一: 借鉴麒麟.NET ...

  5. 透过byte数组简单分析Java序列化、Kryo、ProtoBuf序列化

    序列化在高性能网络编程.分布式系统开发中是举足轻重的之前有用过Java序列化.ProtocolBuffer等,在这篇文章这里中简单分析序列化后的byte数组观察各种序列化的差异与性能,这里主要分析Ja ...

  6. 简单分析Java的HashMap.entrySet()的实现

    关于Java的HashMap.entrySet(),文档是这样描述的:这个方法返回一个Set,这个Set是HashMap的视图,对Map的操作会在Set上反映出来,反过来也是.原文是 Returns ...

  7. Ffmpeg解析media容器过程/ ffmpeg 源代码简单分析 : av_read_frame()

    ffmpeg 源代码简单分析 : av_read_frame() http://blog.csdn.net/leixiaohua1020/article/details/12678577 ffmpeg ...

  8. FFmpeg的HEVC解码器源码简单分析:解析器(Parser)部分

    ===================================================== HEVC源码分析文章列表: [解码 -libavcodec HEVC 解码器] FFmpeg ...

  9. FFmpeg资料来源简单分析:libswscale的sws_getContext()

    ===================================================== FFmpeg库函数的源代码的分析文章: [骨架] FFmpeg源码结构图 - 解码 FFmp ...

随机推荐

  1. oracle清除归档

    清除Oracle归档日志命令echo -e 'delete noprompt archivelog ALL COMPLETED BEFORE '\'SYSDATE-${DELETE_ARCHIVELO ...

  2. ASP.NET的MVC设计模式

    当开发者听到“设计模式”这个词时,他们通常联想到两个场景.一组开发者正在讨论许多创造性意见,正在开会,但是却没有进行编码.另外一组人能制定出正确的计划,保证系统能够开发成功,代码可以重用. 而现实一般 ...

  3. 经典算法:n个人围成一圈,报m的离开,最后剩下谁?

    public int remainPersonNumber(int n, int m) { //输入不合法 if(n < 1 || m < 1) return -1; //初始化,存入Li ...

  4. JAVA安全漫谈1-8笔记

    一.反射篇1 classloader就是java的类加载器,告诉虚拟机如何加载这个类.默认情况下根据类名来加载类,类名必须是完整路径 public class class_init { { Syste ...

  5. 重读APUE(12)-SIGCHLD与僵尸进程

    SIGCHLD信号是当子进程终止时向父进程发送的信号:它的语义如下: 如果进程明确的将该信号设置为SIG_IGN,则调用进程不会产生僵尸进程:这种情况下,wait是等不到给子进程收尸的,所以wait阻 ...

  6. nc简单使用

    1.安装 2.运行

  7. mysql大数据量下优化

    1 优化sql和索引2 增加缓存如:redis3 主从复制或主主复制,读写分离4 利用mysql自带分区表5 先做垂直拆分,将一个大系统分为多个小系统,也就是分布式6 水平切分,要选择一个合理的sha ...

  8. linux查看文件具体时间和大小

    查看具体时间 ll --full-time 查看文件大小: ll -ht 或者du -sh *

  9. Hackertarget:一款发现攻击面的工具

    前言 https://github.com/ismailtasdelen/hackertarget 代码 主要通过这家公司提供的API查询相关数据实现的功能,API看起来可以用很久. #!/usr/b ...

  10. Flutter参数的传递和接收

    上次只写了方法和参数,这次写了完整的示例,页面间参数的传递和接收的示例. 1.参数传递 用在程序上解释就是比如你进入一个商品选择列表,当你想选择一个商品的具体信息的时候,你就要传递商品编号,详细页面接 ...