写点什么

spring 源码系列 -beanDefinition(子路)(1),壁虎 java 视频百度网盘

用户头像
极客good
关注
发布于: 刚刚

}}



④当 spring 把类所对应的 beanDefintion 对象存到 map 之后,spring 会调用程序员提供的 bean 工厂后置处理器。什么叫 bean 工厂后置处理器?在 spring 的代码级别是用一个接口来表示 BeanFactoryPostProcessor,只要实现这个接口便是一个 bean 工厂后置处理器了,BeanFactoryPostProcessor 的详细源码解析后面文章再来分析,这里先说一下他的基本作用。BeanFactoryPostProcessor 接口在 spring 内部也有实现,比如第①步当中完成扫描功能的类ConfigurationClassPostProcessor便是一个 spring 自己实现的 bean 工厂后置处理器,这个类笔者认为是阅读 spring 源码当中最重要的类,没有之一;他完成的功能太多了,以后我们一一分析,先看一下这个类的类结构图。



ConfigurationClassPostProcessor实现了很多接口,和本文有关的只需关注两个接口BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor;但是由于BeanDefinitionRegistryPostProcessor是继承了BeanFactoryPostProcessor所以读者也可以理解这是一个接口,但是笔者更加建议你理解成两个接口比较合适,因为 spring 完成上述①②③步的功能就是调用BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry方法完成的,到了第④步的时候 spring 是执行BeanFactoryPostProcessorpostProcessBeanFactory方法;这里可能说的有点绕,大概意思 spring 完成①②③的功能是调用ConfigurationClassPostProcessorBeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry方法,到了第④步 spring 首先会调用ConfigurationClassPostProcessorBeanFactoryPostProcessorpostProcessBeanFactory的方法,然后在调用程序员提供的BeanFactoryPostProcessorpostProcessBeanFactory方法,所以上图当中第④步我画的是红色虚线,因为第④步可能没有(如果程序员没有提供自己的BeanFactoryPostProcessor;当然这里一定得说明,即使程序员没有提供自己扩展的BeanFactoryPostProcessor,spring 也会执行内置的BeanFactoryPostProcessor也就是ConfigurationClassPostProcessor,所以上图画的并不标准,少了一步;即 spring 执行内置的BeanFactoryPostProcessor


重点:我们用自己的话总结一下BeanFactoryPostProcessor的执行时机(不管内置的还是程序员提供)————:如果是直接实现BeanFactoryPostProcessor的类是在 spring 完成扫描类之后(所谓的扫描包括把类变成 beanDefinition 然后 put 到 map 之中),在实例化 bean(第⑤步)之前执行;如果是实现BeanDefinitionRegistryPostProcessor接口的类;诚然这种也叫 bean 工厂后置处理器他的执行时机是在执行直接实现BeanFactoryPostProcessor的类之前,和扫描(上面①②③步)是同期执行;假设你的程序扩展一个功能,需要在这个时期做某个功能则可以实现这个接口;但是笔者至今没有遇到这样的需求,如果以后遇到或者看到再来补上;(说明一下当笔者发表这篇博客有一个读者联系笔者说他看到了一个主流框架就是扩展了BeanDefinitionRegistryPostProcessor类,瞬间笔者如久旱遇甘霖,光棍遇寡妇;遂立马请教;那位读者说 mybatis 的最新代码里面便是扩展这个类来实现的,笔者记得以前 mybatis 是扩展没有用到这个接口,后来笔者看了一下最新的 mybatis 源码确实如那位读者所言,在下一篇笔者分析主流框架如何扩展 spring 的时候更新)



这里再次啰嗦一下,这一段比较枯燥和晦涩,但是非常重要,如果想做到精读 spring 源码这一段尤为重要,建议读者多看多理解;


那么第④步当中提到的执行程序员提供的BeanFactoryPostProcessor到底有什么意义呢?程序员提供BeanFactoryPostProcessor的场景在哪里?有哪些主流框架这么干过呢?


首先回答第一个问题,意义在哪里?可以看一下这个接口的方法签名


void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)throws BeansException;


其实讨论这个方法的意义就是讨论BeanFactoryPostProcessor的作用或者说意义,参考这个方法的一句 javadoc


Modify the application context's internal bean factory after its standard initialization 在应用程序上下文的标准初始化之后修改它的内部 bean 工厂


再结合这个方法的执行时机和这段 javadoc 我们可以理解 bean 工厂的后置处理器(这里只讨论直接实现BeanFactoryPostProcessor的后置处理器,不包括实现BeanDefinitionRegistryPostProcessor的后置处理器)其实是 spring 提供的一个扩展点(spring 提供很多扩展点,学习 spring 源码的一个非常重要的原因就是要学会这些扩展点,以便对 spring 做二次开发或者写出优雅的插件),可以让程序员干预 bean 工厂的初始化过程(重点会考);这句话最重要的几个字是初始化过程,注意不是实例化过程 ;初始化和实例化有很大的区别的,特别是在读 spring 源码的时候一定要注意这两个名词;翻开 spring 源码你会发现整个容器初始化过程就是 spring 各种后置处理器调用过程;而各种后置处理器当中大体分为两种;一种关于实例化的后置处理器一种是关于初始化的后置处理器,这里不是笔者臆想出来的,如果读者熟悉 spring 的后置处理器体系就可以从 spring 的后置处理器命名看出来 spring 对初始化和实例化是有非常大的区分的。


