Spring 源码分析——应用上下文
本篇文章讨论 Spring 源码中的应用上下文 ApplicationContext 对象,以及 ApplicationContext 应用上下文如何控制 Bean 容器。
对应 Github 源码完整文档:https://github.com/TyCoding/mini-spring/tree/main/docs/ioc/07-application-context
引言
到此为止,我们已经讲了如何用 XML 配置文件加载 Bean 并装配 Bean 容器,以及 Bean 的实例化和初始化过程,那么再整体看一下 Bean 创建阶段的源码设计:

Bean 容器在哪维护
在之前我们知道getBean()
是获取一个 Bean 的入口,而获取单例 Bean 的入口是SingletonBeanRegistry
接口:

其中DefaultSingletonBeanRegistry
是SingletonBeanRegistry
接口的默认实现,此实现类也是三级缓存管理单例 Bean 的核心,Spring 的源码如下:

通过调用SingletonBeanRegistry
接口的getSingleton()
函数可以从 Bean 单例容器中拿到一个单例实例。
Bean 创建过程
在之前我们重复强调过:
-
获取一个 Bean 的入口 getBean()
函数定义在BeanFactory
接口中; -
而从容器中拿到一个单例 Bean 的入口 getSingleton()
函数定义在SingletonBeanRegistry
接口; -
将这两者逻辑融合在一起的函数定义在 AbstractAutowireCapableBeanFactory
类中,此类才是实例化、初始化 Bean 的核心:

ApplicationContext
上面回顾了 Bean 创建实例化、初始化过程,到此为止我们已经能从容器中拿到一个单例 Bean 了;但是,其实整个过程需要我们手动调用getBean()
函数才能拿到一个 Bean 实例对象。
但是实际上 Spring 在启动容器时就应该把需要的 Bean 加载到了容器中(这里涉及到 Bean 的立即加载和延迟加载问题,后面讨论),因此 Spring 需要一个全局的控制,去扫描所有需要加载到容器中的 Bean,以及控制这些 Bean 什么时候实例化、初始化,而这些逻辑就交由 ApplicationContext 对象完成。
ApplicationContext 的初始化过程可以概括为:
-
加载和解析 XML 配置文件和注册配置类 -
创建和装配 Bean 实例 -
注册 Bean 处理器(如 AOP、Processor) -
发布容器事件(某些 Event 时间监听器) -
完成初始化工作,容器准备就绪
ApplicationContext 接口设计如下:

注: 上面仅仅是在项目https://github.com/TyCoding/mini-spring 中的设计,实际上 ApplicationContext 接口有很多实现类,我们这里仅仅是讲解最基础的从 XML 文件加载 Bean 并通过 ApplicationContext 对象加载到容器对象中。
-
ApplicationContext 接口是应用上下文的父类的接口(ListableBeanFactory、HierarchicalBeanFactory 接口是 Spring 的容器扩展父类接口) -
ConfigurableApplicationContext 接口定义一些额外的配置接口,所有的 ApplicationContext 实现类都需要直接或间接的实现 ConfigurableApplicationContext 接口 -
AbstractApplicationContext 实现了 ConfigurableApplicationContext 和 ApplicationContext 接口 -
AbstractRefreshableApplicationContext 类用于刷新加载 ApplicationContext 上下文中的 Bean 定义(提供了 refresh()
函数) -
AbstractXmlApplicationContext 用于从 XML 配置文件加载 ApplicationContext,提供加载 XML 文件的默认实现,并在 refresh()函数执行时加载 Bean 定义 -
ClassPathXmlApplicationContext 用于从 classspath:
类路径下的 XML 文件中加载 ApplicationContext 对象
核心伪代码:
public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory, ResourceLoader {}
public interface ConfigurableApplicationContext extends ApplicationContext {
void refresh() throws BeansException;
}
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException {
// 创建BeanFactory并初始化BeanDefinition
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 实例化Bean之前先加载BeanFactoryPostProcessor处理器
invokeBeanFactoryPostProcessors(beanFactory);
// BeanPostProcessor要在其他Bean实例化之前注册
registerBeanPostProcessors(beanFactory);
// 提前实例化单例Bean
beanFactory.preInstantiateSingletons();
}
}
public interface ConfigurableListableBeanFactory extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {
void preInstantiateSingletons() throws BeansException;
}
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry {
@Override
public void preInstantiateSingletons() throws BeansException {
beanDefinitionMap.forEach((beanName, beanDefinition) -> {
if (beanDefinition.isSingleton()) {
getBean(beanName);
}
});
}
}
注: 这里我们仅是通过最简洁的方式实现 ApplicationContext 中的refresh()
函数,实际源码中设计很复杂。
我们可以简单的理解为refresh()
函数主要完成了以下几个工作:
-
提前加载 BeanDefinition 数据和 BeanFactory 等数据 -
在实例化 Bean 之前注册 PostProcessor 对象(定制化 Bean 实例) -
提前实例化 Bean(根据 BeanDefinition 数据调用 getBean
实例化 Bean)
通常通过 ClassPathXmlApplicationContext(基于 XML 配置文件)和 AnnotationConfigApplicationContext(注解配置)来加载 ApplicationContext 对象,其中reefresh
函数会在容器初始化时自动调用。
注: 虽然 Spring 容器启动时会自动调用refresh()
函数以提前加载 Bean 实例(这里 bean 至少是保证容器运行的一些必要的实例对象),但并不是所有 bean 都会被加载,Spring 会判断哪些 bean 是需要被提前实例化的,有些 bean 只有在被注入或者被调用时才会进行实例化,称为延迟实例化。
在 SpringBoot 项目中经常使用的@Component
和@Service
注解标记 bean,默认情况下这些 bean 都不会被立即实例化,可以通过配置@Lazy(false)
标记一个 bean 需要在容器初始化时立即被加载。
本章节完整的源码示例请参考:https://github.com/TyCoding/mini-spring/tree/main/docs/ioc/07-application-context
Spring 源码专栏
此专栏将从 Spring 源码角度整体分析 Spring 设计思路以及常见的面试题。
配套作者的手写 Spring 的项目:https://github.com/TyCoding/mini-spring 。该项目中包含各个阶段的开发文档,有关 Spring 源码更详细的分析测试文档请查阅:https://github.com/TyCoding/mini-spring/tree/main/docs
联系我
-
个人博客:http://tycoding.cn/ -
GitHub:https://github.com/tycoding -
微信公众号:程序员涂陌 -
QQ 交流群:866685601
原文始发于微信公众号(程序员涂陌):Spring 源码分析——应用上下文
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/145591.html