文章

ApplicationContext相关接口架构分析

ApplicationContext相关接口架构分析

本文主要基于 Spring 5.0.6.RELEASE

摘要: 原创出处http://cmsblogs.com/?p=todo「小明哥」,谢谢!


在前面 40 篇博客中都是基于 <font style="color:rgb(51, 51, 51);">BeanFactory</font> 这个容器来进行分析的,<font style="color:rgb(51, 51, 51);">BeanFactory</font> 容器有点儿简单,它并不适用于我们生产环境,在生产环境我们通常会选择 <font style="color:rgb(51, 51, 51);">ApplicationContext</font> ,相对于大多数人而言,它才是正规军,相比于 BeanFactory 这个杂牌军而言,它由如下几个区别:

  1. 继承 <font style="color:rgb(51, 51, 51);">MessageSource</font> ,提供国际化的标准访问策略。
  2. 继承 <font style="color:rgb(51, 51, 51);">ApplicationEventPublisher</font> ,提供强大的事件机制。
  3. 扩展 <font style="color:rgb(51, 51, 51);">ResourceLoader</font><font style="color:rgb(51, 51, 51);">Resource</font> ,可以用来加载多个 ,可以灵活访问不同的资源。
  4. 对 Web 应用的支持。

1. ApplicationContext

下图是 <font style="color:rgb(51, 51, 51);">ApplicationContext</font> 结构类图:

3a0321713096156d42661f2df11a93c2

ApplicationContext 结构类图

  • BeanFactory :Spring 管理 Bean 的顶层接口,我们可以认为他是一个简易版的 Spring 容器。ApplicationContext 继承 BeanFactory 的两个子类:HierarchicalBeanFactory 和 ListableBeanFactory。HierarchicalBeanFactory 是一个具有层级关系的 BeanFactory,拥有属性 parentBeanFactory 。ListableBeanFactory 实现了枚举方法可以列举出当前 BeanFactory 中所有的 bean 对象而不必根据 name 一个一个的获取。
  • ApplicationEventPublisher :用于封装事件发布功能的接口,向事件监听器(Listener)发送事件消息。
  • ResourceLoader :Spring 加载资源的顶层接口,用于从一个源加载资源文件。ApplicationContext 继承 ResourceLoader 的子类 ResourcePatternResolver,该接口是将 location 解析为 Resource 对象的策略接口。
  • MessageSource :解析 message 的策略接口,用不支撑国际化等功能。
  • EnvironmentCapable :用于获取 Environment 的接口。

2. ApplicationContext 的子接口

<font style="color:rgb(51, 51, 51);">ApplicationContext</font> 有两个直接子类:<font style="color:rgb(51, 51, 51);">WebApplicationContext</font><font style="color:rgb(51, 51, 51);">ConfigurableApplicationContext</font>

2.1 WebApplicationContext

1
// WebApplicationContext.javapublicinterfaceWebApplicationContextextendsApplicationContext{ServletContext getServletContext();}

该接口只有一个<font style="color:rgb(51, 51, 51);">#getServletContext()</font>方法,用于给 Servlet 提供上下文信息。

2.2 ConfigurableApplicationContext

1
// ConfigurableApplicationContext.javapublicinterfaceConfigurableApplicationContextextendsApplicationContext,Lifecycle,Closeable{    // 为 ApplicationContext 设置唯一 ID    voidsetId(String id);    // 为 ApplicationContext 设置 parent    // 父类不应该被修改:如果创建的对象不可用时,则应该在构造函数外部设置它    voidsetParent(@Nullable ApplicationContext parent);    // 设置 Environment    voidsetEnvironment(ConfigurableEnvironment environment);    // 获取 Environment    @Override    ConfigurableEnvironment getEnvironment();    // 添加 BeanFactoryPostProcessor    voidaddBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);    // 添加 ApplicationListener    voidaddApplicationListener(ApplicationListener<?> listener);    // 添加 ProtocolResolver    voidaddProtocolResolver(ProtocolResolver resolver);    // 加载或者刷新配置    // 这是一个非常重要的方法    voidrefresh()throwsBeansException, IllegalStateException;    // 注册 shutdown hook    voidregisterShutdownHook();    // 关闭 ApplicationContext    @Override    voidclose();    // ApplicationContext 是否处于激活状态    booleanisActive();    // 获取当前上下文的 BeanFactory    ConfigurableListableBeanFactorygetBeanFactory()throwsIllegalStateException;}

