SpringBoot源码解读与原理分析(八)ApplicationContext


上一篇详细梳理了BeanFactory的相关特性,本篇梳理BeanFactory的一个扩展——ApplicationContext的相关特性。

3.1.2 ApplicationContext

ApplicationContext是基于BeanFactory的扩展,提供了更强大的特性。

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
		MessageSource, ApplicationEventPublisher, ResourcePatternResolver {}

由源码可知,ApplicationContext除了继承BeanFactory接口,还额外继承了几个功能性接口,这些接口共同组成了ApplicationContext扩展的几个核心特性。

3.1.2.1 ApplicationContext根接口

ApplicationContext

Central interface to provide configuration for an application. This is read-only while the application is running, but may be reloaded if the implementation supports this.

第一段:ApplicationContext是为应用程序提供配置的核心接口。在应用程序运行时,它是只读的,但是如果支持的话,它可以重新加载

An ApplicationContext provides:

  • Bean factory methods for accessing application components. Inherited from org.springframework.beans.factory.ListableBeanFactory.
  • The ability to load file resources in a generic fashion. Inherited from the org.springframework.core.io.ResourceLoaderinterface.
  • The ability to publish events to registered listeners. Inherited from the ApplicationEventPublisherinterface.
  • The ability to resolve messages, supporting internationalization. Inherited from the MessageSourceinterface.
  • Inheritance from a parent context. Definitions in a descendant context will always take priority. This means, for example, that a single parent context can be used by an entire web application, while each servlet has its own child context that is independent of that of any other servlet.

第二段:ApplicationContext的特性

  • 访问应用程序组件的BeanFactory方法:继承ListableBeanFactory。
  • 以一般的方式加载文件资源的能力:继承ResourceLoader接口。
  • 发布事件到已注册的监听器的能力:继承ApplicationEventPublisher接口。
  • 解析消息,支持国际化的能力:继承MessageSource接口。
  • 继承父级上下文。后代上下文中的定义优先级更高。这意味着,一个单一的父级上下文可以被整个Wen应用程序使用,而每个Servlet都有自己的子级上下文独立于其他任何Servlet。

In addition to standard org.springframework.beans.factory.BeanFactorylifecycle capabilities, ApplicationContext implementations detect and invoke ApplicationContextAwarebeans as well as ResourceLoaderAware, ApplicationEventPublisherAwareand MessageSourceAwarebeans.

第三段:除了具有标准的BeanFactory生命周期功能,ApplicationContext实现类还检测并调用ApplicationContextAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware接口的Bean。

3.1.2.2 ConfigurableApplicationContext

ConfigurableApplicationContext是ApplicationContext的子接口。

SPI interface to be implemented by most if not all application contexts. Provides facilities to configure an application context in addition to the application context client methods in the org.springframework.context.ApplicationContextinterface.

第一段:ConfigurableApplicationContext会被绝大多数ApplicationContext实现类实现。除了ApplicationContext接口中的应用程序上下文客户端方法,还提供了配置应用程序上下文的功能。

Configuration and lifecycle methods are encapsulated here to avoid making them obvious to ApplicationContext client code. The present methods should only be used by startup and shutdown code.

第二段:配置和生命周期方法都封装在ConfigurableApplicationContext中,目的是避免暴露给ApplicationContext的调用者。当前的方法仅应该由启动和关闭代码使用。

总结:ConfigurableApplicationContext为ApplicationContext提供了“可写”的功能,实现了该接口的实现类可以由客户端代码修改其内部的某些配置。在源码中,可以看到ConfigurableApplicationContext的一些方法:

  • setParent
  • setEnvironment
  • addBeanFactoryPostProcessor
  • addApplicationListener

ConfigurableApplicationContext扩展了一些方法,但一般不希望开发者使用,而只允许刷新容器(refresh)和关闭容器(close)的方法。但如果要定制化ApplicationContext或者对其进行扩展,ConfigurableApplicationContext的扩展则成为重要的切入点。

3.1.2.3 EnvironmentCapable

ApplicationContext继承了EnvironmentCapable。

拓展:在SpringFramework的底层源码中,如果一个接口的名称以Capable结尾,通常意味着可以通过这个接口的某个特定的方法(通常是getXXX())获取特定的组件。

