likes
comments
collection
share

【重写SpringFramework】ApplicationContext基本实现(chapter 3-2)

作者站长头像
站长
· 阅读数 62

1. 前言

上一节我们对 ApplicationContext 的继承体系进行了梳理,从应用类型和配置方式两个维度来划分众多的实现类。我们关心的是以声明注解的方式来配置的 Spring 容器,具体到 context 模块,ApplicationContext 的实现主要为普通应用服务。本节我们将专注于 context 模块有关的接口和类,分析各自的功能,并实现一个最简单的 Spring 容器。

2. 整体分析

2.1 继承结构

从类图上来看,ApplicationContextBeanFactory 通过装饰模式组合在一起,上一节已经论述过了。这里简化了 BeanFactory 的继承结构,我们重点关心 ApplicationContext 的相关接口的类,如下所示:

  • ApplicationContext:顶级接口,继承了 BeanFactory 接口

  • ConfigurableApplicationContext:对外暴露的配置接口,可以进行一些设置工作

  • AbstractApplicationContext:顶级抽象类,实现了 ApplicationContext 接口的大多数功能

  • GenericApplicationContext:简单的实现类,拥有容器的基本功能,主要用于编写测试代码

  • AnnotationConfigApplicationContext:通过注解方式进行配置的,是本模块中最重要的实现类

  • AbstractRefreshableApplicationContext:可刷新的容器,允许多次调用 refresh 方法,本模块不涉及实现类

【重写SpringFramework】ApplicationContext基本实现(chapter 3-2)

2.2 核心 API 简介

ApplicationContext 作为顶级接口,最重要的作用是继承了 BeanFactory 接口,在装饰模式中充当抽象装饰角色。

  • getAutowireCapableBeanFactory 方法:获取一个内部的 BeanFactory 对象
  • getEnvironment 方法:获取环境变量对象,Environment 保存了系统和配置文件等介质的相关信息
public interface ApplicationContext extends BeanFactory {
    String getId();
    String getDisplayName();
    AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
    ConfigurableEnvironment getEnvironment();
}

ConfigurableApplicationContext 是对外暴露的配置接口,与 ConfigurableBeanFactory 接口类似,可以进行一些设置工作。

  • addBeanFactoryPostProcessor 方法:添加 BeanFactoryPostProcessor 组件,我们在第一章 beans 模块简单介绍过该组件,具体用法将在配置类说明
  • refresh 方法:定义了容器初始化的流程
  • getBeanFactory 方法:获取内部的 BeanFactory 实例,可以进行配置操作
public interface ConfigurableApplicationContext extends ApplicationContext {
    void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
    void refresh();
    void setEnvironment(ConfigurableEnvironment environment);
    ConfigurableBeanFactory getBeanFactory() throws IllegalStateException;
    void setParent(ApplicationContext parent);
}

AbstractApplicationContext 作为顶级的抽象类,提供了 ApplicationContext 接口的大部分功能,最重要的就是 refresh 方法。所有的实现类都会在构造方法中调用 refresh 方法,刷新上下文,使 Spring 容器变得可用。

public abstract class AbstractApplicationContext implements ConfigurableApplicationContext {

    @Override
    public void refresh() {
        //实现略
    }
}

GenericApplicationContext 是一个基本实现类,最大的特点是持有一个 DefaultListableBeanFactory 实例,相当于装饰模式中的具体装饰角色。该类有两个作用,一是作为测试代码使用的 Spring 容器,二是作为整个继承体系中的一环,起到过渡作用。本章我们更关心子类 AnnotationConfigApplicationContext 的表现,这里先不展开讲。

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
    private final DefaultListableBeanFactory beanFactory;

    public GenericApplicationContext() {
        this.beanFactory = new DefaultListableBeanFactory();
    }
}

AbstractRefreshableApplicationContext 是另一个基本实现类,从类图中可以看到它与 GenericApplicationContext 是平行关系。该类的特点是可刷新(Refreshable),刷新的动作是由配置文件触发的。也就是说,该类型的应用会对配置文件进行监控,一旦配置文件发生变更,就会刷新整个容器。我们不关注基于 XML 的实现类,这里仅作为继承结构中的一环,为后续的 Web 应用做铺垫。

public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
    private DefaultListableBeanFactory beanFactory;

    @Override
    protected void refreshBeanFactory() throws BeansException {
        this.beanFactory = new DefaultListableBeanFactory();
        loadBeanDefinitions(beanFactory);
    }

    //模版方法,由子类实现具体的加载逻辑
    protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException;
}

