前言

前面介绍了 Spring 容器的概念,其核心可归纳为两个类: BeanFactory 和 ApplicationContext,ApplicationContext 继承自 BeanFactory ,其不仅包含 BeanFactory 所有功能,还扩展了容器功能。之后介绍了在 SSM 时期和 SpringBoot 时期如何启动 ApplicationContext 。在结尾处,我们指出,ApplicationContext 核心其实是 refresh 方法,容器一系列功能都在该方法中实现,如:注册 Bean、注入 Bean 等。

在 refresh 方法中,实现容器核心功能前,先进行了一系列环境准备工作,我们以 SpringBoot 为当前运行环境,深入讨论这部分内容。

注:本篇文章使用的 SpringBoot 版本为 2.0.3.RELEASE,其 Spring 版本为 5.0.7.RELEASE

正文

refresh 方法定义在 ConfigurableApplicationContext 接口中,被 AbstractApplicationContext 抽象类实现,该方法由十几个子方法组成,这些子方法各司其职,但部分子方法被 AbstractApplicationContext 的子类进行扩展,来增强功能。其中,前四个子方法主要进行上下文准备工作。

第一步:prepareRefresh

我们先从 refresh 中的 prepareRefresh 方法开始讨论:

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 初始化上下文环境,就是记录下容器的启动时间、活动状态等
prepareRefresh(); ...
}
}

该方法被继承 AbstractApplicationContext 抽象类的子类进行扩展,扩展该方法的子类有:

因本次演示的环境是 SpringBoot ,前面我们讲过,SpringBoot 会根据当前 Web 应用类型创建不同的上下文对象 ,如 Servlet Web、Reactive Web 等。这里演示的是 Servlet Web 应用,所以创建的上下文对象是 AnnotationConfigServletWebServerApplicationContext 。该类的 prepareRefresh 方法会被执行:

public class AnnotationConfigServletWebServerApplicationContext
extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { ... @Override
protected void prepareRefresh() { // 清除 Class 的元数据缓存。底层用 Map 保存元数据,执行 Map 的 clear 方法
this.scanner.clearCache(); // 调用父类,也就是 AbstractApplicationContext 的 prepareRefresh 方法
super.prepareRefresh();
}
...
}
public abstract class AbstractApplicationContext {

    ...

    private long startupDate;

    private final AtomicBoolean active = new AtomicBoolean();

	private final AtomicBoolean closed = new AtomicBoolean();

    private Set<ApplicationEvent> earlyApplicationEvents;

    ...

    protected void prepareRefresh() {

        // 记录此上下文开始时的系统时间(以毫秒为单位)
this.startupDate = System.currentTimeMillis(); // 记录此上下文是否已关闭,这里设置为未关闭
this.closed.set(false); // 记录此上下文是否处于活动状态,这里设置为活动状态
this.active.set(true); if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
} // 这也是交由子类扩展的方法。具体子类为 GenericWebApplicationContext,主要是初始化属性源,
// 将 ServletContext 和 ServletConfig 属性配置添加到 Environment 环境上下文中
initPropertySources(); // 校验 Environment 中那些必备的属性配置是否存在,不存在则抛异常。
getEnvironment().validateRequiredProperties(); // 创建 ApplicationEvent 事件集合
this.earlyApplicationEvents = new LinkedHashSet<>();
} }

refresh 中的 prepareRefresh 方法执行结束,主要是记录容器的启动时间、活动状态、检查必备属性是否存在。其中,关于 Environment 环境配置,在前面 SpringBoot 系列文章《SpringBoot(五)外部化配置之Environment》中有详细讨论,感兴趣的同学可自行翻阅。

第二步:obtainFreshBeanFactory

接着进入 refresh 中的 obtainFreshBeanFactory 方法

public abstract class AbstractApplicationContext {

    ...

    public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) { ... ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); ...
}
}
... protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 该方法也是由子类扩展,其子类有 AbstractRefreshableApplicationContext 和 GenericApplicationContext,
// 因当前是 Servlet Web 应用,所以执行的是 GenericApplicationContext 中的 refreshBeanFactory 方法。
// 该方法主要设置 BeanFactory 的 serializationId 属性值,也就是序列化id
refreshBeanFactory(); // 通过 getBeanFactory 返回 BeanFactory 对象。同样也是由子类扩展,调用的是 GenericApplicationContext 类中的 getBeanFactory 方法。
// 返回的是 DefaultListableBeanFactory 。
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
} ...
}

obtainFreshBeanFactory 方法很简单,但如果当前是非 Servlet Web 应用,执行的就是 AbstractRefreshableApplicationContext 中的 refreshBeanFactory 方法,那可就复杂多了,这里就不展开讨论。之后,该方法还返回了 BeanFactory 对象,从这也可以看出 ApplicationContext 底层是以 BeanFactory 为基础,逐步扩展 Spring 容器功能。

第三步:prepareBeanFactory