从上面代码可以看到 <font style="color:rgb(51, 51, 51);">ConfigurableApplicationContext</font> 接口提供的方法都是对 <font style="color:rgb(51, 51, 51);">ApplicationContext</font> 进行配置的,例如<font style="color:rgb(51, 51, 51);">#setEnvironment(ConfigurableEnvironment environment)</font><font style="color:rgb(51, 51, 51);">#addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor)</font>,同时它还继承了如下两个接口:

  • Lifecycle:对 context 生命周期的管理,它提供 #start() 和 #stop() 方法启动和暂停组件。
  • Closeable:标准 JDK 所提供的一个接口,用于最后关闭组件释放资源等。

2.3 ConfigurableWebApplicationContext

<font style="color:rgb(51, 51, 51);">WebApplicationContext</font> 接口和 <font style="color:rgb(51, 51, 51);">ConfigurableApplicationContext</font> 接口有一个共同的子类接口 <font style="color:rgb(51, 51, 51);">ConfigurableWebApplicationContext</font>,该接口将这两个接口进行合并,提供了一个可配置、可管理、可关闭的 <font style="color:rgb(51, 51, 51);">WebApplicationContext</font> ,同时该接口还增加了<font style="color:rgb(51, 51, 51);">#setServletContext(ServletContext servletContext)</font><font style="color:rgb(51, 51, 51);">setServletConfig(ServletConfig servletConfig)</font>等方法,用于装配 <font style="color:rgb(51, 51, 51);">WebApplicationContext</font> 。代码如下:

1
// ConfigurableWebApplicationContext.javapublicinterfaceConfigurableWebApplicationContextextendsWebApplicationContext,ConfigurableApplicationContext{    voidsetServletContext(@Nullable ServletContext servletContext);    voidsetServletConfig(@Nullable ServletConfig servletConfig);    ServletConfig getServletConfig();    voidsetNamespace(@Nullable String namespace);    String getNamespace();    voidsetConfigLocation(String configLocation);    voidsetConfigLocations(String... configLocations);    String[] getConfigLocations();}

上面三个接口就可以构成一个比较完整的 Spring 容器,整个 Spring 容器体系涉及的接口较多,所以下面小编就一个具体的实现类来看看 ApplicationContext 的实现(其实在前面一系列的文章中,小编对涉及的大部分接口都已经分析了其原理),当然不可能每个方法都涉及到,但小编会把其中最为重要的实现方法贴出来分析。ApplicationContext 的实现类较多,就以 ClassPathXmlApplicationContext 来分析 ApplicationContext

3. ClassPathXmlApplicationContext

ClassPathXmlApplicationContext 是我们在学习 Spring 过程中用的非常多的一个类,很多人第一个接触的 Spring 容器就是它,包括小编自己,下面代码我想很多人依然还记得吧。

1
// 示例ApplicationContext ac =newClassPathXmlApplicationContext("applicationContext.xml");StudentService studentService = (StudentService)ac.getBean("studentService");

下图是 ClassPathXmlApplicationContext 的结构类图:

dde0bf4ae9014ec73c80f4c45045850a

ClassPathXmlApplicationContext 的类图

主要的的类层级关系如下:

1
org.springframework.context.support.AbstractApplicationContextorg.springframework.context.support.AbstractRefreshableApplicationContextorg.springframework.context.support.AbstractRefreshableConfigApplicationContextorg.springframework.context.support.AbstractXmlApplicationContextorg.springframework.context.support.ClassPathXmlApplicationContext

这种设计是模板方法模式典型的应用,AbstractApplicationContext 实现了 ConfigurableApplicationContext 这个全家桶接口,其子类 AbstractRefreshableConfigApplicationContext 又实现了 BeanNameAware 和 InitializingBean 接口。所以 ClassPathXmlApplicationContext 设计的顶级接口有:

1
2
3
4
5
6
7
8
9
BeanFactorySpring 容器 Bean 的管理
MessageSource管理 message 实现国际化等功能
ApplicationEventPublisher事件发布
ResourcePatternResolver资源加载
EnvironmentCapable系统 Environmentprofile + Properties 相关
Lifecycle管理生命周期
Closable关闭释放资源
InitializingBean自定义初始化
BeanNameAware设置 beanName  Aware 接口

下面就这些接口来一一分析。

3.1 MessageSource

MessageSource 定义了获取 message 的策略方法#getMessage(…)。

在 ApplicationContext 体系中,该方法由 AbstractApplicationContext 实现。