3. AbstractApplicationContext

3.1 基本属性

AbstractApplicationContext 是整个 ApplicationContext 体系中的核心类,refresh 方法更是重中之重。我们先介绍一些基本的属性,后续还会陆续增加新的属性,以及接口和父类。基本属性如下所示:

  • id:当前容器的唯一标识符
  • displayName:当前容器的名称,方便用户识别
  • environment:环境变量相关
  • parent:可能存在的父容器
  • startupShutdownMonitor:容器启动或关闭的监视器锁
  • active:容器的启动标识符
  • closed:容器的关闭标识符
  • beanFactoryPostProcessors:容器持有的 BeanFactoryPostProcessor 集合
public abstract class AbstractApplicationContext implements ConfigurableApplicationContext {
    private String id = ObjectUtils.identityToString(this);
    private String displayName = ObjectUtils.identityToString(this);
    private ConfigurableEnvironment environment = new StandardEnvironment();
    private ApplicationContext parent;
    private final Object startupShutdownMonitor = new Object();
    private final AtomicBoolean active = new AtomicBoolean();
    private final AtomicBoolean closed = new AtomicBoolean()
    private final List<BeanFactoryPostProcessor> beanFactoryPostProcessors = new ArrayList<>();
}

3.2 refresh 方法

refresh 方法定义了 Spring 上下文刷新的主体流程,子类可以重写一些模板方法完成一些定制功能,这是模板方法模式的典型应用。由于刷新上下文的工作包含众多流程,Spring 将每个步骤提取为一个方法,主要有 11 步。为了便于理解,我们将这些步骤分为三个部分:

  • 准备工作(1~3):提供了一个可用的 BeanFactory 实例。(从第 4 步开始,代码被包裹在 try catch 块中,一旦报错就会销毁单例,并结束创建过程)
  • 上下文配置(4~8):主要包括两个方面,一是注册容器内置的组件,二是通过多种方式加载 BeanDefinition,其中一部分会被实例化。
  • 收尾工作(9~11):在此之前,所有的 BeanDefinition 都已经被加载了,其中只有一部分实例化了。因此首要工作是要确保所有的单例都被实例化,还有就是事件相关的处理以及其他工作。
@Override
public void refresh() {
    //1. 准备工作
    //step-1 刷新前的准备
    prepareRefresh();

    //step-2 通知子类刷新BeanFactory
    ConfigurableBeanFactory beanFactory = obtainFreshBeanFactory();

    //step-3 ApplicationContext使用BeanFactory前的准备工作,比如添加必要的组件
    prepareBeanFactory(beanFactory);

    try {
        //2. 上下文配置
        //step-4 扩展点,允许子类进行自定义操作(主要是与BeanFactory有关的)
        postProcessBeanFactory(beanFactory);

        //step-5 执行BeanFactoryPostProcessor的相关逻辑
        invokeBeanFactoryPostProcessors(beanFactory);

        //step-6 注册BeanPostProcessor
        registerBeanPostProcessors(beanFactory);

        //step-7 注册事件多播器,默认为SimpleApplicationEventMulticaster
        initApplicationEventMulticaster(beanFactory);

        //step-8 扩展点,允许子类进行自定义操作(与BeanFactory无关)
        onRefresh();

        //3. 收尾工作
        //step-9 将所有的监听器添加到事件多播器中
        registerListeners();

        //step-10 创建容器中剩余的单例Bean
        finishBeanFactoryInitialization(beanFactory);

        //step-11 发送refresh完成事件
        finishRefresh();
    } catch (Exception e) {
        //销毁已存在的单例(略)
    }
}

4. refresh 方法详解

【重写SpringFramework】ApplicationContext基本实现(chapter 3-2)

4.1 准备工作

第 1 步,刷新前的准备工作,主要是给一些标记字段赋值。

//step-1 刷新前的准备,将Context设置为活跃的
private void prepareRefresh() {
    this.closed.set(false);
    this.active.set(true);
}

第 2 步,获取 BeanFactory 实例。对于 GenericApplicationContext 来说,默认使用 DefaultListableBeanFactory 作为 IOC 容器,并且是一个空的容器。

//step-2 通知子类刷新BeanFactory
private ConfigurableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    return getBeanFactory();
}