接着进入 refresh 中的 prepareBeanFactory 方法。prepareBeanFactory 方法主要是对 BeanFactory 做一些配置,包含各种类加载器、需要忽略的依赖以及后置处理器、解析器等,

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) { ... prepareBeanFactory(beanFactory);
...
}
...
} protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 设置类加载器
beanFactory.setBeanClassLoader(getClassLoader());
// 设置表达式解析器,主要用来解析 EL 表达式; Bean 初始化完成后填充属性时会用到
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
// 设置属性注册解析器,主要用来解析 Bean 中的各种属性类型,如 String、int 等
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// 添加一个后置处理器:ApplicationContextAwareProcessor。
// 该后置处理器用于向实现了 Aware 系列接口的 bean 设置相应属性。
// (后置处理器和 Aware 接口也是比较核心的概念,后面会有文章详细讨论)
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this)); // 以下接口,在自动注入时会被忽略,其都是 Aware 系列接口
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class); // 当以下特殊的 Bean 需自动注入时,指定其注入的类型 。
// 如:注入 BeanFactory 时,注入的类型对象为 ConfigurableListableBeanFactory 。
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this); // 添加 ApplicationListenerDetector 后置处理器。
// 该后置处理器用来检测那些实现了 ApplicationListener 接口的 bean,并将其添加到应用上下文的事件广播器上。
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this)); // 判断容器中是否存在 loadTimeWeaver Bean,如果存在则上下文使用临时的 ClassLoader 进行类型匹配。
// 集成 AspectJ 时会用到 loadTimeWeaver 对象。
if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
} // 注册和环境相关的 Bean,如 environment、systemProperties、systemEnvironment
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
}

在 prepareBeanFactory 方法中,主要对 BeanFactory 添加了一系列属性项,如添加忽略自动注入的接口、添加 BeanPostProcessor 后置处理器、手动注册部分特殊的 Bean及环境相关的 Bean 。

第四步:postProcessBeanFactory

postProcessBeanFactory 方法是上下文准备的最后一步,主要用来注册 Web 请求相关的处理器、Bean及配置。

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) { ... postProcessBeanFactory(beanFactory);
...
}
}

该方法也是由子类进行扩展,实现该方法的子类有:



前面也说过,当前是 Servlet Web 应用,所以创建的 ApplicationContext 上下文是 AnnotationConfigServletWebServerApplicationContext,执行该类的 postProcessBeanFactory 方法。

public class AnnotationConfigServletWebServerApplicationContext
extends ServletWebServerApplicationContext implements AnnotationConfigRegistry { private final AnnotatedBeanDefinitionReader reader; private final ClassPathBeanDefinitionScanner scanner; private final Set<Class<?>> annotatedClasses = new LinkedHashSet<>(); private String[] basePackages; ... @Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 先执行父类 ServletWebServerApplicationContext 的 postProcessBeanFactory 方法。
// 跳转到 1 查看父类实现
super.postProcessBeanFactory(beanFactory); // basePackages 存储的是类路径。先判断是否为 null,不为 null 则通过 ClassPathBeanDefinitionScanner 的 scan 方法
// 扫描该路径下符合条件的 Class,并将 Class 信息包装成 BeanDefinition 注册到容器中,
// 当然,这里没有指定扫描路径,所以不会进入这个 if。
// (BeanDefinition 概念会在后面章节详细讨论)
if (this.basePackages != null && this.basePackages.length > 0) {
this.scanner.scan(this.basePackages);
} // annotatedClasses 存储的 Class 集合。先判断该集合是否为空,不为空则通过
// AnnotatedBeanDefinitionReader 的 register 方法将 Class 信息包装成 BeanDefinition 注册到容器中,
// 这里同样没有设置 Class 集合内容,所以不会进入这个 if。
if (!this.annotatedClasses.isEmpty()) {
this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
}
}
} 1、
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
... @Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 添加 BeanPostProcessor 后置处理器:WebApplicationContextServletContextAwareProcessor,
// 该后置处理器主要是从 ConfigurableWebApplicationContext 上下文中获取 ServletContext 和 ServletConfig 对象
beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this)); // 添加一个 忽略自动注入的接口
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
}
...
}

postProcessBeanFactory 方法执行的操作和前面类似,也是添加了后置处理器和忽略自动注入的接口。

总结

ApplicationContext 上下文准备工作基本结束,主要还是在 BeanFactory 中添加一系列后置处理器、注册特殊的 Bean 及设置忽略自动注入的接口。其中还提到了 Spring 容器的三个核心部分:Aware 系列接口、BeanPostProcessor 后置处理器、BeanDefinition ,这部分在后面的文章会逐步讨论。接下来将对 Spring 容器的核心功能展开讨论。

以上就是本章内容,如果文章中有错误或者需要补充的请及时提出,本人感激不尽。