在 AbstractApplicationContext 中,它持有一个 MessageSource 实例,将#getMessage(…)方法委托给该实例来实现,代码如下:

1
// AbstractApplicationContext.javaprivate MessageSource messageSource;// 实现 getMessage()public String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale) {    // 委托给 messageSource 实现    return getMessageSource().getMessage(code, args, defaultMessage, locale);}private MessageSource getMessageSource()throwsIllegalStateException {    if(this.messageSource ==null) {        throw new IllegalStateException("MessageSource not initialized - "+"call 'refresh' before accessing messages via the context: "+this);    }    return this.messageSource;}
  • 真正实现逻辑,是在 AbstractMessageSource 中,代码如下:
1
// AbstractMessageSource.javapublic final String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale) {    String msg = getMessageInternal(code, args, locale);    if(msg !=null) {        return msg;    }    if(defaultMessage ==null) {        return getDefaultMessage(code);    }    return renderDefaultMessage(defaultMessage, args, locale);}

```plain text

  • 具体的实现这里就不分析了,有兴趣的小伙伴可以自己去深入研究。

    ```

3.2 ApplicationEventPublisher

ApplicationEventPublisher ,用于封装事件发布功能的接口,向事件监听器(Listener)发送事件消息。

该接口提供了一个#publishEvent(Object event, …)方法,用于通知在此应用程序中注册的所有的监听器。该方法在 AbstractApplicationContext 中实现。

1
// AbstractApplicationContext.java@OverridepublicvoidpublishEvent(ApplicationEvent event) {    publishEvent(event,null);}@OverridepublicvoidpublishEvent(Object event) {    publishEvent(event,null);}protected void publishEvent(Object event, @Nullable ResolvableType eventType) {    Assert.notNull(event,"Event must not be null");    // Decorate event as an ApplicationEvent if necessary    ApplicationEvent applicationEvent;    if(eventinstanceofApplicationEvent) {        applicationEvent = (ApplicationEvent) event;    }else{        applicationEvent =newPayloadApplicationEvent<>(this, event);        if(eventType ==null) {            eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();        }    }    // Multicast right now if possible - or lazily once the multicaster is initialized    if(this.earlyApplicationEvents !=null) {        this.earlyApplicationEvents.add(applicationEvent);    }else{        getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);    }    // Publish event via parent context as well...    if(this.parent !=null) {        if(this.parentinstanceofAbstractApplicationContext) {            ((AbstractApplicationContext)this.parent).publishEvent(event, eventType);        }else{            this.parent.publishEvent(event);        }    }}
  • 如果指定的事件不是 ApplicationEvent,则它将包装在PayloadApplicationEvent 中。
  • 如果存在父级 ApplicationContext ,则同样要将 event 发布给父级 ApplicationContext 。

3.3 ResourcePatternResolver

ResourcePatternResolver 接口继承 ResourceLoader 接口,为将 location 解析为 Resource 对象的策略接口。他提供的#getResources(String locationPattern)方法,在 AbstractApplicationContext 中实现,在 AbstractApplicationContext 中他持有一个 ResourcePatternResolver 的实例对象。代码如下:

1
// AbstractApplicationContext.java/** ResourcePatternResolver used by this context. */private ResourcePatternResolver resourcePatternResolver;public Resource[] getResources(String locationPattern)throws IOException {    return this.resourcePatternResolver.getResources(locationPattern);}
  • 如果小伙伴对 Spring 的 ResourceLoader 比较熟悉的话,你会发现最终是在 PathMatchingResourcePatternResolver 中实现,该类是 ResourcePatternResolver 接口的实现者。

3.4 EnvironmentCapable

提供当前系统环境 Environment 组件。提供了一个#getEnvironment()方法,用于返回 Environment 实例对象。该方法在 AbstractApplicationContext 实现。代码如下:

1
// AbstractApplicationContext.javapublic ConfigurableEnvironment getEnvironment() {    if(this.environment ==null) {        this.environment = createEnvironment();    }    return this.environment;}
  • 如果持有的 environment 实例对象为空,则调用 #createEnvironment() 方法,创建一个。代码如下:
1
// AbstractApplicationContext.javaprotected ConfigurableEnvironment createEnvironment() {    return new StandardEnvironment();}

```plain text

  • StandardEnvironment 是一个适用于非 WEB 应用的 Environment。

    ```

3.5 Lifecycle

Lifecycle ,一个用于管理声明周期的接口。