refreshBeanFactory 方法是一个模板方法,子类可以重写。比如 ClassPathXmlApplicationContext 通过 XmlBeanDefinitionReader 加载 spring.xml 文件,其父类重写了该方法,实现了加载 Xml 配置文件的功能。示例代码如下:

public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        //读取xml配置文件并加载Bean
        reader.loadBeanDefinitions(getConfigResources());
    }
}

第 3 步,在获得 BeanFactory 实例之后,还需要设置一些组件,使之变得可用。在 Spring 应用中,我们可以直接注入 BeanFactoryApplicationContextEnvironment 等组件,原因在于这些组件提前被设置为了默认的依赖项。prepareBeanFactory 方法实现了四种操作,如下所示:

  • 设置 BeanFactory 的属性,包括 ClassLoaderBeanExpressionResolverPropertyEditorRegistrar 等,我们不是很关心这些组件,仅了解
  • 注册 BeanPostProcessor,其中 ApplicationContextAwareProcessor 负责处理 Aware 感知接口,该类的逻辑较为简单,自行参看代码。
  • 注册用于依赖注入的组件,这些类型都与 BeanFactoryApplicationContext 有关,它们都是 Spring 容器的不同表现形式。
  • 注册单例,将环境变量对象添加到 Spring 容器中。
//step-3 BeanFactory要变得可用,还需要设置一些组件
private void prepareBeanFactory(ConfigurableBeanFactory beanFactory) {
    //1) 设置BeanFactory的相关组件(OMIT)
    //2) 注册BeanPostProcessor
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));	//处理Aware感知接口
    //注册ApplicationListenerDetector(TODO)

    //3)注册依赖项组件
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    //注册ApplicationEventPublisher (TODO)

    //4) 注册单例
    beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}

4.2 上下文配置

第 4 步,postProcessBeanFactory 方法是空实现,允许子类对 BeanFactory 进行自定义的操作。比如子类想添加 BeanPostProcessor,这个节点是非常好的选择。示例代码如下,支持 web 应用的 ApplicationContext 子类中,添加了用于处理 ServletContextBeanPostProcessor 组件。

public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableApplicationContext {
    @Override
    protected void postProcessBeanFactory(ConfigurableBeanFactory beanFactory) {
        beanFactory.addBeanPostProcessor(new ServletContextAwareProcessor(this.servletContext, this.servletConfig));
    }
}

第 5 步,查找容器中的 BeanFactoryPostProcessor,依次执行相关操作。最典型的应用是处理声明了 @Configuration 的配置类,大部分组件都是在这一过程中被加载的。

//step-5 调用已注册的BeanFactoryPostProcessor
protected void invokeBeanFactoryPostProcessors(ConfigurableBeanFactory beanFactory) {
    List<BeanDefinitionRegistryPostProcessor> processors = new ArrayList<>();
    List<String> names = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class);
    for (String name : names) {
        processors.add(beanFactory.getBean(name, BeanDefinitionRegistryPostProcessor.class));
    }

    if (beanFactory instanceof BeanDefinitionRegistry) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        for (BeanDefinitionRegistryPostProcessor processor : processors) {
            //执行BeanDefinition注册相关操作
            processor.postProcessBeanDefinitionRegistry(registry);
            processor.postProcessBeanFactory(beanFactory);
        }
    }
}

第 6 步,将已加载的 BeanPostProcessor 注册到 BeanFactory 中。具体过程由 PostProcessorRegistrationDelegate 的静态方法实现,逻辑较为简单,请自行查看代码。

//step-6 注册BeanPostProcessor
protected void registerBeanPostProcessors(ConfigurableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory);
}

第 7 步,注册事件多播器组件,先略过,稍后在事件机制中介绍。

第 8 步,onRefresh 方法是空实现,注意与第 4 步的区别。此时 BeanFactory 和容器的相关配置已经完成,子类可以进行一些自定义的操作。比如 EmbeddedWebApplicationContext 完成了内嵌 servlet 容器的创建,示例代码如下:

public class EmbeddedWebApplicationContext extends GenericApplicationContext implements WebApplicationContext {
    private volatile EmbeddedServletContainer embeddedServletContainer;

    @Override
    protected void onRefresh() {
        super.onRefresh();
        //创建嵌入式Servlet容器
        createEmbeddedServletContainer();
    }

    private void createEmbeddedServletContainer() {
        EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
        this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer());
    }
}

4.3 收尾工作

第 9 步,将事件监听器添加到事件多播器中,先略过,稍后在事件机制中介绍。

