BeanPostProcessor接口的理解

好久没有写博客了,看了一眼自己的文章列表,上次写还是8月份的时候,9月份转载了两篇。今天想起来写一篇,是因为自己纠正了对BeanPostProcessor接口的理解误区,写文章往往都是源于这种豁然开朗的灵感。不过今天又是孤陋寡闻的一天呢,一个知识点理解错了这么长时间居然都不自知。

Bean的生命周期应该都很清楚,先贴这张图:

这张Bean生命周期顺序图里大部分环节都是比较好理解的,比如setBeanName和setBeanFactory包括setApplicationContext,都是实现了对应的接口,就可以在实例化这个Bean的时候为这个Bean设置BeanName,或者注入BeanFactory和ApplicationContext,可以用于获取IOC容器中的其他Bean等。但是实现了BeanPostProcessor接口不能按这个逻辑去理解,先看一下实现BeanPostProcessor接口后的重写哪两个方法:

之前我一直是按这个理解逻辑去理解实现了BeanPostProcessor接口,理解误区是:MyBeanPost这个类实现了BeanPostProcessor接口,实例化的MyBeanPost的时候就会去调用该类里重写的postProcessBeforeInitialization()和postProcessAfterInitialization()方法,这两个方法里拿到的beanName就是@Component里定义的"myBeanPost",Object类型的bean就是MyBeanPost对象,然后实例化MyBeanPost对象前后去在这两个方法里做点什么。

之前一直是这么理解的,其实一直是有疑惑的,因为按这么理解,postProcessBeforeInitialization()方法能做的在自定义的@PostConstruct方法里也能做,那这两个就区分不开了。虽然有疑惑但自己也没有去试过,直到今天项目开发的时候真的想用postProcessAfterInitialization()方法去在初始化完一个Bean的时候注入点东西的时候,一试傻眼了。直接贴上图那种写法时的启动日志:

 

如果按我之前那么理解,这里应该只打印出"myBeanPost执行了postProcessBeforeInitialization"和"myBeanPost执行了postProcessAfterInitialization"才对啊,居然打印出了这么多,而且我全局搜了一下,偏偏没有beanName是"myBeanPost"的日志记录。这个时候我才知道之前我一直理解错了,于是重视起来开始找原因。

Spring的源码一顿翻之后找到了Spring初始化Bean的一段代码:

这里的invokeInitMethods()就是反射调用我们自定义的初始化方法,即顺序图中的第八步,可以清楚的看到applyBeanPostProcessorsBeforeInitialization()方法在前,applyBeanPostProcessorsAfterInitialization()方法在后,这似乎也和顺序图中执行postProcessBeforeInitialization()在执行自定义初始化方法前,执行postProcessAfterInitialization()在后对应上了,继续点进去看,先看applyBeanPostProcessorsBeforeInitialization()方法:

需要注意这里existingBean参数是正在实例化的Bean,这里的getBeanPostProcessors()方法是去拿所有实现了BeanPostProcessor接口的实现类的Bean,然后再调用BeanPostProcessor接口实现类的postProcessBeforeInitialization()方法。

看到这里就推翻了我之前的理解了,原来一个类实现了BeanPostProcessor接口,那重写的两个方法不是实例化该类的时候调用的,而是容器实例化其他Bean的时候调用的,容器会找出当前容器中所有实现了BeanPostProcessor接口实现类对象,然后一个遍历一个一个调用,这也是为什么上图打印出来的日志会有这么多BeanName的日志记录。也就是说如果容器需要实例化N个Bean,同时容器中已有M个BeanPostProcessor接口实现类对象,那BeanPostProcessor接口的那两个方法就会被调用N*M次,虽然是在不同的实现类中调用的。

applyBeanPostProcessorsAfterInitialization()同理:

一样的,总结一下,对于BeanPostProcessor接口的理解的理解应该是这样:

BeanPostProcessor接口有两个方法,分别为Bean容器中每个对象被实例化前和实例化后调用,即交给Bean容器管理的所有对象,比如打了@Component,@RestController等注解的类,程序启动时就会去实例化并放到Bean容器中的这些类。每次实例化一个Bean都会调用BeanPostProcessor接口重写方法,所有那两个方法是被多次调用的。应该在那两个方法中很据BeanName拿到自己想处理的Bean实例,再去做对应处理。

文章开头也提到,日志打印了这么多,偏偏没有beanName是"myBeanPost"的日志记录,也就是说,BeanPostProcessor接口的实现类明明也打了@Component注解,可是为啥在自己的重写方法中打印不出来beanName是自己的日志记录呢?这是正常而且必然的。因为虽然MyBeanPost也打了@Component,程序启动时也会去实例化这个Bean,但是实例化它的时候,getBeanPostProcessors()方法是拿不到MyBeanPost自己这个Bean的。还没实例化完呢怎么放进Bean容器中,没放进去当然拿不到了,自然也不会去执行这个Bean里重写的方法了。

不过如果另外写一个BeanPostProcessor接口的实现类就不一定了,如果实例化MyBeanPost的时候另一个BeanPostProcessor实现类已经被实例化好了放进Bean容器中了,getBeanPostProcessors()就能拿到,然后在另一个BeanPostProcessor实现类里重写的方法里打印出beanName为"myBeanPost"的日志记录。反正,BeanPostProcessor实现类不能在自己重写的方法中不能拿到自己的Bean实例。

所以BeanPostProcessor接口的正确用法应该是写一个MyBeanPostProcessor实现类,意思是自定义的Bean处理类,然后在这个自定义的Bean处理类中根据beanName去筛选拿到Bean容器中的其他Bean,做一些实例化这些Bean之前之后的逻辑。例如这样:

要注意重写的方法默认是返回null的,这里要返回处理后的bean,如果返回null就会是处理之前的bean,这个逻辑在applyBeanPostProcessorsBeforeInitialization()方法和applyBeanPostProcessorsAfterInitialization()方法里,之前贴的图里有,再贴一遍:

current即自定义处理之后的,如果是null,还是返回result。

 

 

 

 

 

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值