Spring之国际化信息MessageSource源码阅读

本文详细探讨了Spring框架中的MessageSource组件,从MessageSource接口到各个实现类如HierarchicalMessageSource、AbstractMessageSource、ResourceBundleMessageSource和ReloadableResourceBundleMessageSource的源码分析,深入理解其在国际化信息处理中的作用。同时,提到了MessageFormat组件在消息格式化中的功能,用于将消息和参数转化为字符串。
摘要由CSDN通过智能技术生成

MessageSource架构图

这里写图片描述
1. MessageSource:抽象化的消息接口
2. HierarchicalMessageSource:分层的消息源接口,可获取父消息源
3. MessageSourceSupport:帮助消息源解析的抽象类,通过指定“消息格式化组件MessageFormat”格式化消息。
4. DelegatingMessageSource: 消息源解析委派类(用户未指定时,SpringContext默认使用当前类),功能比较简单:将字符串和参数数组格式化为一个消息字符串
5. AbstractMessageSource:支持‘配置文件’的方式国际化资源 的 抽象类,内部提供一个与区域设置无关的公共消息配置文件,消息代码为关键字。
6. StaticMessageSource:主要用于程序测试,它允许通过编程的方式提供国际化信息
7. ResourceBundleMessageSource:该实现类允许用户通过beanName指定一个资源名(包括类路径的全限定资源名),或通过beanNames指定一组资源名。不同的区域获取加载资源文件,达到国际化信息的目的。
8. ReloadableResourceBundleMessageSource:同ResourceBundleMessageSource区别(spring3.2):
1)加载资源类型及方式:
ResourceBundleMessageSource 依托JDK自带ResourceBundle加载资源,支持绝对路径和工程路径,支持文件为.class文件和.properties。
ReloadResourceBundleMessageSource依托spring的ResourceLoader加载Resource资源,功能更加强大,同时支持.properties和.xml文件。
2)缓存时间:
ResourceBundleMessageSource主要利用ResourceBundle.Control 实现简单的自动重载。
ReloadResourceBundleMessageSource每次加载资源都会记录每个资源的加载时间点,在缓存资源过期后会再次比较文件的修改时间,如果不变则不需要重新加载,同时刷新本次加载时间点。
3)编码方式:
ResourceBundleMessageSource可以统一指定默认的文件编码方式
ReloadResourceBundleMessageSource不仅可以指定统一的默认编码方式,也同时支持为每个文件单独制定编码方式

spring中初始化MessageSource组件

//springContext国际化资源信息初始化。这里的messageSource主要是将这个Bean定义的信息资源加载为容器级的国际化信息资源。
public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext, DisposableBean {
   

    public static final String MESSAGE_SOURCE_BEAN_NAME = "messageSource";

    /** 实际委托的消息源解析对象 */
    private MessageSource messageSource;

    //======省略一段代码========/
    public void refresh() throws BeansException, IllegalStateException {

        //======省略一段代码========/

        // 初始化此上下文的消息源。
        initMessageSource();

        // Initialize event multicaster for this context.
        initApplicationEventMulticaster();

        //======省略一段代码========/
    }

    //======省略一段代码========/


    /**
     * 初始化此上下文的消息源。
     * 如果没有在此上下文中定义,请使用父级。
     */
    protected void initMessageSource() {
        //获取工厂
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        //先找工厂中messageSource是否有对应的实例(注册过)
        if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
            this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
            //messageSource是否有父消息源
            if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
                HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
                if (hms.getParentMessageSource() == null) {
                    // 父MessageSource未注册,则设置messageSource的parentMessageSource为“父上下文的‘MessageSource’ ”。
                    hms.setParentMessageSource(getInternalParentMessageSource());
                }
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Using MessageSource [" + this.messageSource + "]");
            }
        }
        else {
  //没有对应的实例,则自己初始化一个
            // 实例化一个spring默认实现消息源对象
            DelegatingMessageSource dms = new DelegatingMessageSource();
            //设置父消息源
            dms.setParentMessageSource(getInternalParentMessageSource());
            this.messageSource = dms;
            //‘单例模式’注册到工厂中
            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 + "]");
            }
        }
    }
    //获取父上下文的消息源
    protected MessageSource getInternalParentMessageSource() {
        return (getParent() instanceof AbstractApplicationContext) ?
            ((AbstractApplicationContext) getParent()).messageSource : getParent();
    }
}

各个类源码阅读

MessageSource接口

public interface MessageSource {
   

    /**
     * 尝试解决消息。 如果没有找到消息,返回默认消息。
     */
    String getMessage(String code, Object[] args, String defaultMessage, Locale locale);