说白了就是——beanFactory 怎么 new 出来的(实例化)BeanFactoryPostProcessor是干预不了的,但是 beanFactory new 出来之后各种属性的填充或者修改(初始化)是可以通过BeanFactoryPostProcessor来干预;可以看到BeanFactoryPostProcessor里唯一的方法postProcessBeanFactory中唯一的参数就是一个标准的 beanFactory 对象——ConfigurableListableBeanFactory;既然 spring 在调用postProcessBeanFactory方法的时候把已经实例化好的 beanFactory 对象传过来了,那么自然而然我们可以对这个 beanFactory 肆意妄为了;


虽然肆意妄为听起来很酷,实则很多人会很迷茫;就相当于现在送给了你一辆奥迪 A6(笔者的 dream car!重点会考)告诉你可以对这辆车肆意妄为,可你如果只是会按按喇叭,那就对不起赠送者的一番美意了;其实你根本不知道可以在午夜开车这辆车去长沙的解放西路转一圈,继而会收货很多意外的“爱情”;笔者举这个例子就是想说当你拿到 beanFactory 对象的时候不能只会 sout,那不叫肆意妄为;我们可以干很多事情,但是你必须要了解 beanFactory 的特性或者 beanFactory 的各种 api,但是 beanFactory 这个对象太复杂了,这里不适合展开讨论,与本文相关的只要知道上述我们讲到的那个 beanDefintionMap(存储 beanDefintion 的集合)就定义在 beanFactory 当中;而且他也提供额 api 供程序员来操作这个 map,比如可以修改这个 map 当中的 beanDefinition 对象,也可以添加一个 beanDefinition 对象到这个 map 当中;看一段代码


@Componentpublic class TestBeanFactoryPostPorcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {//转换为子类,因为父类没有添加 beanDefintion 对象的 apiDefaultListableBeanFactory defaultbf =(DefaultListableBeanFactory) beanFactory;


//new 一个 Y 的 beanDefinition 对象,方便测试动态添加 GenericBeanDefinition y= new GenericBeanDefinition();y.setBeanClass(Y.class);//添加一个 beanDefinition 对象,原本这个 Y 没有被 spring 扫描到 defaultbf.registerBeanDefinition("y", y);


//得到一个已经被扫描出来的 beanDefintion 对象 x//因为 X 本来就被扫描出来了,所以是直接从 map 中获取 BeanDefinition x = defaultbf.getBeanDefinition("x");//修改这个 X 的 beanDefintion 对象的 class 为 Z//原本这个 x 代表的 class 为 X.class;现在为 Z.classx.setBeanClassName("com.enjoy.beanDefinition.Z");}


}


项目里面有三个类 X,Y,Z 其中只有 X 加了 @Component 注解;也就是当代码执行到上面那个方法的时候只扫描到了 X;beanFactory 里的 beanDefinitionMap 当中也只有 X 所对应的 beanDefinition 对象;笔者首先 new 了一个 Y 所对应的 beanDefinition 对象然后调用registerBeanDefinition("y", y);把 y 对应的 beanDefinition 对象 put 到 beanDefinitionMap,这是演示动态添加一个自己实例化的 beanDefinition 对象;继而又调用getBeanDefinition("x")得到一个已经存在的 beanDefinition 对象,然后调用x.setBeanClassName("Z");把 x 所对应的 beanDefinition 对象所对应的 class 改成了 Z,这是演示动态修改一个已经扫


【一线大厂Java面试题解析+核心总结学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


描完成的 beanDefinition 对象;


测试代码如下:


public static void main(String[] args) {AnnotationConfigApplicationContext ac =new AnnotationConfigApplicationContext();ac.register(Appconfig.class);ac.refresh();//正常打印 System.out.println(ac.getBean(Y.class));//正常打印 System.out.println(ac.getBean(Z.class));//异常打印//虽然 X 加了注解,但是被偷梁换柱了,故而异常 System.out.println(ac.getBean(X.class));}


附图:



总结一下上面那副图,spring 实例化一个 bean 其实和你提供的那个类并没有直接关系,而是和一个 beanDefintion 对象所对应的那个类有直接关系(正常情况下一个 beanDefinition 对象会对应一个类,但是也有不正常的情况);打个庸俗的比方好比读者你喜欢一个女生小 A;而小 A 喜欢笔者,但是你不能说你也喜欢笔者;而且笔者还是钢铁直男无论如何也不被可能掰弯的;

用户头像

极客good

关注

还未添加个人签名 2021.03.18 加入

还未添加个人简介

评论

发布
暂无评论
spring源码系列-beanDefinition(子路)(1),壁虎java视频百度网盘