1.引言
spring框架有四个基础核心:
- IoC容器
- JavaConfig
- 事件监听
- Spring factories
从本篇文章开始,我会依次给你分享关于四个基础核心的相关知识。开始吧!
2.IoC容器
2.1.什么是IOC容器
可以把spring IoC容器,看做一家餐馆,你到餐馆点菜,你只关心最终吃到这道菜,至于菜需要用什么原料?怎么做?你其实是不关心的。
同理,回到技术术语,当你在使用IoC容器的时候,你只关心需要什么bean对象,至于这个bean对象是怎么实例化的?依赖了什么组件?你其实是不关心的。
好了,那么到底什么是IoC呢?它的英文名称是Inversion of Control,即控制反转。控制反转有点抽象,我给你举个例子,你大概就懂了。
- 在没有spring框架以前,程序代码中,当需要某个对象的时候,通常需要new 的方式来获取对象,并且通过构造方法,或者set方法处理对象之间的依赖关系。关键词:这里是写程序的人,在控制管理对象
- 在有了spring框架后,程序代码中,当需要某个对象的时候,通常从ApplicationContext中获取对象,至于这个对象怎么初始化的?怎么完成组件依赖的?统统不用关心,spring框架提供了一系列工厂来管理这些bean对象。
也就是说,需要的bean对象原来是由你来new,你来管理,转变成交给spring框架来管理,对象的控制权由你,转移给了spring框架,这就是控制反转!
那么这里的IoC容器,其实就是一个Map,后面我们会继续解密。暂且按下不表。
当然了,一家餐馆要做出美味佳肴,原料和菜谱是必不可少的。IoC容器一样,要管理bean对象和它们之间的依赖关系,需要有相应的原材料和菜谱。
xml
<bean id="a" class="com.xxx.A">
<property name="b" ref="b">
</bean>
早期spring框架的玩家,对上面这个配置一定不会陌生!通过bean标签配置了一个bean,通过property属性标签,说明a对象,依赖一个b对象。这些是开发通过配置告诉spring框架,来,这就是原材料。
自然spring框架需要有与配置相应的原材料表示,这就是BeanDefinition。该实例负责bean对象的全部必要信息,包括:Class、访问控制、构造方法、参数、属性等。
原材料有了,菜谱呢?对应的菜谱是BeanDefinitionRegistry,和BeanFactory。BeanDefinitionRegistry负责抽象bean注册逻辑。Beanfactory负责抽象bean管理逻辑。配合起来天衣无缝!
- BeanFactory核心方法
- BeanDefinitionRegistry核心方法
且它们有一个共同的杰出后代:DefaultListableBeanFactory。作为默认的容器实现,同时实现了BeanDefinitionRegistry和BeanFactory,因此它同时负责bean的注册,管理工作。上一个图,直观一些。
了解了IoC相关的概念,再来看IoC的大致工作流程,主要有两个阶段。
2.2.IOC容器工作流程
- 启动阶段
我们知道,要让容器管理bean对象,需要告诉容器相关的原材料信息,在实际开发中通常是你我提供的配置信息,比如xml配置或者注解配置,这些信息统称为Configuration MetaData,即配置元数据。
在spring 框架中,负责这个事情的是BeanDefinitionReader,该类负责加载、解析、分析Configuration MetaData信息,将这些信息封装成BeanDefinition,并注册到BeanDefinitionRegistry。这样一来启动阶段就算完成了。
- bean实例化阶段
经过启动阶段,原材料准备好。当有某个使用方通过BeanFactory的getBean方法,获取某个bean对象时,容器会检查目标bean对象之前是否已经实例化。
如果有,则直接返回给使用方;
如果没有,则进入bean的实例化,容器会根据注册的BeanDefinition进行实例化,并为其注入依赖。实例化完成,返回给使用方。
需要注意的是,我们一直在说BeanFactory,事实上在实际应用中,用的更多的是另外一个容器实现:ApplicationContext。ApplicationContext是扩展至BeanFactory。它们两的区别在于:BeanFactory是延迟加载,而ApplicationContext是饥饿加载。
2.3.容器扩展机制
在整个spring容器管理bean生命周期过程中,不同阶段提供了对应的扩展点,这些扩展点使得我们在使用spring框架时,具备足够的灵活性。
在启动阶段,BeanFactoryPostProcessor可以在容器实例化对象之前,对注册到容器中的BeanDefinition对象进行修改,然后改变bean对象的行为。
要想自定义扩展类,需要实现接口BeanFactoryPostProcessor。实际应用中,你可能会需要自定义扩展多个BeanFactoryPostProcessor,为了保障多个BeanFactoryPostProcessor执行顺序,通常还需要扩展实现Ordered接口。
spring为我们提供了一个示例扩展:PropertyPlaceholderConfigurer。你一定还有这样的印象,在项目开发中,很多配置信息为了方便统一管理,在引用的地方,是这样的
xml
${jdbc.driver}
${jdbc.url}
${jdbc.username}
${jdbc.passwd}
这些配置信息,在BeanDefinition对象中,是以占位符的形式存在。但当BeanFactory需要真正实例化bean对象时,需要将占位符替换成具体的配置信息。这个活谁来负责?没错,就是PropertyPlaceholderConfigurer。
相比较在启动阶段的扩展,大多数同学可能更熟悉BeanPostProcessor。BeanFactoryPostProcessor负责扩展bean定义;而BeanPostProcessor负责扩展实例化后的bean。它有两个方法:
- postProcessBeforeInitialization:前置处理(执行初始化),即通常意义的init-method前
- postProcessAfterInitialization:后置处理(销毁),即通常意义的destroy-method后
在实际应用中,你可以自定义一个注解,在某个bean上加上该注解,然后在BeanPostProcessor中检查该对象上,是否有注解?如果有,你便可以为所欲为的进行扩展!是不是很happy!
看一个spring框架提供的例子,经常能够看到各种Aware接口,它们的作用是在对象实例化完成以后,将Aware接口中定义的依赖注入到当前实例中,Aware的意思即注入,前面是什么,就注入什么。
比如ApplicationContextAware接口,如果你需要在你的bean对象中,获取到ApplicationContext对象,你只需要让你的bean实现ApplicationContextAware接口,则万事大吉!