    /**
     * 尝试解决消息。 如果无法找到消息,则视为错误。
     */
    String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;

    /**
     * 尝试使用传入的{@code MessageSourceResolvable}参数中包含的所有属性来解析消息。
     * <p>NOTE: 我们必须在此方法上抛出{@code NoSuchMessageException},因为在调用此方法时,我们无法确定可解析的{@code defaultMessage}属性是否为空。
     */
    String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;

}

MessageSourceResolvable解析消息要素的包装接口和类

//解析消息要素的包装类:code、Arguments、默认消息
public interface MessageSourceResolvable {
   

    /**
     * 返回用于解决此消息的代码,按照他们应该尝试的顺序。 因此,最后一个代码将是默认代码。
     * @return a String array of codes which are associated with this message
     */
    String[] getCodes();

    /**
     * 返回要用于解析此消息的参数数组。
     * @return an array of objects to be used as parameters to replace
     * placeholders within the message text
     * @see java.text.MessageFormat
     */
    Object[] getArguments();

    /**
     * 返回要用于解析此消息的默认消息。
     * @return the default message, or {@code null} if no default
     */
    String getDefaultMessage();

}

//spring默认实现的 解析消息要素的包装类
public class DefaultMessageSourceResolvable implements MessageSourceResolvable, Serializable {
   

    private final String[] codes;

    private final Object[] arguments;

    private final String defaultMessage;


    //构造方法
    public DefaultMessageSourceResolvable(String code) {
        this(new String[] {code}, null, null);
    }
    //构造方法
    public DefaultMessageSourceResolvable(String[] codes) {
        this(codes, null, null);
    }

    //构造方法
    public DefaultMessageSourceResolvable(String[] codes, String defaultMessage) {
        this(codes, null, defaultMessage);
    }
    //构造方法
    public DefaultMessageSourceResolvable(String[] codes, Object[] arguments) {
        this(codes, arguments, null);
    }

    //构造方法
    public DefaultMessageSourceResolvable(String[] codes, Object[] arguments, String defaultMessage) {
        this.codes = codes;
        this.arguments = arguments;
        this.defaultMessage = defaultMessage;
    }
    //构造方法
    public DefaultMessageSourceResolvable(MessageSourceResolvable resolvable) {
        this(resolvable.getCodes(), resolvable.getArguments(), resolvable.getDefaultMessage());
    }


    public String[] getCodes() {
        return this.codes;
    }

    /**
     * 返回此可解析的默认代码,即代码数组中的最后一个代码。
     */
    public String getCode() {
        return (this.codes != null && this.codes.length > 0) ? this.codes[this.codes.length - 1] : null;
    }

    public Object[] getArguments() {
        return this.arguments;
    }

    public String getDefaultMessage() {
        return this.defaultMessage;
    }


    /**
     * 为此MessageSourceResolvable构建默认的String表示形式:包括代码,参数和默认消息。
     */
    protected final String resolvableToString() {
        StringBuilder result = new StringBuilder();
        result.append("codes [").append(StringUtils.arrayToDelimitedString(this.codes, ","));
        result.append("]; arguments [" + StringUtils.arrayToDelimitedString(this.arguments, ","));
        result.append("]; default message [").append(this.defaultMessage).append(']');
        return result.toString();
    }

    /**默认实现公开了此MessageSourceResolvable的属性。要在更具体的子类中被覆盖,可能通过{@code resolvableToString()}包含可解析的内容。
     * @see #resolvableToString()
     */
    @Override
    public String toString() {
        return getClass().getName() + ": " + resolvableToString();
    }


    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof MessageSourceResolvable)) {
            return false;
        }
        MessageSourceResolvable otherResolvable = (MessageSourceResolvable) other;
        return ObjectUtils.nullSafeEquals(getCodes(), otherResolvable.getCodes()) &&
                ObjectUtils.nullSafeEquals(getArguments(), otherResolvable.getArguments()) &&
                ObjectUtils.nullSafeEquals(getDefaultMessage(), otherResolvable.getDefaultMessage());
    }

    @Override
    public int hashCode() {
        int hashCode = ObjectUtils.nullSafeHashCode(getCodes());
        hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(getArguments());
        hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(getDefaultMessage());
        return hashCode;
    }

}

HierarchicalMessageSource消息源分层接口

public interface HierarchicalMessageSource extends MessageSource {
   

    /**
     * 设置将用于尝试解决此对象无法解析的消息的父级。
     * @param parent 将用于解析此对象无法解析的邮件的父MessageSource。 可能是{@code null},在这种情况下不需要进一步的解决。
     */
    void setParentMessageSource(MessageSource parent);