Interface indicating a component that contains and exposes an Environmentreference.

第一段:EnvironmentCapable接口是一个包含并暴露Environment引用的组件。

All Spring application contexts are EnvironmentCapable, and the interface is used primarily for performing instanceofchecks in framework methods that accept BeanFactory instances that may or may not actually be ApplicationContext instances in order to interact with the environment if indeed it is available.

第二段:所有应用程序上下文都适用EnvironmentCapable,该接口主要用于在框架方法中进行instanceof检查,这些方法所接受的BeanFactory实例可能是也可能不是ApplicationContext实例,以便在环境确实可用的情况下与环境交互。

As mentioned, org.springframework.context.ApplicationContext ApplicationContext
extends EnvironmentCapable, and thus exposes a getEnvironment()method; however,
ConfigurableApplicationContextredefines getEnvironment()and narrows the signature to return a ConfigurableEnvironment. The effect is that an Environment object is ‘read-only’ until it is being accessed from a ConfigurableApplicationContext, at which point it too may be configured.

第三段:ApplicationContext继承了EnvironmentCapable,并暴露了getEnvironment()方法;然而,ConfigurableApplicationContext重新定义了getEnvironment()方法并返回ConfigurableEnvironment。这样做的效果是,环境对象时只读的,直到从ConfigurableApplicationContext访问它,此时它是可以被配置的。

总结:EnvironmentCapable是ApplicationContext的其中一个父接口。只要获取到ApplicationContext,就可以借助父类EnvironmentCapable的getEnvironment()方法获取到Environment。

3.1.2.4 MessageSource

ApplicationContext继承了MessageSource。

Strategy interface for resolving messages, with support for the parameterization and internationalization of such messages.

第一段:MessageSource是用于解析消息的策略接口,支持此类消息的参数化和国际化

Spring provides two out-of-the-box implementations for production:

  • org.springframework.context.support.ResourceBundleMessageSource: built on top of the standard java.util.ResourceBundle, sharing its limitations.
  • org.springframework.context.support.ReloadableResourceBundleMessageSource: highly configurable, in particular with respect to reloading message definitions.

第二段:SpringFramework提供了两个开箱即用的实现:

  • ResourceBundleMessageSource :建立在标准的java.util.ResourceBundle之上,共享它的局限性。
  • ReloadableResourceBundleMessageSource :高度可配置,特别是在重新加载消息定义方面。

总结: MessageSource是ApplicationContext的其中一个父接口,用于实现ApplicationContext的国际化支持。当调用ApplicationContext的国际化相关方法时,内部直接将调用转发到MessageSource的落地实现类对象上。

3.1.2.5 ApplicationEventPublisher

从类名可知,ApplicationEventPublisher是一个事件发布器。ApplicationContext继承了ApplicationEventPublisher。

在SpringFramework内部,ApplicationContext是观察者模式中广播器的角色:ApplicationContext实现了ApplicationEventPublisher接口,因此具有事件广播器发布事件的能力;ApplicationContext还组合了ApplicationEventMulticaster,因此具有事件广播器广播事件的能力。

3.1.2.6 ResourcePatternResolver

ApplicationContext继承了ResourcePatternResolver。

Strategy interface for resolving a location pattern (for example, an Ant-style path pattern) into Resource objects.

第一段:ResourcePatternResolver是一个根据特定路径解析资源文件的策略接口(例如Ant风格的路径)。

This is an extension to the org.springframework.core.io.ResourceLoaderinterface. A passed-in ResourceLoader (for example, an org.springframework.context.ApplicationContextpassed in via org.springframework.context.ResourceLoaderAwarewhen running in a context) can be checked whether it implements this extended interface too.

第二段:这是ResourceLoader接口的扩展。可以检查传入的ResourceLoader是否也实现了这个扩展接口(例如在上下文中运行时通过ResourceLoaderAware传入的ApplicationContext)。

PathMatchingResourcePatternResolveris a standalone implementation that is usable outside an ApplicationContext, also used by ResourceArrayPropertyEditorfor populating Resource array bean properties.

第三段:PathMatchingResourcePatternResolver是一个独立的实现,可以在ApplicationContext之外调用,ResourceArrayPropertyEditor也使用它来填充资源数组Bean属性。

