My logo
Published on

Spring容器的重要组件(invokeBeanFactoryPostProcessor 方法)

怎么理解容器

容器是一个抽象的概念,是由一些列组件组合在一起完成spring bean管理的工作;容器主要有两种分别BeanFactoryApplicationContextBeanFactory是一个简单的容器,能完成bean的实例化,bean的依赖注入;早期开发应用程序资源有限,试用BeanFactory可以节省很大的资源;说个最简单的比方BeanFactory当中的bean都是懒加载的,但 是随着互联网的发展现在的资源已经没有那么紧张了(比如内存),绝大部分情况下都是用

ApplicationContext这个容器他们主要区别在于ApplicationContext的功能比较丰富,支持国际化、支持事件发布、支持

ApplicationContext为例他有一个经典的实现AnnotationConfigApplicationContext

spring-inoke

改容器当中有一个非常重要的组件**DefaultListableBeanFactory 对象;这个对象当中有一个组件 beanDefinitionMap主要来存储所有扫描出来的beandefinition**;这个容器里面的组件非常多。

执行顺序、时机

1、关于beanDefinitionMap当中的内置的值 当spring容器执行到invokeBeanFactoryPostProcessors方法的时候,并没有完成扫描、但是 beanDefinitionMap当中有6个值(你提供了配置类),其中前五个是spring内置的;

实在执行AnnotationConfigApplicationContext的构造方法当中执行new AnnotatedBeanDefinitionReader(this);的时候就put到里面了;第六个是我们配置类我们手动添加的, spring调用了register方法put到里面的

spring-inoke

2、invokeBeanFactoryPostProcessors方法是执行所有的可靠的BeanFactoryPostProcessor BeanFactoryPostProcessor有一个子类BeanDefinitionRegistryPostProcessor

父类当中的方法是postProcessBeanFactory,子类的方法是postProcessBeanDefinitionRegistry 根据实现的接口和来源不同我们讲类分为以下几类

下面是代码示例,参考Spring源码 example 工程:

spring-inoke

注意:ccpp==ConfigurationClassPostProcessor

  • spring执行顺序总体上是先执行子类在执行父类;
  • 先执行api提供的;
  • 然后执行内置的实现了PriorityOrdered接口的;
  • 继而执行扫描出来的或者动态bd添加的实现了Orderd接口的,需要说明的是PriorityOrdered是继承了Orderd的。

故而最后实现什么都实现的所以顺序是:

  1. C#postProcessBeanDefinitionRegistry 因为api提供最先
  2. ccpp#postProcessBeanDefinitionRegistry 内置且实现了PriorityOrdered
  3. I#postProcessBeanDefinitionRegistry 扫描出来的实现了PriorityOrderedOrderd
  4. D#postProcessBeanDefinitionRegistry 被扫描出来的,没有实现,他添加了F
  5. F#postProcessBeanDefinitionRegistry 动态添加的子类

--------------------------------所有子类执行完了-------------------------------

  1. C#postProcessBeanFactory C是api且实现了子类提供级别真的高(注意对比B)
  2. ccpp#postProcessBeanFactory 内置的
  3. I#postProcessBeanFactory 实现了PriorityOrdered
  4. d#postProcessBeanFactory 作为子类动态添加的 比F先是因为字母排序
  5. f#postProcessBeanFactory 作为子类动态添加的
  6. b#postProcessBeanFactory api调用的,但是B没有实现子类,所以级别较低
  7. h#postProcessBeanFactory 直接实现父类,比B低(b是api提供)H是扫描
  8. a#postProcessBeanFactory 为什么A 比H低,A也是直接实现父类-H实现了排序接口

问题思考(重点)

只要把上面的顺序搞懂了这些问题才会变得有意义

1、这样的顺序能不能变

最好不要变;子类高于父类无可厚非,实现了排序的先执行也无可厚非;唯一有争议的是为什么api的先调用;

spring是想尽量充分利用了postProcessBeanDefinitionRegistry方法的意义;避免注入不完整 的bean;因为如果一个bean在扫描之后之后添加可能会有些功能不完善,api放到最前就不会有这个问题。

2、通过api提供的bean存到了list当中,不会重复执行

3、如何确保postProcessBeanDefinitionRegistry方法对子类类型的bd的修改最大化生效

同级别子类修改一个子类的bd会失效(对普通类的修改不会失效),因为spring是找出同级别的bean

直接实例化存到集合当中,然后一起执行,这样在执行第一个的时候如果修改了第二个的bd则无效(这也不是bug,估计作者不想搞的太复杂)

但是对普通类的修改就不会失效了,因为普通类实例化特别靠后,你修改一定会生效;如果你一定需要修改一个子类的bd,最好提高当前子类的级别

4、BeanDefinitionRegistryPostProcessor和ImportBeanDefinitionRegistart的区别

  1. ImportBeanDefinitionRegistart回调的时候可以获取注解信息
  2. ImportBeanDefinitionRegistart的执行时机早于BeanDefinitionRegistryPostProcessor(除api 提供)
  3. BeanDefinitionRegistryPostProcessor(除api提供)对一些bean的注册可能有些功能会失效比 如@Bean
  4. ImportBeanDefinitionRegistart没有上述问题,因为他是在ccpp内部执行的
  5. 如果你一定要动态注册bd,尽量推荐试用ImportBeanDefinitionRegistart
  6. 除非你除了要动态添加bd还需要对beanFactory做一些全局设定,那么可以用 BeanDefinitionRegistryPostProcessor 因为他是一个bean工厂后置处理器

5、我们不推荐用BeanFactoryPostProcessor来注册beandefinition

BeanFactoryPostProcessor的优先级比BeanDefinitionRegistryPostProcessor还要低

6、mybatis的扩展新版和老版的区别

spring-inoke

老版简单粗暴容易理解,新版有画蛇添足之嫌;为什么新版搞这么复杂呢?可以先自己思考一下

关于BeanFactoryPostProcessor

怎么理解bean工厂的后置处理器?

主要是提供给程序员扩展的;在spring容器初始化期间可以让程序员对BeanFactory组件进行全局设置,比如修改、添加beandefinition、比如设置忽略类型、比如设置忽略接口;这节课我们先分析两个
主要的api,后面会做更多api的分析

1、void ignoreDependencyType(Class<?> type); 在自动注入的模型下忽略type类型bean的注入,对所有bean都生效

2、void ignoreDependencyInterface(Class<?> ifc);

一般情况下ifc是个接口,一般情况下ifc里面有一个setter;如果某个bean实现了 ifc接口,那么必然会 重写里面的setter,而重写的这个setter就不会被自动注入;

主要作用也是忽略某个类型的bean的自动注入;但是ignoreDependencyInterface不能对所有bean生效,你得实现ifc接口才能对忽略的设置生效。