Spring(三)核心容器 - ApplicationContext 上下文启动准备的更多相关文章

  1. spring的核心容器ApplicationContext

    //bean.xml配置文件 <?xml version="1.0" encoding="UTF-8"?><beans xmlns=" ...

  2. 【Spring】 Spring的核心容器

    Spring的核心容器 文章目录 Spring的核心容器 BeanFactory ApplicationContext 1.通过ClassPathXmlApplicationContext创建 2.通 ...

  3. Spring之核心容器bean

    摘要:Spring的核心容器实现了Ioc,其目 的是提供一种无侵入式的框架.在本文中,首先讲解了Spring的基础bean的相关知识,然后介绍了Spring是如何对bean进行管理的. 在Spring ...

  4. Spring的核心容器

    Spring框架的主要功能是通过其核心容器来实现的.Spring提供了2种核心容器:BeanFactory.ApplicationContext. BeanFactory BeanFactory是一个 ...

  5. Spring 在web 容器中的启动过程

    1.对于一个web 应用,其部署在web 容器中,web 容器提供其一个全局的上下文环境,这个上下文就是 ServletContext ,其后面的spring IoC 容器提供宿主环境 2.在web. ...

  6. Spring学习总结(6)——Spring之核心容器bean

    一.Bean的基础知识 1.在xml配置文件中,bean的标识(id 和 name) id:指定在benafactory中管理该bean的唯一的标识.name可用来唯一标识bean 或给bean起别名 ...

  7. Spring(五)核心容器 - 注册 Bean、BeanDefinitionRegistry 简介

    目录 前言 正文 1.BeanDefinitionRegistry 简介 2.registerBeanDefinition 方法注册 Bean 最后 前言 上篇文章我们对 BeanDefinition ...

  8. Spring(四)核心容器 - BeanDefinition 解析

    前言 在上篇文章中,我们讨论了 refresh 的前四个方法,主要是对 ApplicationContext 上下文启动做一些准备工作.原计划是对接下来的 invokeBeanFactoryPostP ...

  9. 初识Spring——Spring核心容器

    一. IOC和DI基础 IOC-Inversion of Control,译为控制反转,是一种遵循依赖倒置原则的代码设计思想. 所谓依赖倒置,就是把原本的高层建筑依赖底层建筑“倒置”过来,变成底层建筑 ...

随机推荐

  1. H3C STP基本配置

  2. C#面试题整理2(不带答案)

    一.C# 理论 1.1.简述 private. protected. public. internal.protected internal 访问修饰符和访问权限 1.2.简述abstract.sea ...

  3. Java 学习笔记(11)——lambda 表达式

    在写Java代码的时候,如果某个地方需要一个接口的实现类,一般的做法是新定义一个实现类,并重写接口中的方法,在需要使用的时候new一个实现类对象使用,为了一个简单的接口或者说为了一个回调函数就得额外编 ...

  4. Asp.net全局资源文件( App_GlobalResources)和本地资源文件(App_LocalResources)

    今天项目中需要加入对不同语言的支持. 找了半天的资料,关于GlobalResources和LocalResource都说的模棱两可的. 只能自己边找资料边试.     全局资源文件 样例(Demo中有 ...

  5. mac如何查看已连接wifi的密码

    可以通道mac自带的“钥匙串访问”功能查看.选择需要查询的wifi名称,右击选择“将密码拷贝到剪贴板”,输入管理员密码后,密码就拷贝好了. 找个地方粘贴即可看到密码

  6. 泛圈科技Yottachain区块链云存储打破传统云迎来价值数据存储

    随着物联网时代的发展,更多的数据随之产生.从智能设备到电脑再到视频游戏机,各种各样的信息从不同的电子产品源源不断地涌入.通常,人们将数据存储在本地驱动器中.但是,由于产生的数据量是无限的,超过了本地存 ...

  7. MySQL基础之事务编程学习笔记

    MySQL基础之事务编程学习笔记 在学习<MySQL技术内幕:SQL编程>一书,并做了笔记.本博客内容是自己学了<MySQL技术内幕:SQL编程>事务编程一章之后,根据自己的理 ...

  8. linux大盘格式化分区

    Linux 实例的磁盘管理 对于 Linux 系统上的大磁盘,也要采用 GPT 分区格式, 也可以不分区, 把磁盘当成一个整体设备使用. 在 Linux 上一般采用 XFS 或者 EXT4 来做大盘的 ...

  9. $BZOJ1799\ Luogu4127$ 月之谜 数位统计$DP$

    AcWing Description Sol 看了很久也没有完全理解直接$DP$的做法,然后发现了记搜的做法,觉得好棒! 这里是超棒的数位$DP$的记搜做法总结   看完仿佛就觉得自己入门了,但是就像 ...

  10. 星星泡饭-R1SE

    作词 : 吴孤儿 时光不用斟酌 再流淌 摩天轮慢慢地旋转 约定 留下搅拌的星光 赵磊: 媲美哪颗星星的孤寂 是我们 脏不了的心 勇敢 游戏 品尝着很饿的梦境 我的梦想只是梦想 哪怕回音只是气球碰撞 会 ...