Can be used with any sort of location pattern (e.g. “/WEB-INF/*-context.xml”): Input patterns have to match the strategy implementation. This interface just specifies the conversion method rather than a specific pattern format.

第四段:可以用于任何类型的路径模式(例如"/WEB-INF/*-context.xml"):输入模式必须与策略实现匹配。这个接口只是指定转换方法,而不是指定特定的模式格式。

This interface also suggests a new resource prefix “classpath*:” for all matching resources from the class path. Note that the resource location is expected to be a path without placeholders in this case (e.g. “/beans.xml”); JAR files or classes directories can contain multiple files of the same name.

第五段:这个接口还建议为类路径中的所有匹配资源添加一个新的资源前缀“classpath*:”。请注意,在这种情况下,资源位置应该是一个没有占位符的路径(例如“/beans.xml”);JAR文件或类目录可以包含多个同名文件。

总结:从类名可以将ResourcePatternResolver理解为“资源模式解析器”,其作用是根据特定的路径解析资源文件,支持Ant形式的带星号的路径解析。ResourcePatternResolver可以根据特殊的路径返回多个匹配到的资源文件。借助该机制,ApplicationContext可以将所需要的资源文件加载到应用程序中,从而完成功能配置、属性赋值等工作。

拓展:Ant路径匹配写法

  • /WEB-INF/*.xml :匹配/WEB-INF目录下的任意XML文件。
  • /WEB-INF/**/beans-*.xml :匹配/WEB-INF目录下任意层级目录的beans-开头的XML文件。
  • /**/*.xml :匹配任意XML文件。
3.1.2.7 AbstractApplicationContext

介绍完接口,接下来是ApplicationContext的几个重要实现类。借助IDEA的结构图,可以看到,ApplicationContext的实现类中,AbstractApplicationContext显得尤为重要:

ApplicationContext的实现类

实际上AbstractApplicationContext定义和实现了绝大部分应用上下文的特性和功能。

Abstract implementation of the org.springframework.context.ApplicationContextinterface. Doesn’t mandate the type of storage used for configuration; simply implements common context functionality. Uses the Template Method design pattern, requiring concrete subclasses to implement abstract methods.

第一段:ApplicationContext接口的抽象实现类。它只是简单地实现了应用上下文的基本功能,并不强制约束配置的承载形式(XML、注解驱动等)。内部使用模板方法设计模式规范整体功能逻辑,具体实现由子类负责。

In contrast to a plain BeanFactory, an ApplicationContext is supposed to detect special beans defined in its internal bean factory: Therefore, this class automatically registers BeanFactoryPostProcessors, BeanPostProcessors, and ApplicationListenerswhich are defined as beans in the context.

第二段:与普通的BeanFactory相比,ApplicationContext能够检测在其内部BeanFactory中定义的特殊Bean。这个类自动注册了BeanFactoryPostProcessors、BeanPostProcessors喝ApplicationListeners,这些都被定义为上下文中的Bean。

A org.springframework.context.MessageSourcemay also be supplied as a bean in the context, with the name “messageSource”; otherwise, message resolution is delegated to the parent context. Furthermore, a multicaster for application events can be supplied as an “applicationEventMulticaster” bean of type org.springframework.context.event.ApplicationEventMulticasterin the context; otherwise, a default multicaster of type org.springframework.context.event.SimpleApplicationEventMulticasterwill be used.

第三段:ApplicationContext实现了国际化接口MessageSource,否则消息解析将委托给父级上下文;实现了事件广播器接口ApplicationEventMulticaster,否则将使用默认的事件广播器接口SimpleApplicationEventMulticaster。

Implements resource loading by extending org.springframework.core.io.DefaultResourceLoader. Consequently treats non-URL resource paths as class path resources (supporting full class path resource names that include the package path, e.g. “mypackage/myresource.dat”), unless the getResourceByPathmethod is overridden in a subclass.

第四段:通过继承DefaultResourceLoader实现资源加载。因此将非URL资源路径视为类路径资源(支持包含包路径的完整类路径资源名,例如“mypackage/myresource.dat”),除非getResourceByPath方法在子类中被重写。

总结:AbstractApplicationContext只构建功能抽象,实现了国际化接口、事件广播器接口,加载资源文件的策略默认是直接继承DefaultResourceLoader的策略。

3.1.2.8 GenericApplicationContext

Generic ApplicationContext implementation that holds a single internal org.springframework.beans.factory.support.DefaultListableBeanFactoryinstance and does not assume a specific bean definition format. Implements the org.springframework.beans.factory.support.BeanDefinitionRegistryinterface in order to allow for applying any bean definition readers to it.

第一段:GenericApplicationContext实现类拥有一个内部DefaultListableBeanFactory实例,并且不采用特定的Bean定义格式。它还实现了BeanDefinitionRegistry接口,以便允许将任何Bean定义读取器应用于该容器中。

重点:

  • GenericApplicationContext组合了BeanFactory,因此ApplicationContext并不是继承自BeanFactory容器,而是组合了BeanFactory;
  • GenericApplicationContext实现了BeanDefinitionRegistry接口,所以Bean的定义信息可以通过GenericApplicationContext注册到容器中。

Typical usage is to register a variety of bean definitions via the org.springframework.beans.factory.support.BeanDefinitionRegistryinterface and then call refresh()to initialize those beans with application context semantics (handling org.springframework.context.ApplicationContextAware, auto-detecting BeanFactoryPostProcessors, etc).

第二段:典型用法是通过BeanDefinitionRegistry结构注册各种Bean定义信息,然后调用refresh()方法在应用上下文语义中初始化这些bean对象(处理ApplicationContextAware,自动检测BeanFactoryPostProcessors等)。

注意,在GenericApplicationContext中实现的Bean定义注册方法registerBeanDefinition方法,在底层是调用DefaultListableBeanFactory的registerBeanDefinition方法,说明它对此没有做任何扩展,仅仅是委托机制的体现。

In contrast to other ApplicationContext implementations that create a new internal BeanFactory instance for each refresh, the internal BeanFactory of this context is available right from the start, to be able to register bean definitions on it. refresh()may only be called once.

第三段:与其他的「为每次刷新都创建一个新的内部BeanFactory实例」 的ApplicationContext实现类相比,此上下文中的内部BeanFactory从一开始就可以使用,以便能够在其上注册Bean定义。refresh()方法只能被调用一次。

注意,此上下文中的内部BeanFactory从一开始就可以使用,是因为在GenericApplicationContext的构造方法中就已经初始化完毕

public GenericApplicationContext() {
    // 内置的BeanFactory在构造方法中被初始化
    this.beanFactory = new DefaultListableBeanFactory();
}

@Override
protected final void refreshBeanFactory() throws IllegalStateException {
    if (!this.refreshed.compareAndSet(false, true)) {
        // 利用CAS,确保refresh()方法只能被调用一次
        throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
    }
    this.beanFactory.setSerializationId(getId());
}

For the typical case of XML bean definitions, simply use ClassPathXmlApplicationContextor FileSystemXmlApplicationContext, which are easier to set up - but less flexible, since you can just use standard resource locations for XML bean definitions, rather than mixing arbitrary bean definition formats. The equivalent in a web environment is org.springframework.web.context.support.XmlWebApplicationContext.

第四段:对于XML Bean定义的典型情况,只需使用ClassPathXmlApplicationContext或FileSystemXmlApplicationContext,因为它们更易于设置(但灵活性较差,只能从标准的资源配置文件中读取XML Bean定义,而不能混合使用任意Bean定义的格式)。在Web环境中,GenericApplicationContext的替代方案是XmlWebApplicationContext。

3.1.2.9 AnnotationConfigApplicationContext

Standalone application context, accepting component classes as input in particular @Configuration-annotated classes, but also plain @Componenttypes and JSR-330 compliant classes using javax.injectannotations.

第一段:AnnotationConfigApplicationContext是一个独立的ApplicationContext,它接受组件类作为输入(特别是使用@Configuration注解标注的类),还可以使用普通的@Component类和符合JSR-330规范(使用javax.inject包的注解)的类。

Allows for registering classes one by one using register(Class...)as well as for classpath scanning using scan(String...).

第二段:允许使用register(Class…)方法直接传入指定的配置类,以及使用scan(String…)方法进行类路径的包扫描。

In case of multiple @Configurationclasses, @Beanmethods defined in later classes will override those defined in earlier classes. This can be leveraged to deliberately override certain bean definitions via an extra @Configurationclass.

第三段:对于多个@Configuration类,在后面的类中定义的@Bean方法将覆盖在先前的类中定义的这些方法。这可以通过一个额外的@Configuration类来故意覆盖某些Bean定义。

总结:AnnotationConfigApplicationContext是SpringFramework中最常使用的注解驱动IOC容器,对注入被@Configuration标注的类很友好。它本身继承GenericApplicationContext,因此也只能刷新一次。

3.1.2.10 AbstractRefreshableConfigApplicationContext

AbstractRefreshableApplicationContextsubclass that adds common handling of specified config locations. Serves as base class for XML-based application context implementations such as ClassPathXmlApplicationContextand FileSystemXmlApplicationContext, as well as org.springframework.web.context.support.XmlWebApplicationContext.

AbstractRefreshableApplicationContext子类添加了对指定的配置文件路径的通用处理功能,用作基于XML配置文件的IOC容器的基类,它的落地实现有ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,还有支持Web环境的XmlWebApplicationContext。

但要注意的是,AbstractRefreshableApplicationContext作为基于XML配置文件的IOC容器,SpringBoot已不再使用,SpringBoot扩展出的IOC容器全部都是基于注解驱动的。

基于XML配置文件的IOC容器有一个特殊的特性:可重复刷新。并且IOC容器会在加载配置文件时才初始化内部的BeanFactory,然后是解析配置文件、注册bean对象等动作。

3.1.3 选择ApplicationContext而不是BeanFactory

在实际项目开发中,应该选择ApplicationContext还是BeanFactory的问题,官方文档给出了一种观点:

You should use an ApplicationContextunless you have a good reason for not doing so, with GenericApplicationContextand its subclass AnnotationConfigApplicationContextas the common implementations for custom bootstrapping. These are the primary entry points to Spring’s core container for all common purposes: loading of configuration files, triggering a classpath scan, programmatically registering bean definitions and annotated classes, and (as of 5.0) registering functional bean definitions.

你应该使用ApplicationContext,除非你有好的理由不用它。一般情况下,我们推荐将GenericApplicationContext及其子类AnnotationConfigApplicationContext作为自定义引导的常见实现。这些实现类是用于所有常见目的的SpringFramework核心容器主要入口点:加载配置文件,出发类路径扫描,编程式注册Bean定义和带注解的类,以及(从5.0版本开始)注册功能性Bean的定义。

这段话下面还提供了一张表,对比了BeanFactory和ApplicationContext的不同特性:

BeanFactory和ApplicationContext的不同特性
第一项到第六项分别是:Bean的实例化和属性注入、生命周期管理、Bean后置处理器的支持、BeanFactory后置处理器的支持、消息转换服务(国际化)、事件发布机制(事件驱动)。可见,BeanFactory仅支持第一项Bean的实例化和属性注入,ApplicationContext支持全部六项。

因此,ApplicationContext相较于BeanFactory功能更强大,这也解释了为什么日常开发中都是使用ApplicationContext而很少使用BeanFactory。

3.1.4 总结

至此,可以总结一下ApplicationContext的特性:

  • ApplicationContext :根接口
  • ConfigurableApplicationContext :可写功能
  • EnvironmentCapable :获取Environment组件
  • MessageSource :国际化支持
  • ApplicationEventPublisher :事件发布
  • ResourcePatternResolver :资源解析
  • AbstractApplicationContext :构建功能抽象
  • GenericApplicationContext :注解驱动
  • AnnotationConfigApplicationContext :注解驱动
  • AbstractRefreshableConfigApplicationContext :XML配置文件驱动

将SpringFramework的两部分——BeanFactory和ApplicationContext接口及其子类合在一起看,利用IDEA生成这两个接口的继承关系图:

BeanFactory和ApplicationContext
上图的左侧主要是ApplicationContext的父级接口及实现类,右侧主要是BeanFactory及其子接口。ApplicationContext继承了ListableBeanFactory、HierarchicalBeanFactory,从而使两者联系起来。

······

本节完,更多内容请查阅分类专栏:SpringBoot源码解读与原理分析

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

维先生d

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值