文本已收录至个人GitHub仓库,欢迎Star:github.com/bin39232820…
种一棵树最好的时间是十年前,其次是如今
我知道不少人不玩qq了,可是怀旧一下,欢迎加入六脉神剑Java菜鸟学习群,群聊号码:549684836 鼓励你们在技术的路上写博客java
一样这篇文章也是直接copy的,由于子路老师已经写得很好了。我这边至关于从新学习一遍,加深印象git
若是想系统的学习spring源码那么第一个须要搞明白的知识即是spring当中的BeanDefinition——spring bean的建模对象;程序员
笔者特别强调,beanDefintion的比较枯燥和晦涩难懂,可是很是很是重要,我打算写三篇来把beanDefintion知识讲完;若是你想精读spring源码,请你必定细读三篇beanDefintion的知识,他是spring framework当中的基石;github
那么什么是spring bean的建模对象呢?一言概之就是把一个bean实例化出来的模型对象?有人会问把一个bean实例化出来有Class就好了啊,Class也就是咱们一般说的类对象,就是一个普通对象的建模对象,那么为何spring不能用Class来创建bean呢?很简单,由于Class没法完成bean的抽象,好比bean的做用域,bean的注入模型,bean是不是懒加载等等信息,Class是没法抽象出来的,故而须要一个BeanDefinition类来抽象这些信息,以便于spring可以完美的实例化一个bean(关于什么是bean什么是对象,请参考我写的第一篇循环引用笔者有解释)spring
上述文字能够简单理解spring当中的BeanDefinition就是java当中的Class Class能够用来描述一个类的属性和方法等等其余信息 BeanDefintion能够描述springbean当中的scope、lazy,以及属性和方法等等其余信息api
对上图的文字说明:假设磁盘上有N个.java文件,首先咱们把这些java文件编译成class文件,继而java虚拟机启动会把这些class文件load到内存,当遇到new关键字的时候会根据类的模板信息实例化这个对象也就是在堆上面分配内存bash
下面我会对这幅图作大篇幅的说明,在springbean实例化简图当中我一共标记了⑤步;逐一来讲明吧mybatis
前提:假设在你的项目或者磁盘上有X和Y两个类,X是被加了spring注解的,Y没有加spring的注解;也就是正常状况下当spring容器启动以后经过getBean(X)能正常返回X的bean,可是若是getBean(Y)则会出异常,由于Y不能被spring容器扫描到不能被正常实例化;app
①[^1]当spring容器启动的时候会去调用ConfigurationClassPostProcessor这个bean工厂的后置处理器完成扫描,关于什么是bean工厂的后置处理器下文再来详细解释;spring完成扫描的具体源码放到后续的文章中再来讲。阅读本文读者只需知道扫描具体干了什么事情便可;其实所谓的spring扫描就是把类的信息读取到,可是读取到类的信息存放到哪里呢?好比类的类型(class),好比类的名字,类的构造方法。可能有读者会有疑问这些信息不须要存啊,直接存在class对象里面不就能够?好比当spring扫描到X的时候Class clazzx = X.class;那么这个classx里面就已经具有的前面说的那些信息了,确实如此,可是spring实例化一个bean不只仅只须要这些信息,还有我上文说到的scope,lazy,dependsOn等等信息须要存储,因此spring设计了一个BeanDefintion的类用来存储这些信息。框架
故而当spring读取到类的信息以后②[2]会实例化一个BeanDefinition的对象,继而调用这个对象的各类set方法存储信息;每扫描到一个符合规则的类,spring都会实例化一个BeanDefinition对象,而后把根据类的类名生成一个bean的名字(好比一个类IndexService,spring会根据类名IndexService生成一个bean的名字indexService
,spring内部有一套默认的名字生成规则,可是程序员能够提供本身的名字生成器覆盖spring内置的,这个后面更新),
③
[3]继而spring会把这个beanDefinition对象和生成的beanName放到一个map当中,key=beanName,value=beanDefinition对象;至此上图的第①②③步完成。
这里须要说明的是spring启动的时候会作不少工做,不只仅是完成扫描,在扫描以前spring还干了其余大量事情;好比实例化beanFacctory、好比实例化类扫描器等等,这里不讨论,在之后的文章再来讨论
用一段代码和结果来证实上面的理论
Appconfig.java
@ComponentScan("com.luban.beanDefinition")
@Configuration
public class Appconfig {
}
X.java
@Component
public class X {
public X(){
System.out.println("X Constructor");
}
}
Y.java
public class Y {
}
Test.java
public class Test{
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
ac.register(Appconfig.class);
ac.refresh();
}
}
复制代码
上述代码里面有X和Y两个类(下面有笔者运行这些代码的结果截图),X被注解了,Y没注解,而且在X当中有个构造方法一旦X被实例化便会打印"X Constructor";并且在main方法的最开始打印了"start"按照上面笔者的理论spring首先会扫描X继而把X解析称为一个beanDefinition对象放到map;笔者在spring的源码org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors方法上打了一个断点(invokeBeanFactoryPostProcessors方法里面就完成了上述所说的扫描解析功能),当应用程序启动的时候首先会打印start,继而启动spring容器,而后调用invokeBeanFactoryPostProcessors方法,在没有执行该方法以前查看beanDefintionMap当中并无key为"x"的元素,说明X并无被扫描,而后继续执行,当执行完invokeBeanFactoryPostProcessors方法时候再次查看beanDefintionMap能够看到map当中多了一个key为"x"的元素,其对应的value就是一个beanDefintion的对象,最后查看后台发现尚未打印"X Constructor"这说明这个时候X并无被实例化,这个例子说明spring是先把类扫描出来解析称为一个beanDefintion对象,而后put到beanDefintionMap后面才会去实例化X,至于这个beanDefintionMap后面的文章我会详细讲解,本文读者只需知道他是一个专门来存放beanDefinition的集合便可
④当spring把类所对应的beanDefintion对象存到map以后,spring会调用程序员提供的bean工厂后置处理器。什么叫bean工厂后置处理器?在spring的代码级别是用一个接口来表示BeanFactoryPostProcessor,只要实现这个接口即是一个bean工厂后置处理器了,BeanFactoryPostProcessor的详细源码解析后面文章再来分析,这里先说一下他的基本做用。BeanFactoryPostProcessor接口在spring内部也有实现,好比第①步当中完成扫描功能的类ConfigurationClassPostProcessor即是一个spring本身实现的bean工厂后置处理器,这个类笔者认为是阅读spring源码当中最重要的类,没有之一;他完成的功能太多了,之后咱们一一分析,先看一下这个类的类结构图。
ConfigurationClassPostProcessor实现了不少接口,和本文有关的只需关注两个接口BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor;可是因为BeanDefinitionRegistryPostProcessor是继承了BeanFactoryPostProcessor因此读者也能够理解这是一个接口,可是笔者更加建议你理解成两个接口比较合适,由于spring完成上述①②③步的功能就是调用BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法完成的,到了第④步的时候spring是执行BeanFactoryPostProcessor的postProcessBeanFactory方法;这里可能说的有点绕,大概意思spring完成①②③的功能是调用ConfigurationClassPostProcessor的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,到了第④步spring首先会调用ConfigurationClassPostProcessor的BeanFactoryPostProcessor的postProcessBeanFactory的方法,而后在调用程序员提供的BeanFactoryPostProcessor的postProcessBeanFactory方法,因此上图当中第④步我画的是红色虚线,由于第④步可能没有(若是程序员没有提供本身的BeanFactoryPostProcessor;固然这里必定得说明,即便程序员没有提供本身扩展的BeanFactoryPostProcessor,spring也会执行内置的BeanFactoryPostProcessor也就是ConfigurationClassPostProcessor,因此上图画的并不标准,少了一步;即spring执行内置的BeanFactoryPostProcessor;
重点:咱们用本身的话总结一下BeanFactoryPostProcessor的执行时机(无论内置的仍是程序员提供)————Ⅰ:若是是直接实现BeanFactoryPostProcessor的类是在spring完成扫描类以后(所谓的扫描包括把类变成beanDefinition而后put到map之中),在实例化bean(第⑤步)以前执行;Ⅱ若是是实现BeanDefinitionRegistryPostProcessor接口的类;诚然这种也叫bean工厂后置处理器他的执行时机是在执行直接实现BeanFactoryPostProcessor的类以前,和扫描(上面①②③步)是同期执行;假设你的程序扩展一个功能,须要在这个时期作某个功能则能够实现这个接口;可是笔者至今没有遇到这样的需求,若是之后遇到或者看到再来补上;(说明一下当笔者发表这篇博客有一个读者联系笔者说他看到了一个主流框架就是扩展了BeanDefinitionRegistryPostProcessor类,瞬间笔者如久旱遇甘霖,光棍遇寡妇;遂立马请教;那位读者说mybatis的最新代码里面即是扩展这个类来实现的,笔者记得之前mybatis是扩展没有用到这个接口,后来笔者看了一下最新的mybatis源码确实如那位读者所言,在下一篇笔者分析主流框架如何扩展spring的时候更新)
这个地方读者插一句,真的这个 Spring 的各类 PostProcess真的是强大,我在这称他为钩子函数,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当中;看一段代码
@Component
public class TestBeanFactoryPostPorcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//转换为子类,由于父类没有添加beanDefintion对象的api
DefaultListableBeanFactory 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.class
x.setBeanClassName("com.luban.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,这是演示动态修改一个已经扫描完成的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喜欢笔者,可是你不能说你也喜欢笔者;并且笔者仍是钢铁直男不管如何也不被可能掰弯的;
看完这个结果你还敢轻视spring当中的建模对象beanDefintion的做用了吗?可是BeanDefinition这个接口有太多的实现类,是一个比较复杂的体现,下一篇我慢慢把spring体现当的各类beanDefinition对象逐一介绍清楚;
**我在想若是一个只会Spring应用的程序员,哪天他得罪你了 ,你偷偷写个配置类,让他的Service 永远也注册不到Spring中去,我估计他会疯,,哈哈固然我只是告诉你们,了解一下底层的代码仍是颇有必要,并无让你们这样去作
那么第④步当中提到的执行程序员提供的BeanFactoryPostProcessor到底有什么意义呢?程序员提供BeanFactoryPostProcessor的场景在哪里?有哪些主流框架这么干过呢? 第一个问题咱们大概回答了一下,程序员通常提供BeanFactoryPostProcessor是为了对beanFactory作修改或者叫作干预他的初始化;能修改什么?或者能干预什么?这个问题比较庞大,至少你如今知道能够修改beanFactory当中的beanDefinitionMap 至于剩下两个问题,我之后更新吧
我copy这篇文章的缘由,我是真以为讲得好,对Spring源码的理解确定是到了必定的深度,对于我这种初涉源码的小白都能理解,因此想着分享给你们。而后我但是跟着debug了一遍哈哈。建议全部的读者不要看了就算了,能够看一遍,本身再debug一遍,之后有空再画个流程图,基本上就转换成本身的东西了,一开始都是copy嘛。
好了各位,以上就是这篇文章的所有内容了,能看到这里的人呀,都是真粉。
创做不易,各位的支持和承认,就是我创做的最大动力,咱们下篇文章见
六脉神剑 | 文 【原创】若是本篇博客有任何错误,请批评指教,不胜感激 !