Spring5源码解析-Spring中的应用上下文
之前讲到过,Spring中的 beans生活(用这俩字觉得更形象具体)在其应用程序的上下文环境中。在本文中,我们将详细介绍应用程序上下文,另外此篇同样是由域联系到的逃逸分析的关于Spring容器的续篇。
关于Spring5源码解析-@Autowired这篇文章讲了通过@Autowired
注解进行依赖注入。这一次我们来探讨应用程序上下文(application context)的概念。在第一部分中,我们来看看所有Spring管理的bean生活在什么样的环境中。在第二部分,来分析下到负责上下文管理的类。在最后一部分中,我们来进行一些实践操作。
什么是Spring的应用程序上下文?
众所周知,Spring管理的这些类被称为bean,并且生活在Spring容器中。bean处理程序的最基本实现是bean factory。作为org.springframework.beans.factory.BeanFactory接口的实现类,这是一个初始化,配置和管理bean的容器。但通常在Spring应用程序中仅使用BeanFactory
是不够的。它出现在应用程序上下文中。
应用程序上下文(Application context)是一种面向企业化(其实Spring文档里也有面向企业这一说,不过这不就是企业里流水线的工厂里才能有的东西么
)的bean工厂。作为标准bean工厂,它是bean class生活的空间。但与标准bean工厂不同,应用程序上下文提供了一个补充企业层(也就是通用的东西了,比如企业里的胸牌,服装等)。又迷糊了吧,举个例子 :例如,通过提供国际化,转换服务或事件传播,使我们省去很多麻烦去亲自处理。通常,应用程序上下文优于bean工厂。但它的唯一缺点是内存消耗比bean工厂大,出现这种情况是由于补充的服务。如果内存的使用对于你的程序要求非常苛刻(例如在applet或移动环境中),请考虑更多使用bean factory。否则,在更标准的应用程序中,应使用应用程序上下文(application context)。
Spring的应用程序上下文类
想要了解Spring中应用程序上下文,关键部分就是org.springframework.context.ApplicationContext接口。它扩展了一些其他接口:
- org.springframework.core.env.EnvironmentCapable:用于标记对象来对外暴露自己说我实现了Environment接口。根据这个接口的注释可以知道,它主要用于完成类型的检查。
1 | /** |
- org.springframework.beans.factory.ListableBeanFactory:通过继承该interface可以列出所有bean,也可以只列出与预期类型相对应的bean。
- org.springframework.beans.factory.HierarchicalBeanFactory:支持分层bean的管理。
- org.springframework.context.MessageSource:用来解决消息支持国际化。
- org.springframework.context.ApplicationEventPublisher:通过该接口,可以允许通知所有类来监听到某些应用程序上下文事件。
- org.springframework.core.io.support.ResourcePatternResolver:是一个有助于将资源地址(例如:classpath:/WEB-INF/web.xml)解析到org.springframework.core.io.Resource对象中的策略接口。
1 | /** |
对于我们来说,实现这些接口使应用程序上下文比一个简单的bean工厂更有用。我们通过org.springframework.web.context.support.XmlWebApplicationContext这个实现类来看其在Web应用程序中使用。此类扩展了同一个包下AbstractRefreshableWebApplicationContext
这个抽象类。
XmlWebApplicationContext
实现了AbstractRefreshableApplicationContext
中的抽象方法loadBeanDefinitions
,用于读取所有bean。从这个方法实现,可以看出,所有的bean都是通过org.springframework.beans.factory.xml.XmlBeanDefinitionReader从XML文件读取的。
1 | /** |
另一个有趣的方法,继承自AbstractRefreshableWebApplicationContext
,是postProcessBeanFactory
。它在加载所有bean定义之后并在其实例化之前被调用(postProcess
就是bean构造函数之后调用即实例化之前)。AbstractRefreshableWebApplicationContext
使用它来注册请求和会话作用域以及环境bean(具体看下面源码)。另外,这个抽象类实现了ConfigurableWebApplicationContext
接口,这样一来就可以定义servlet
的上下文和一些本地的配置。
1 | /** |
由XmlWebApplicationContext
间接继承的另一个抽象类是AbstractRefreshableApplicationContext
。它有几种处理上下文刷新的方法。处理事件通知的类是org.springframework.context.support.AbstractApplicationContext,由XmlWebApplicationContext
间接继承。它包含一个将事件(ApplicationEvent
类的实例)发送到所有侦听对象的publishEvent
方法。
但是我们的重中之重是处理生命周期,是AbstractApplicationContext
类的public void refresh() throws BeansException, IllegalStateException
方法来做到的。
1 |
|
通过阅读源码,我们可以注意到以下操作:
- 上下文准备刷新(属性源初始化)
- bean工厂准备好用来一系列操作(classloader定义,基本bean注册)
- bean后置处理(postProcessBeanFactory方法)被调用
- 消息源(消息管理)被初始化
- event multicaster初始化(event multicaster是将事件分派到合适的侦听对象的对象)
- 在特定的上下文子类中初始化其他特殊的bean。
- 监听器的注册
- 所有剩余的bean的实例化(例如:转换服务)
在非Web环境中,我们可以使用标准应用程序上下文,如FileSystemXmlApplicationContext
,ClassPathXmlApplicationContext
或GenericXmlApplicationContext
。
关于Spring的应用程序环境的一些实践
关于此 部分,我们将看到:如何在一个控制器中获得一个上下文,查找得到一些bean配置并来解析一个消息。在进入正式的代码之前,我们需要做一些上下文的配置:
1 | <!-- activate configuration by annotations, for example enable @Controller annotation --> |
通过上面的配置,我们可以编写一个测试controller和一个类ApplicationContextProvider,它将保存一个应用程序上下文实例并按需返回:
1 | // controller, TestController.java |
ApplicationContext
实例由Spring
管理。这就是为什么我们可以使用@Autowired
注解将其注入另一个Spring管理的bean(在我们的例子中是一个controller )。这是通过注入的从一个bean得到上下文第一种方法。
第二种方法是使ApplicationContextProvider
类实现org.springframework.context.ApplicationContextAware接口。这里需要说一下,该接口实现后可以获取当前正在运行的ApplicationContext
的这个事件的通知。所以实现类必须实现这个方法:void setApplicationContext(ApplicationContext applicationContext)throws BeansException。该方法允许设置当前的ApplicationContext
实例并用来使用。上下文通过org.springframework.context.support.ApplicationContextAwareProcessor传递给ApplicationContextAware
实现,在AbstractApplicationContext
类中注册(见下面源码)。需要注意的是,ApplicationcontextAwareProcessor
也用于设置bean工厂或应用程序的上下文环境,见下面此类源码中的private final StringValueResolver embeddedValueResolver;
的StringValueResolver
接口的实现。可以知道,要实现这两种功能,这些类必须分别从org.springframework.context包中实现EmbeddedValueResolverAware
和EnvironmentAware
接口。
1 | /** |
1 | /** |
在我们的例子中,对于ApplicationContextAware
的实现是只是一个简单的上下文提供者。但是在别的地方,我们定义的这个provider可能是用来得到上下文资源的对象。这就是关于获取应用程序上下文的两种方式。
最后,我们通过一个方法来使用context(上下文)的getMessage方法来对消息解析。在我们的message_en.properties
文件中,可以事先声明消息的模板:testMessage =It’s our test message with content。然后我们会在日志文件中看到相应的输出。
顺便说一下,从ApplicationContextProvider
获得的对象和@Autowired
的对象之间的上下文是相同的:
1 | [TestController] Received application context :Root WebApplicationContext: startup date [Wed Apr 02 20:00:23 CEST 2014]; root of context hierarchy |