在 AbstractApplicationContext 中存在一个 LifecycleProcessor 类型的实例对象lifecycleProcessor,AbstractApplicationContext 中关于 Lifecycle 接口的实现都是委托给lifecycleProcessor实现的。代码如下:

1
// AbstractApplicationContext.java/** LifecycleProcessor for managing the lifecycle of beans within this context. */@Nullableprivate LifecycleProcessor lifecycleProcessor;@Overridepublicvoidstart() {    getLifecycleProcessor().start();    publishEvent(new ContextStartedEvent(this));}@Overridepublicvoidstop() {    getLifecycleProcessor().stop();    publish Event(new ContextStoppedEvent(this));}@OverridepublicbooleanisRunning() {    return (this.lifecycleProcessor !=null && this.lifecycleProcessor.isRunning());}
  • 在启动、停止的时候会分别发布 ContextStartedEvent 和 ContextStoppedEvent 事件。

3.6 Closable

Closable 接口用于关闭和释放资源,提供了#close()方法,以释放对象所持有的资源。在 ApplicationContext 体系中由AbstractApplicationContext 实现,用于关闭 ApplicationContext 销毁所有 Bean ,此外如果注册有 JVM shutdown hook ,同样要将其移除。代码如下:

1
// AbstractApplicationContext.javapublic void close() {synchronized(this.startupShutdownMonitor) {    doClose();    // If we registered a JVM shutdown hook, we don't need it anymore now:    // We've already explicitly closed the context.    if(this.shutdownHook !=null) {        try{            Runtime.getRuntime().removeShutdownHook(this.shutdownHook);        }catch(IllegalStateException ex) {            // ignore - VM is already shutting down        }    }}
  • 调用 #doClose() 方法,发布 ContextClosedEvent 事件,销毁所有 Bean(单例),关闭 BeanFactory 。代码如下:
1
// AbstractApplicationContext.javaprotected void doClose() {    // ... 省略部分代码    try{        // Publish shutdown        event.publishEvent(newContextClosedEvent(this));    }catch(Throwable ex) {        logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);    }    // ... 省略部分代码    destroyBeans();    closeBeanFactory();    onClose();    this.active.set(false);}

3.7 InitializingBean

InitializingBean 为 Bean 提供了初始化方法的方式,它提供的#afterPropertiesSet()方法,用于执行初始化动作。在 ApplicationContext 体系中,该方法由 AbstractRefreshableConfigApplicationContext 实现,代码如下:

1
// AbstractRefreshableConfigApplicationContext.javapublic void afterPropertiesSet() {    if(!isActive()) {        refresh();    }}
  • 执行该方法将在下篇文章进行详细分析说明 refresh() 方法,该方法在 AbstractApplicationContext 中执行,执行整个 Spring 容器的初始化过程。 。

3.8 BeanNameAware

BeanNameAware ,设置 Bean Name 的接口。接口在 AbstractRefreshableConfigApplicationContext 中实现。

1
// AbstractRefreshableConfigApplicationContext.javapublic void setBeanName(String name) {    if(!this.setIdCalled) {        super.setId(name);        setDisplayName("ApplicationContext '"+ name +"'");    }}

4. 小结

由于篇幅问题,再加上大部分接口小编都已经在前面文章进行了详细的阐述,所以本文主要是以 Spring Framework 的 ApplicationContext 为中心,对其结构和功能的实现进行了简要的说明。

这里不得不说 Spring 真的是一个非常优秀的框架,具有良好的结构设计和接口抽象,它的每一个接口职能单一,且都是具体功能到各个模块的高度抽象,且几乎每套接口都提供了一个默认的实现(defaultXXX)。

对于 ApplicationContext 体系而言,他继承 Spring 中众多的核心接口,能够为客户端提供一个相对完整的 Spring 容器,接口 ConfigurableApplicationContext 对 ApplicationContext 接口再次进行扩展,提供了生命周期的管理功能。

抽象类 ApplicationContext 对整套接口提供了大部分的默认实现,将其中“不易变动”的部分进行了封装,通过“组合”的方式将“容易变动”的功能委托给其他类来实现,同时利用模板方法模式将一些方法的实现开放出去由子类实现,从而实现“对扩展开放,对修改封闭”的设计原则。

最后我们再来领略下图的风采:

dde0bf4ae9014ec73c80f4c45045850a

ClassPathXmlApplicationContext 的类图

本文由作者按照 CC BY 4.0 进行授权