第 10 步,BeanFactory 的初始化已完成,这里添加了一个 StringValueResolver 的匿名类,用于解析 @Value 注解。之前在讲解依赖注入时,在测试代码中也是这样使用的,现在我们知道这一功能是由AbstractApplicationCotext 提供的。此外,还调用了 BeanFactorypreInstantiateSingletons 方法,确保所有的单例都被创建。

//step-10 BeanFactory的最后操作,实例化所有的单例Bean
protected void finishBeanFactoryInitialization(ConfigurableBeanFactory beanFactory) {
    //1) 注册字符串解析器,用于解析@Value注解
    if (!beanFactory.hasEmbeddedValueResolver()) {
        beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
            @Override
            public String resolveStringValue(String strVal) {
                return getEnvironment().resolvePlaceholders(strVal);
            }
        });
    }

    //2) 实例化所有的单例Bean
    beanFactory.preInstantiateSingletons();
}

第 11 步,初始化 Lifecycle 相关组件,并发送容器刷新完成的事件。这里先略过,Lifecycle 组件和事件机制在后面章节中讲解。

//step-11 收尾工作
protected void finishRefresh() {
    //初始化LifecycleProcessor并启动生命周期组件(TODO)
    //发送ContextRefreshedEvent事件(TODO)
}

5. 测试

测试方法比较简单,创建了一个 Spring 容器,向里边注册 BeanDefinition,最终拿到相应的实例。需要注意的是 registerBeanDefinitiongetBean 方法是通过 ApplicationContext 实例调用的,这说明对于装饰模式的核心功能来说,不管是组件类还是装饰类调用,对于外界都是透明的。

//测试方法
@Test
public void testSimpleContext() {
    GenericApplicationContext context = new GenericApplicationContext();
    context.registerBeanDefinition("user", new RootBeanDefinition(User.class));
    context.refresh();  //刷新容器
    User user = context.getBean("user", User.class);
    System.out.println("ApplicationContext简单实现:" + user);
}

从测试结果可以看到,通过 ApplicationContext 同样可以完成注册 BeanDefinition 以及获取单例的操作。

ApplicationContext简单实现:basic.User@4563e9ab

6. 总结

本节我们梳理了 context 模块涉及的 ApplicationContext 的继承结构,包括两个接口,两个抽象类和两个实现类。其中 AbstractApplicationContext 是核心类,不仅拥有一系列属性,还定义了 refresh 方法完成了容器的初始化,其他实现类的构造函数都需要调用 refresh 方法,才能使得容器变得可用。

我们着重分析了 refresh 方法的实现原理,将复杂的流程分为三个部分,首先是准备一个可用的 BeanFactory 实例,然后是对容器设置必要的组件,最后确保所有的 Bean 都被实例化,以及其他的收尾工作。如此一来,我们实现了一个最简单的 ApplicationContext 容器。由于装饰模式的作用,我们将原先由组件类 BeanFactory 完成的功能,转交给装饰类 ApplicationContext 同样可以完成。

7. 项目结构

新增修改一览,新增(13),修改(0)。

context
├─ src
│  ├─ main
│  │  └─ java
│  │     └─ cn.stimd.spring.context
│  │        ├─ support
│  │        │  ├─ AbstractApplicationContext.java (+)
│  │        │  ├─ AbstractRefreshableApplicationContext.java (+)
│  │        │  ├─ ApplicationContextAwareProcessor.java (+)
│  │        │  ├─ GenericApplicationContext.java (+)
│  │        │  └─ PostProcessorRegistrationDelegate.java (+)
│  │        ├─ ApplicationContext.java (+)
│  │        ├─ ApplicationContextAware.java (+)
│  │        ├─ ConfigurableApplicationContext.java (+)
│  │        ├─ EnvironmentAware.java (+)
│  │        └─ ResourceLoaderAware.java (+)
│  └─ test
│     └─ java
│        └─ context
│           └─ basic
│              ├─ BasicTest.java (+)
│              └─ User.java (+)
└─ pom.xml (+)

注:+号表示新增、*表示修改

注:项目的 master 分支会跟随教程的进度不断更新,如果想查看某一节的代码,请选择对应小节的分支代码。


欢迎关注公众号【Java编程探微】,加群一起讨论。

原创不易,觉得内容不错请关注、点赞、收藏。

转载自:https://juejin.cn/post/7399481687656251407
评论
请登录