    /**
     * 返回此MessageSource的父级,否则返回{@code null}。
     */
    MessageSource getParentMessageSource();

}

MessageSourceSupport

//用于支撑消息源解析的抽象类
public abstract class MessageSourceSupport {
   
    //默认’消息格式组件‘
    private static final MessageFormat INVALID_MESSAGE_FORMAT = new MessageFormat("");

    /** Logger available to subclasses */
    protected final Log logger = LogFactory.getLog(getClass());
    //是否始终应用’消息格式组件‘,解析没有参数的消息。
    private boolean alwaysUseMessageFormat = false;


    //缓存来保存已解决消息msg的’消息格式组件‘。 用于传入的默认消息(如:AbstractMessageSource中的commonMessages中的消息)。 
    private final Map<String, Map<Locale, MessageFormat>> messageFormatsPerMessage =
            new HashMap<String, Map<Locale, MessageFormat>>();


    /**
     * 设置是否始终应用’消息格式组件‘,解析没有参数的消息。
     * <p>例如:,MessageFormat希望单引号被转义为"''"。 如果您的消息文本全部用这样的转义编写,
     * 即使没有定义参数占位符,您需要将此标志设置为“true”。 
     * 否则,只有具有实际参数的消息文本应该用MessageFormat转义来编写。
     * @see java.text.MessageFormat
     */
    public void setAlwaysUseMessageFormat(boolean alwaysUseMessageFormat) {
        this.alwaysUseMessageFormat = alwaysUseMessageFormat;
    }

    /**
     * 返回是否始终应用’消息格式组件‘,解析没有参数的消息。
     */
    protected boolean isAlwaysUseMessageFormat() {
        return this.alwaysUseMessageFormat;
    }


    //渲染给定的默认消息字符串。 
    protected String renderDefaultMessage(String defaultMessage, Object[] args, Locale locale) {
        return formatMessage(defaultMessage, args, locale);
    }

    //渲染给定的消息字符串。 
    protected String formatMessage(String msg, Object[] args, Locale locale) {
        if (msg == null || (!this.alwaysUseMessageFormat && ObjectUtils.isEmpty(args))) {
            return msg;
        }
        MessageFormat messageFormat = null;
        synchronized (this.messageFormatsPerMessage) { //防止并发操作
            //先尝试取缓存’消息格式组件‘
            Map<Locale, MessageFormat> messageFormatsPerLocale = this.messageFormatsPerMessage.get(msg);
            if (messageFormatsPerLocale != null) {
                messageFormat = messageFormatsPerLocale.get(locale);
            }
            else {
  //缓存为空,设置初始化空map值
                messageFormatsPerLocale = new HashMap<Locale, MessageFormat>();
                this.messageFormatsPerMessage.put(msg, messa
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cloud 提供了一种简单且灵活的方式来实现国际化(Internationalization,简称i18n)支持。在 Spring Cloud 中,你可以使用 Spring Boot 的国际化特性来实现应用程序的多语言支持。 首先,你需要准备好不同语言的资源文件,通常是以 `.properties` 或 `.yml` 格式存储的键值对。每个资源文件对应一个语言,包含了应用程序中需要翻译的文本内容。 接下来,在 Spring Boot 的配置文件(`application.properties` 或 `application.yml`)中,你需要设置默认的语言和资源文件的位置。例如,你可以使用以下配置设置英文为默认语言,并将资源文件存放在 `classpath:messages` 目录下: ```yaml spring.messages.basename=messages/messages spring.messages.default-locale=en ``` 然后,在你的代码中,你可以使用 `MessageSource` 类来获取对应语言的文本内容。`MessageSource` 是 Spring Framework 提供的一个国际化消息源,它会根据当前的语言环境(由 `LocaleResolver` 指定)来选择合适的资源文件。 你可以通过在组件中注入 `MessageSource` 类并调用 `getMessage()` 方法来获取文本。例如,在一个控制器中,你可以这样使用: ```java @RestController public class MyController { @Autowired private MessageSource messageSource; @GetMapping("/hello") public String hello(Locale locale) { return messageSource.getMessage("hello.message", null, locale); } } ``` 在上面的例子中,`hello.message` 是一个资源文件中定义的键,`getMessage()` 方法会根据当前的语言环境来选择合适的文本进行返回。 通过这种方式,你可以在 Spring Cloud 中实现多语言支持,使你的应用程序能够根据用户的语言偏好来展示不同的界面和文本内容。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值