概述ApplicationContextApplicationContext 的子接口WebApplicationContextConfigurableApplicationContextConfigurableWebApplicationContextClassPathXmlApplicationContextMessageSourceApplicationEventPublisherResourcePatternResolverEnvironmentCapableLifecycleCloseableInitializingBeanBeanNameAware总结参考文献
概述
在 Spring之IoC理论一章中提到关于 IoC 的学习主要涉及到五大模块,从 Resource 和 ResourceLoader 用于资源管理开始,然后讲述 BeanDefinitionReader 如何将 Resource 转换为 IoC 容器独特的数据存储对象:BeanDefinition,紧接着又介绍了 BeanFactory 是如何实现 getBean()
方法来获取 bean 实例对象。至此关于 IoC 的实现流程其实已经大概介绍完毕了,其中主要基于 BeanFactory 进行分析的,但是在实际生产环境中,我们使用比较多的是 ApplicationContext,该接口包含 BeanFactory 的所有功能,同时又提供了更多的扩展功能。
ApplicationContext 相比于 BeanFactory 有以下几个区别:
- 继承 MessageSource,提供国际化的标准访问策略。
- 继承 ApplicationEventPublisher ,提供强大的事件机制。
- 扩展 ResourceLoader,可以用来加载多个 Resource,可以灵活访问不同的资源。
- 对 Web 应用的支持。
接着步入正题,我们简单学习一下 ApplicationContext。
ApplicationContext
下图是 ApplicationContext 结构类图:
可以看到 ApplicationContext 实现了很多个接口,这里我们简单介绍几个接口:
- BeanFactory:Spring 管理 Bean 的顶层接口,ApplicationContext 继承了 BeanFactory 的两个子类:HierarchicalBeanFactory 和 ListableBeanFactory。HierarchicalBeanFactory 是一个具有层级关系的 BeanFactory,拥有属性 parentBeanFactory。 ListableBeanFactory 实现了枚举方法可以列举出当前 BeanFactory 中所有的 bean 对象而不必根据 name 一个一个的获取。
- ApplicationEventPublisher:用于封装事件发布功能的接口,向事件监听器(Listener)发送事件消息。
- ResourceLoader:Spring 加载资源的顶层接口,用于加载资源文件。ApplicationContext 继承 ResourceLoader 的子类 ResourcePatternResolver,该接口是将 location 解析为 Resource 对象的策略接口。
- MessageSource :解析 message 的策略接口,用于支撑国际化等功能。
- EnvironmentCapable :用于获取 Environment 的接口。
ApplicationContext 的子接口
ApplicationContext 有两个直接子类:WebApplicationContext 和 ConfigurableApplicationContext。
WebApplicationContext
public interface WebApplicationContext extends ApplicationContext {
//
String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";
//请求范围的范围标识符:“ request”。
String SCOPE_REQUEST = "request";
//会话范围的范围标识符:“会话”。
String SCOPE_SESSION = "session";
//SCOPE_APPLICATION
String SCOPE_APPLICATION = "application";
//工厂中ServletContext环境Bean的名称。
String SERVLET_CONTEXT_BEAN_NAME = "servletContext";
//工厂中ServletContext init-params环境Bean的名称。
String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";
//工厂中ServletContext属性环境bean的名称
String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";
@Nullable
ServletContext getServletContext();
}
该接口提供 Web 应用程序配置的界面。在应用程序运行时,它是只读的,但是如果实现支持,则可以重新加载。该接口中的 getServletContext()
方法向通用 ApplicationContext 接口添加了一个方法,并定义了在引导过程中必须绑定根上下文的众所周知的应用程序属性名称。
像通用应用程序上下文一样,Web 应用程序上下文是分层的。每个应用程序只有一个根上下文,而应用程序中的每个 servlet(包括MVC框架中的调度程序servlet)都有自己的子上下文。
除了标准的应用程序上下文生命周期功能外,WebApplicationContext 实现还需要检测ServletContextAware
Bean并相应地调用该setServletContext
方法。
ConfigurableApplicationContext
public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {
//在单个String值中,可以将任意数量的这些字符视为多个上下文配置路径之间的分隔符。
String CONFIG_LOCATION_DELIMITERS = ",; \t\n";
//工厂中ConversionService bean的名称。
String CONVERSION_SERVICE_BEAN_NAME = "conversionService";
//工厂中LoadTimeWeaver Bean的名称。
String LOAD_TIME_WEAVER_BEAN_NAME = "loadTimeWeaver";
//Environment工厂中bean的名称。
String ENVIRONMENT_BEAN_NAME = "environment";
//工厂中系统属性Bean的名称。
String SYSTEM_PROPERTIES_BEAN_NAME = "systemProperties";
//工厂中系统环境bean的名称。
String SYSTEM_ENVIRONMENT_BEAN_NAME = "systemEnvironment";
//Name的关闭钩子线程:“SpringContextShutdownHook”。
String SHUTDOWN_HOOK_THREAD_NAME = "SpringContextShutdownHook";
//设置此应用程序上下文的唯一ID。
void setId(String var1);
//设置此应用程序上下文的父级。
void setParent(@Nullable ApplicationContext var1);
//设置Environment这个应用程序上下文。
void setEnvironment(ConfigurableEnvironment var1);
//Environment以可配置的形式返回此应用程序上下文的,以便进行进一步的自定义。
ConfigurableEnvironment getEnvironment();
//添加一个新的BeanFactoryPostProcessor,它将在刷新之前应用于此应用程序上下文的内部Bean工厂,然后再评估任何Bean定义。
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor var1);
//添加一个新的ApplicationListener,它将在上下文事件(例如上下文刷新和上下文关闭)时收到通知。
void addApplicationListener(ApplicationListener<?> var1);
//在此应用程序上下文中注册给定的协议解析器,从而允许处理其他资源协议。
void addProtocolResolver(ProtocolResolver var1);
//加载或刷新配置的持久表示形式,可能是XML文件,属性文件或关系数据库模式。
void refresh() throws BeansException, IllegalStateException;
//向JVM运行时注册一个shutdown挂钩,除非JVM当时已经关闭,否则在JVM关闭时关闭该上下文。
void registerShutdownHook();
//关闭此应用程序上下文,释放实现可能持有的所有资源和锁。
void close();
//确定此应用程序上下文是否处于活动状态,即,是否至少刷新一次并且尚未关闭。
boolean isActive();
//返回此应用程序上下文的内部bean工厂。
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}
从上面代码可以看到 ConfigurableApplicationContext 接口提供的方法都是对 ApplicationContext 进行配置的,例如 setEnvironment()
、addBeanFactoryPostProcessor
,同时它还继承了如下两个接口:
- Lifecycle:对 context 生命周期的管理,它提供
start()
和stop()
方法启动和暂停组件。 - Closeable:标准 JDK 所提供的一个接口,用于最后关闭组件释放资源等。
ConfigurableWebApplicationContext
WebApplicationContext 接口和 ConfigurableApplicationContext 接口有一个共同的子类接口 ConfigurableWebApplicationContext,该接口将这两个接口进行合并,提供了一个可配置、可管理、可关闭的WebApplicationContext,同时该接口还增加了 setServletContext()
,setServletConfig()
等方法,用于装配WebApplicationContext。
public interface ConfigurableWebApplicationContext extends WebApplicationContext, ConfigurableApplicationContext {
//引用上下文路径和/或Servlet名称的ApplicationContext ID的前缀。
String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":";
//工厂中ServletConfig环境Bean的名称。
String SERVLET_CONFIG_BEAN_NAME = "servletConfig";
//为此Web应用程序上下文设置ServletContext。
void setServletContext(@Nullable ServletContext var1);
//为此Web应用程序上下文设置ServletConfig。
void setServletConfig(@Nullable ServletConfig var1);
//返回此Web应用程序上下文的ServletConfig(如果有)。
@Nullable
ServletConfig getServletConfig();
//设置此Web应用程序上下文的名称空间,以用于构建默认的上下文配置位置。
void setNamespace(@Nullable String var1);
//返回此Web应用程序上下文的名称空间(如果有)。
@Nullable
String getNamespace();
//以init-param样式设置此Web应用程序上下文的配置位置
void setConfigLocation(String var1);
//设置此Web应用程序上下文的配置位置。
void setConfigLocations(String... var1);
//返回此Web应用程序上下文的配置位置,null如果未指定,则返回。
@Nullable
String[] getConfigLocations();
}
ClassPathXmlApplicationContext
ClassPathXmlApplicationContext 是我们在学习 Spring 过程中使用非常多的一个类,一般使用 ApplicationContext 接口,大都是采用该类。
ApplicationContext context = new ClassPathXmlApplicationContext("application_context.xml");
User user = (User) context.getBean("user");
下图是 ClassPathXmlApplicationContext 的结构类图:
主要的类层级关系如下:
这种设计是模板方法模式典型的应用,AbstractApplicationContext 实现了 ConfigurableApplicationContext 这个全家桶接口,其子类 AbstractRefreshableConfigApplicationContext 又实现了 BeanNameAware 和 InitializingBean 接口。所以 ClassPathXmlApplicationContext 设计的顶级接口有:
BeanFactory:Spring 容器 Bean 的管理
MessageSource:管理 message ,实现国际化等功能
ApplicationEventPublisher:事件发布
ResourcePatternResolver:资源加载
EnvironmentCapable:系统 Environment(profile + Properties) 相关
Lifecycle:管理生命周期
Closeable:关闭,释放资源
InitializingBean:自定义初始化
BeanNameAware:设置 beanName 的 Aware 接口
下面就这些接口来一一分析。
MessageSource
MessageSource 定义了获取 message 的策略方法 getMessage()
,在 ApplicationContext 体系中,该方法 AbstractApplicationContext 实现,在 AbstractApplicationContext 中,它持有一个 MessageSource 实例,将 getMessage()
的实现给该实例来实现,如下:
private MessageSource messageSource;
public String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
return this.getMessageSource().getMessage(resolvable, locale);
}
private MessageSource getMessageSource() throws IllegalStateException {
if (this.messageSource == null) {
throw new IllegalStateException("MessageSource not initialized - call 'refresh' before accessing messages via the context: " + this);
} else {
return this.messageSource;
}
}
该方法具体实现在 AbstractMessageSource 中,具体就不做分析了,有兴趣的朋友可以去研究一下。
此外还有一个 initMessageSource()
方法,在 refresh()
中被调用,用来初始化一些国际化的属性。
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = this.getBeanFactory();
if (beanFactory.containsLocalBean("messageSource")) {
this.messageSource = (MessageSource)beanFactory.getBean("messageSource", MessageSource.class);
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource)this.messageSource;
if (hms.getParentMessageSource() == null) {
hms.setParentMessageSource(this.getInternalParentMessageSource());
}
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Using MessageSource [" + this.messageSource + "]");
}
} else {
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(this.getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton("messageSource", this.messageSource);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No 'messageSource' bean, using [" + this.messageSource + "]");
}
}
}
ApplicationEventPublisher
用于封装事件发布功能的接口,向事件监听器(Listener)发送事件消息。
该接口提供了一个 publishEvent()
用于通知在此应用程序中注册的所有的监听器。该方法在 AbstractApplicationContext 中实现。
public void publishEvent(ApplicationEvent event) {
this.publishEvent(event, (ResolvableType)null);
}
public void publishEvent(Object event) {
this.publishEvent(event, (ResolvableType)null);
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
Object applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent)event;
} else {
applicationEvent = new PayloadApplicationEvent(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
}
}
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
} else {
this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
}
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
} else {
this.parent.publishEvent(event);
}
}
}
如果指定的事件不是 ApplicationEvent,则它将包装在 PayloadApplicationEvent 中。如果存在父级 ApplicationContext ,则同样要将 event 发布给父级 ApplicationContext。
ResourcePatternResolver
ResourcePatternResolver 接口继承 ResourceLoader 接口,为将 location 解析为 Resource 对象的策略接口。它提供的 getResources()
在 AbstractApplicationContext 中实现,在 AbstractApplicationContext 中它持有一个 ResourcePatternResolver 的实例对象。 其定义如下:
public Resource[] getResources(String locationPattern) throws IOException {
return this.resourcePatternResolver.getResources(locationPattern);
}
该方法的具体实现在 PathMatchingResourcePatternResolver 类中,在 Spring IoC资源管理之ResourceLoader一节中有讲解。
EnvironmentCapable
提供当前系统环境 Environment 组件。提供了一个 getEnvironment()
用于返回 Environment 实例对象,该方法在 AbstractApplicationContext 实现。
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = this.createEnvironment();
}
return this.environment;
}
如果持有的 environment 实例对象为空,则调用 createEnvironment()
创建一个。
protected ConfigurableEnvironment createEnvironment() {
return new StandardEnvironment();
}
StandardEnvironment 是一个适用于非 WEB 应用的 Environment。
Lifecycle
一个用于管理声明周期的接口。
在 AbstractApplicationContext 中存在一个 LifecycleProcessor 类型的实例对象 lifecycleProcessor,AbstractApplicationContext 中关于 Lifecycle 接口的实现都是委托给 lifecycleProcessor 实现的。如下:
public void start() {
this.getLifecycleProcessor().start();
this.publishEvent((ApplicationEvent)(new ContextStartedEvent(this)));
}
public void stop() {
this.getLifecycleProcessor().stop();
this.publishEvent((ApplicationEvent)(new ContextStoppedEvent(this)));
}
public boolean isRunning() {
return this.lifecycleProcessor != null && this.lifecycleProcessor.isRunning();
}
在启动、停止的时候会分别发布 ContextStartedEvent 和 ContextStoppedEvent 事件。
Closeable
Closeable 接口用于关闭和释放资源,提供了 close()
以释放对象所持有的资源。在 ApplicationContext 体系中由AbstractApplicationContext 实现,用于关闭 ApplicationContext 销毁所有 bean ,此外如果注册有 JVM shutdown hook,同样要将其移除。如下:
public void close() {
synchronized(this.startupShutdownMonitor) {
this.doClose();
if (this.shutdownHook != null) {
try {
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
} catch (IllegalStateException var4) {
}
}
}
}
调用 doClose()
发布 ContextClosedEvent 事件,销毁所有 bean(单例),关闭 BeanFactory 。如下:
protected void doClose() {
if (this.active.get() && this.closed.compareAndSet(false, true)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Closing " + this);
}
LiveBeansView.unregisterApplicationContext(this);
try {
this.publishEvent((ApplicationEvent)(new ContextClosedEvent(this)));
} catch (Throwable var3) {
this.logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", var3);
}
if (this.lifecycleProcessor != null) {
try {
this.lifecycleProcessor.onClose();
} catch (Throwable var2) {
this.logger.warn("Exception thrown from LifecycleProcessor on context close", var2);
}
}
this.destroyBeans();
this.closeBeanFactory();
this.onClose();
if (this.earlyApplicationListeners != null) {
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
this.active.set(false);
}
}
InitializingBean
InitializingBean 为 bean 提供了初始化方法的方式,它提供的 afterPropertiesSet()
用于执行初始化动作。在 ApplicationContext 体系中,该方法由 AbstractRefreshableConfigApplicationContext 实现,如下:
public void afterPropertiesSet() {
if (!this.isActive()) {
this.refresh();
}
}
执行 refresh()
,该方法在 AbstractApplicationContext 中执行,执行整个 Spring 容器的初始化过程。该方法在 Spring IoC之ClassPathXmlApplicationContext 一节中有介绍。
BeanNameAware
设置 bean name 的接口。接口在 AbstractRefreshableConfigApplicationContext 中实现。
public void setBeanName(String name) {
if (!this.setIdCalled) {
super.setId(name);
setDisplayName("ApplicationContext '" + name + "'");
}
}
总结
本文主要是以 Spring Framework 的 ApplicationContext 为中心,对其结构和功能的实现进行了简要的说明。 Spring 是一个非常优秀的框架,具有良好的结构设计和接口抽象,它的每一个接口职能单一,且都是具体功能到各个模块的高度抽象,且几乎每套接口都提供了一个默认的实现(defaultXXX)。对于 ApplicationContext 体系而言,它继承 Spring 中众多的核心接口,能够为客户端提供一个相对完整的 Spring 容器,接口 ConfigurableApplicationContext 对 ApplicationContext 接口再次进行扩展,提供了生命周期的管理功能。抽象类 AbstractApplicationContext 对整套接口提供了大部分的默认实现,将其中“不易变动”的部分进行了封装,通过“组合”的方式将“容易变动”的功能委托给其他类来实现,同时利用模板方法模式将一些方法的实现开放出去由子类实现,从而实现“对扩展开放,对修改封闭”的设计原则。关于 ApplicationContext 的讲解我们主要以 ClassPathXmlApplicationContext 为例,感兴趣的朋友可以前往 Spring IoC之ClassPathXmlApplicationContext 。
参考文献
http://cmsblogs.com/?p=4036