Spring启动流程解析-7-初始化消息源

一,Spring启动流程概述

 

Spring的IoC容器在实现控制反转和依赖注入的过程中,可以划分为两个阶段:

  • 容器启动阶段

  • Bean实例化阶段

容器初始化

  1. 加载配置

  2. 分析配置信息

  3. 将Bean信息装配到BeanDefinition

  4. 将Bean信息注册到相应的BeanDefinitionRegistry

  5. 其他后续处理

容器实例化

  1. 根据策略实例化对象

  2. 装配依赖

  3. Bean初始化前处理

  4. 对象初始化

  5. 对象其他处理

  6. 注册回调接口

二,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 + "]");
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值