一,Spring启动流程概述
Spring的IoC容器在实现控制反转和依赖注入的过程中,可以划分为两个阶段:
-
容器启动阶段
-
Bean实例化阶段
容器初始化
-
加载配置
-
分析配置信息
-
将Bean信息装配到BeanDefinition
-
将Bean信息注册到相应的BeanDefinitionRegistry
-
其他后续处理
容器实例化
-
根据策略实例化对象
-
装配依赖
-
Bean初始化前处理
-
对象初始化
-
对象其他处理
-
注册回调接口
二,Spring启动流程详解
初始化消息源
Spring中定义一个MessageSource接口,以用于支持信息的国际化和包含参数的信息的替换。ApplicationContext接口扩展了MessageSource接口,因而提供了消息处理的功能(i18n或者国际化)。与HierarchicalMessageSource一起使用,还能够处理嵌套的消息,这些是Spring提供的处理消息的基本接口。
public interface MessageSource {
// 用来从MessageSource获取消息的基本方法。如果在指定的locale中没有找到消息,则使用默认的消息
// args中的参数将使用标准类库中的MessageFormat来替换消息中的值
String getMessage(String code, Object[] args, String default, Locale loc):
// 与上一个方法相同,不同之处在于:没有指定默认值。如果没找到消息,会抛出一个NoSuchMessageException异常。
String getMessage(String code, Object[] args, Locale loc) throws NoSuchMessageException;
// 与上面的方法不同之处在于:将code、args等属性封装到MessageSourceResolvable中
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
}
MessageSource类图
Spring提供了两个MessageSource实现:ResourceBundleMessageSource和StaticMessageSource。它们都继承HierarchicalMessageSource能够处理嵌套的消息。
ResourceBundleMessageSource会用得更多一些。StaticMessageSource很少被使用,但能以编程的方式向源添加消息。
类或接口 | 描述 |
HierarchicalMessageSource | MessageSource子接口,提供解析层级消息的能力。该接口的目的是设置父MessageSource用于当前对象无法解析消息时,由父MessageSource尝试解析。 |
DelegatingMessageSource | 空的MessageSource,它将所有处理转发给父MessageSource。如果没有父MessageSource,它不会处理任何消息。用户未指定MessageSource时,Spring默认使用该类。该类的功能比较简单:将字符串和参数数组格式化为一个消息字符串 |
AbstractMessageSource | 支持“配置文件”方式的抽象类,内部提供一个与区域设置无关的公共消息配置文件,消息代码为关键字 |
AbstractResourceBasedMessageSource | 基于资源并实现了MessageSource接口的抽象类,同时提供了一些参数配置的方法 |
ResourceBundleMessageSource | 该类通过beanName指定一个资源名(包括类路径的全限定资源名),或通过beanNames指定一组资源名。不同的区域获取加载资源文件,达到国际化信息的目的。该类依赖JDK的ResourceBundle,并结合了JDK MessageFormat提供的标准消息解析。被访问的ResourceBundle和为每个消息生成的MessageFormat都会被缓存。 |
ReloadableResourceBundleMessageSource | 同ResourceBundleMessageSource。该类参与Spring ApplicationContex的资源加载。它使用java.util.Properties作为其消息的自定义数据结构,通过PropertiesPersister策略从Spring Resource中加载它们。该策略不仅能够根据时间戳的更改重新加载文件,而且还能够使用特定的字符编码加载属性文件。 |
StaticMessageSource | MessageSource的简单实现,允许以编程方式注册消息,提供基本的国际化支持(通常用于测试) |
使用示例
Xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>locale.messages</value>
<value>locale.exceptions</value>
</list>
</property>
</bean>
</beans>
messageSource配置
# exceptions_en_US.properties文件
exceptions.illegal = The user {0} attempted to login, time: {1}
# exceptions_zh_CN.properties文件
exceptions.illegal = \u975e\u6cd5\u7528\u6237{0}\u5c1d\u8bd5\u767b\u5f55, \u65f6\u95f4\uff1a{1}
# message_en_US.properties文件
id.ilegal = userid {0} is illegal! time: {1}
password.empty = username{0}'s password is empty!
# message_zh_CN.properties文件
id.ilegal = \u7528\u6237\u7f16\u53f7{0}\u975e\u6cd5\u767b\u5f55\uff01{1}
password.empty = \u7528\u6237\u540d{0}\u7684\u5bc6\u7801\u4e0d\u80fd\u4e3a\u7a7a\uff01
注意:中文messageSource文件需要把中文转为Ascii
源码解读
getMessage()时序图
该段源码主要目的
当一个ApplicationContext被加载时,它会自动在context中查找已定义为MessageSource类型的Bean。此Bean的名称须为messageSource。
如果找到,那么所有对上述方法的调用将被委托给该Bean。否则ApplicationContext会在其父类中查找是否含有同名的Bean。如果有,就把它作为MessageSource。如果最终没有找到任何的消息源,实例化一个空的DelegatingMessageSource,使它能够接受上述方法的调用。
protected void initMessageSource() {
// 获取BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// beanFactory中是否包含名字为messageSource的Bean
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
// 创建MessageSource类型的Bean
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
// 判断messageSource是否有parent MessageSource
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
// 设置MessageSource的parent MessageSource.
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
if (hms.getParentMessageSource() == null) {
// 如果未注册任何父MessageSource,则仅将父上下文的MessageSource设置为父MessageSource
hms.setParentMessageSource(getInternalParentMessageSource());
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using MessageSource [" + this.messageSource + "]");
}
}
// beanFactory中不包含名字为messageSource的Bean
else {
// 新建DelegatingMessageSource对象
DelegatingMessageSource dms = new DelegatingMessageSource();
// 设置DelegatingMessageSource的parent MessageSource
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
// 向BeanFactory中注册MessageSource
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isDebugEnabled()) {
logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME +
"': using default [" + this.messageSource + "]");
}
}
}