Spring源码分析ioc容器总结篇

原创 吴就业 99 0 2018-11-01

本文为博主原创文章,未经博主允许不得转载。

本文链接:https://wujiuye.com/article/95e00a37127a4ded906e24a337728f97

作者:吴就业
链接:https://wujiuye.com/article/95e00a37127a4ded906e24a337728f97
来源:吴就业的网络日记
本文为博主原创文章,未经博主允许不得转载。

这篇文章是对前面几篇文章做一个小节,本篇不写一行代码,单纯的文字总结。

无论是基于xml配置文件方式还是基于@Configuration注解配置类方式,spring都是将我们配置的bean解析为BeanDefinition对象的,一个BeanDefinition就是对一个bean的定义,BeanDefinition存储了一个bean的类名、实例化这个bean将使用的构造参数及参数值、实例化这个bean时需要注入的字段名和字段的值等。

BeanDefinition的是对一个对象的描述,比如说你现在想让技术员帮你组装一台电脑,技术员就会向你询问你想要怎样的一台电脑,对cpu的要求、对显卡的要求是什么,以及对内存的要求等,然后技术员就会根据你的要求去给你组装电脑,你可能是跟朋友一起组装,要求同一样的配置。这时候,你对组装电脑的要求就是对电脑的定义,spring中对bean的定义就是BeanDefinition,如果你只是想要一台,那么技术员就只会给你组装一台,这就是单例,spring中bean工厂就相当于技术员的角色,而如果你是想要一样的配置组装多台电脑这就可以理解为原型。

如果是使用xml配置文件方式,那么spring首先会读取xml配置文件,将xml文件解析为一个个节点,然后获取配置文件中的节点,根据这些bean节点创建BeanDefinition对象。默认会使用无参数构造函数实例化目标bean,但如果这个bean在配置文件中指定了构造参数,那么这些构造参数的配置也会存储在BeanDefinition对象中,在实例化的时候spring就会寻找匹配的构造函数使用配置文件中配置的参数值来实例化这个bean。在xml文件中配置bean的时候构造参数和属性注入都可以是ref引用别的bean,这就是依赖,spring会保证在实例化这个bean之前先实例化依赖的bean。

使用注解配置类方式会有些不同,如果实例化AnnotationConfigApplicationContext应用上下文的时候使用的是指定注解配置类的构造方法,那么会先注册这个注解配置类到bean工厂,也就是将这个配置类解析为一个BeanDefinition对象注册到bean工厂,然后在refresh阶段先初始化bean工厂(做一些配置)之后再调用bean工厂后置处理器。上一篇介绍重点介绍了ConfigurationClassPostProcessor这个spring自己注入的bean,这也是一个bean工厂后置处理器。在bean工厂后置处理器ConfigurationClassPostProcessor中解析配置类的BeanDefinition对象,就是处理配置类上的注解和其方法体内的@Bean方法。

如果配置类(此时配置类已经是一个BeanDefinition对象)上有扫描包的注解就去扫描指定包,扫描指定包下被@Component等注解的类将其解析为BeanDefinition对象,最后将扫描的BeanDefinition结果集注册到bean工厂。如果扫描结果中有@Configuration注解配置类,后续还会继续解析扫描到的注解配置类(被@Configuration注解的BeanDefinition对象)。如果当前配置类有@Import注解,会先处理@Import导入的bean或者注解配置类。@Bean就相对复杂一点,这里就先不提。这是一个广度优先遍历,直到所有的bean都被注册以及所有的注解配置类都被解析处理。

如果bean配置了懒初始化,那么只有在我们调用工厂的getBean方法的时候才会去实例化这个bean,当然如果这个bean是单例的且其它bean会依赖这个bean,那么在其它bean实例化的时候也会将这个bean先实例化。

bean的实例化是根据beanName获取到其BeanDefinition对象,根据BeanDefinition对象中保存的bean的ClassName并使用bean工厂的类加载器来这个ClassName的Class对象,最后才调用这个Class对象的newInstance方法实例化bean。实例化bean完成之后还会使用BeanUtil工具类为其属性赋值,就是我们配置的这个bean需要注入的属性。

在bean实例化完成且注入属性值之后会调用配置的初始化方法(如果有initMethod或者这个bean实现了InitializingBean接口)。在调用初始化方法之前会先调用bean后置处理器BeanPostProcessor的postProcessBeforeInitialization方法,且在调用初始化方法完成之后会调用BeanPostProcessor的postProcessAfterInitialization方法。这些BeanPostProcessor可以不止一个,你可以注入多个,多个BeanPostProcessor都会被调用,且bean工厂中每个bean在调用初始化方法前后都会调用这些BeanPostProcessor。

前面几篇所讲的内容基本上也就那么多了,还有很多内容是没讲到的,因为spring的复杂并不是这么简短的文字就能说完的,不复杂又怎么会这么强大呢?还有spring的aop功能的实现,如果有时间我还会继续分析,但也可能会暂停下来而去学习新的技能:spring cloud。

如果本文有说得不对的地方还望指正。

#后端

声明:公众号、CSDN、掘金的曾用名:“Java艺术”,因此您可能看到一些早期的文章的图片有“Java艺术”的水印。

文章推荐

JVM方法表、栈桢、局部变量表、操作数栈的理解

看懂Java字节码首先得要了解栈桢和方法表,这两个知识点是比较重要的。另外了解这两个知识点还有助于指导Java性能调优工作。

Redis数据持久化策略

我们可以通过修改redis.conf配置文件来选择使用持久化策略,redis提供了三种持久化策略:RDB快照、AOF(Append-only file)、混合策略。

ConcurrentHashMap是如何实现线程安全的

ConcurrentHashMap 在1.7中 实现线程安全是通过锁住Segment对象的。而在1.8 中则是针对首个节点(table[hash(key)]取得的链接或红黑树的首个节点)进行加锁操作。

Spring源码分析(四)bean工厂初始化阶段

继续介绍AnnotationConfigApplicationContext工作流程,本篇介绍refresh方法,即bean工厂的初始化阶段。读完本篇你会了解到bean是什么时候被全部扫描成BeanDefinition注入到工厂中的。还有一些后置处理器的职责。

Spring源码分析(二)bean的创建过程

上一篇有说到过BeanDefinition,主要关注的是其扩展接口AnnotateBeanDefinition和其子类AnnotateGenericBeanDefinition。本篇先超前介绍spring是如果通过BeanDefinition来创建一个bean的。

Spring源码分析(一)理解一些类及接口

本篇主要介绍一些重要的接口及类,下一篇介绍bean的创建过程,接着介绍AnnotationConfigApplicationContext的初始化流程,最后一篇通过源码总结bean的生命周期。