• Spring的国际化消息


    Spring的国际化消息

    spring的国际化接口是MessageSource,他有很多实现了,这里以ResourceBundleMessageSource来说明它的原理。

    总体流程:

    总体流程的代码:

    MessageFormat messageFormat = resolveCode(code, locale);
    if (messageFormat != null) {
        synchronized (messageFormat) {
            return messageFormat.format(argsToUse);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    测试代码

    public class Main {
    
        public static void main(String[] args) {
            ClassPathXmlApplicationContext app =
                    new ClassPathXmlApplicationContext("com/firefish/springsourcecodedeepanalysis/i18n/application.xml");
            Object[] param = {"John", new GregorianCalendar().getTime()};
    
            String test1 = app.getMessage("test", param, Locale.US);
            String test2 = app.getMessage("test", param, Locale.CHINA);
            System.out.println(test1);
            System.out.println(test2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
        <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
            <property name="basenames">
                <list>
                    <value>i18n/messagesvalue>
                list>
            property>
        bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    // messages.properties文件内容
    test=test
    
    // mmessages_en_US.properties文件内容
    test=test
    
    // messages_zh_CN.properties文件内容
    test=测试
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    原理分析
    1、程序入口

    ClassPathXmlApplicationContext容器加载的时候会去初始化国际化消息,这里会把我们配置好的ResourceBundleMessageSource对象初始化好,之后就是调用这个对象的方法完成功能。

    // Initialize message source for this context.
    // 7、为应用初始化message源,为了"国际化"。国际化的入口
    initMessageSource();
    
    • 1
    • 2
    • 3

    随着app.getMessage("test", param, Locale.US);代码的执行,会依次执行

    2、获取ResourceBundle

    结果漫长的方法调用,我们到了loadBundle,newBundle方法,这个方法有一段代码,功能是拼接处文件的名称,用类加载器完成文件的加载,随后把结果缓存起来。

    // 根据baseName和locale获取bundleName的名称。如messages_zh_CN
    String bundleName = toBundleName(baseName, locale);
    // 再把properties后缀拼接上,得到完成名称
    final String resourceName = toResourceName(bundleName, "properties");
    // 用类加载器加载了文件
    URL url = classLoader.getResource(resourceName);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    3、转换为javase的MessageFormat

    直接看代码。

        protected MessageFormat resolveCode(String code, Locale locale) {
            Set<String> basenames = getBasenameSet();
            for (String basename : basenames) {
                ResourceBundle bundle = getResourceBundle(basename, locale);
                if (bundle != null) {
                    // 获取到code在特定locale下的值
                    MessageFormat messageFormat = getMessageFormat(bundle, code, locale);
                    if (messageFormat != null) {
                        return messageFormat;
                    }
                }
            }
            return null;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    4、应用javase的MessageFormat的format

    直接看代码。

    if (messageFormat != null) {
        synchronized (messageFormat) {
            // javase完成实际变量替换占位符参数
            return messageFormat.format(argsToUse);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    传送门:保姆式Spring5源码解析

    欢迎与作者一起交流技术和工作生活

    联系作者

  • 相关阅读:
    Eureka注册中心
    超分辨率技术在实时音视频领域的研究与实践
    3716. 命名法 北京师范大学考研机试题 模拟思想
    模板学堂丨禅道业务数据分析大屏
    22/11/24
    【IOS】启动报错Cannot launch ‘/private/var/containers/Bundle/Application/....‘
    JDK1.8 JVM内存模型
    用神经网络验算1+1是否等于0+2
    SeataAT模式如何达到读已提交的隔离级别
    Python学习常见问题及其解决方案(1)
  • 原文地址:https://blog.csdn.net/yuchangyuan5237/article/details/126804852