基础概念
IOC 和 DI
IOC,即控制反转。是Spring的一种设计思想。传统程序设计中,我们创建一个对象是通过 new 关键字,是程序主动去创建依赖对象,而在spring中专门有一个容器来创建和管理这些对象,并将对象依赖的其他对象注入到该对象中,这就是我们经常说到的 IOC 容器
所有类的创建、销毁都由spring来控制。对于某一个具体对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫做控制反转
DI,即依赖注入。可以说IOC和DI是同一概念的不同角度描述。
依赖注入是指组件之间的依赖关系由容器在运行期决定,也就是由容器动态将某个依赖关系注入到组件之中。依赖注入的目的不是为了带来更多的功能,而是为了提升组件重用的频率,通过依赖注入机制,我们只需要简单的配置就可以实现
bean
由spring IOC 容器管理的对象称之为 bean。bean是一个由IOC容器进行实例化、组装和管理的对象。
例如我们经常使用到的 @Service 注解,这个注解就相当于告诉 IOC 容器,这个类需要你帮我创建和管理
BeanDefinition
beanDefinition 就是 bean 的定义信息,用来存储 bean 的所有属性方法定义
BeanFactory 和 ApplicationContext
BeanFactory :它是一个用于访问 IOC 容器的根接口,提供了完整的 IOC 服务支持
ApplicationContext:BeanFactory 的子接口,在 BeanFactory 的所有功能上,还额外新增了其他高级的特性,比如:事件发布、国际化支持、统一资源加载策略等。正常情况下,我们使用的都是 ApplicationContext。
以电话来举例:
我们家里使用的 “座机” 就类似于 BeanFactory,可以进行电话通讯,满足了最基本的需求。
而现在非常普及的智能手机,iPhone、小米等,就类似于 ApplicationContext,除了能进行电话通讯,还有其他很多功能:拍照、地图导航、听歌等。
FactoryBean
一般情况下,我们将 bean 的创建和管理都交给 IOC 容器,spring 会利用反射来实例化bean对象,但是如果我们想自己实现 bean 的创建操作,可以实现吗?肯定可以,FactoryBean就实现了这个需求。
FactoryBean 是一个特殊的 bean ,接口中第一个三个方法 isSingleton()、getObjectType()、getObject() ,它是一个工厂bean,可以自己创建 bena实例,如果一个类实现了 FactoryBean接口,则该类可以自己定义创建实例对象的方法,只需要实现它的 getObject() 方法即可
下面将介绍 Spring IOC 的核心流程。
Spring IOC的核心方法就在于 refresh
方法,这个方法里面完成了 Spring的初始化、准备bean、实例化bean和扩展功能的实现。
refresh 方法
在ConfigurableApplicationContext里面定义了这个方法:
根据注释可以知道,这个方法是用来加载刷新配置,这些配置可能来自java配置、xml文件、properties文件、关系型数据库或者其他格式。
作为一个启动方法,它应当在初始化失败后销毁已经创建的单例,防止占着资源而不使用。也就是说调用这个方法的话,要么所有的单例已经被实例化,要么所有的单例都不存在。
BeansException:bean工厂不能被初始化,抛出BeansException
IllegalStateException:bean工厂已经被初始化了,但是不支持多次刷新,抛出IllegalStateException
具体实现
在 AbstractApplicationContext 中进行了实现:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
/**
* 前戏,做容器刷新前的准备工作
* 1、设置容器启动时间
* 2、设置活跃状态为true
* 3、设置关闭状态为false
* 4、获取Environment对象,并加载当前系统属性值到Environment对象中
* 5、准备监听器和事件的集合对象,默认为空集合
*/
prepareRefresh();
/**
* 告诉子类刷新内部bean工厂
*
* 创建容器对象:DefaultListableBeanFactory
* 加载xml配置文件到BeanFactory中,最重要的就是BeanDefinition
* 就是从xml中读取bean的定义信息,然后舍设置到 DefaultListableBeanFactory 中的 beanDefinitionMap 属性中
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/**
* BeanFactory 的准备工作,对 BeanFactory 各种属性进行填充
*/
prepareBeanFactory(beanFactory);
try {
/**
* 子类覆盖方法做额外的处理,此处我们一般不需要做任何扩展工作
*
* 在 web 模块中有具体的实现
* org.springframework.web.context.support.AbstractRefreshableWebApplicationContext
*/
postProcessBeanFactory(beanFactory);
/**
* 调用各种 beanDefinitionRegistryPostProcessor、beanFactoryPostProcessor
*/
invokeBeanFactoryPostProcessors(beanFactory);
// 将所有实现了 BeanPostProcessor 接口的类注册到 BeanFactory 中,
// registerBeanPostProcessors 方法只是注册到 BeanFactory 中,具体调用是在 bean 初始化的时候
// 具体的:在所有 bean 实例化时,执行初始化方法前会调用所有 BeanPostProcessor 的 postProcessBeforeInitialization 方法,
// 在执行初始化方法后会调用所有 BeanPostProcessor 的 postProcessAfterInitialization 方法。
registerBeanPostProcessors(beanFactory);
// 为上下文初始化message源,即不同语言的消息体,国际化处理
initMessageSource();
// 初始化事件监听多路广播器
initApplicationEventMulticaster();
// 留给子类来初始化其他的 bean
onRefresh();
// 在所有注册的 bean 中,查找 listener bean 注册到广播器中
registerListeners();
// 初始化剩下的单实例bean(非懒加载的)
finishBeanFactoryInitialization(beanFactory);
// 发布finishRefresh事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 销毁所有已经创建了的单例bean
destroyBeans();
// 重置 active 标记
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
prepareRefresh():只是做一些容器刷新前的准备工作,无需过多关注,我们从第二个方法 obtainFreshBeanFactory() 往下看,这里只做简要分析,后面文章中会对每个方法做详细介绍:
初始化 BeanFactory、加载 Bean 定义
1、创建一个新的 BeanFactory,默认为 DefaultListableBeanFactory
。
2、读取 Spring 配置文件,并封装成 Resource。
3、根据 Resource 加载 XML 配置文件,并解析成 Document 对象 。
4、从根节点开始,遍历解析 Document 中的节点。
4.1、对于默认命名空间的节点:先将 bean 节点内容解析封装成 BeanDefinition
,然后将 beanName、BeanDefinition 放到 BeanFactory 的缓存中,用于后续创建 bean 实例时使用。
4.2、对于自定义命名空间的节点:会拿到自定义命名空间对应的解析器,对节点进行解析处理。
例如:<context:component-scan base-package=“com.yao” /> ,该节点对应的解析器会扫描 base-package 指定路径下的所有类,将使用了 @Component(@Controller、@Service、@Repository)注解的类封装成 BeanDefinition,然后将 beanName、BeanDefinition 放到 BeanFactory 的缓存中,用于后续创建 Bean 实例时使用。
触发 BeanFactoryPostProcessor
实例化和调用所有 BeanFactoryPostProcessor
,包括其子类 BeanDefinitionRegistryPostProcessor
。
BeanFactoryPostProcessor 接口是 Spring 初始化 BeanFactory 时对外暴露的扩展点
,SpringIOC容器允许 BeanFactoryPostProcessor 在容器实例化任何 bean 之前读取 bean 的定义,并可以修改它。
BeanDefinitionRegistryPostProcessor 继承自 BeanFactoryPostProcessor,比 BeanFactoryPostProcessor 具有更高的优先级
,主要用来在常规的 BeanFactoryPostProcessor 激活之前注册一些 bean 定义。特别是,你可以通过 BeanDefinitionRegistryPostProcessor 来注册一些常规的 BeanFactoryPostProcessor,因为此时所有常规的 BeanFactoryPostProcessor 都还没开始被处理。
注:这边的 “常规 BeanFactoryPostProcessor” 主要用来跟 BeanDefinitionRegistryPostProcessor 区分。
Mybatis 中的 MapperScannerConfigurer 是一个典型的 BeanDefinitionRegistryPostProcessor 的扩展使用,有兴趣的可以看看这个类的源码。
注册 BeanPostProcessor
注册所有的 BeanPostProcessor
,将所有实现了 BeanPostProcessor 接口的类加载到 BeanFactory 中。
BeanPostProcessor 接口是 Spring 初始化 bean 时对外暴露的扩展点
,Spring IoC 容器允许 BeanPostProcessor 在容器初始化 bean 的前后,添加自己的逻辑处理。在这边只是注册到 BeanFactory 中,具体调用是在 bean 初始化的时候。
实例化所有剩余的非懒加载单例 bean
1、遍历所有被加载到缓存中的 beanName,触发所有剩余的非懒加载单例 bean 的实例化。
2、首先通过 beanName 尝试从缓存中获取,如果存在则跳过实例化过程;否则,进行 bean 的实例化。
3、根据 BeanDefinition,使用构造函数创建 bean 实例。
4、根据 BeanDefinition,进行 bean 实例属性填充。
5、执行 bean 实例的初始化。
5.1、触发 Aware 方法。
5.2、触发 BeanPostProcessor 的 postProcessBeforeInitialization 方法。
5.3、如果 bean 实现了 InitializingBean 接口,则触发 afterPropertiesSet() 方法。
5.4、如果 bean 设置了 init-method 属性,则触发 init-method 指定的方法。
5.5、触发 BeanPostProcessor 的 postProcessAfterInitialization 方法。
6、将创建好的 bean 实例放到缓存中,用于之后使用。
完成上下文的刷新
使用应用事件广播器推送上下文刷新完毕事件(ContextRefreshedEvent )到相应的监听器。
至此,整个 IoC 的核心流程介绍完毕。