Spring的国际化消息
spring的国际化接口是MessageSource,他有很多实现了,这里以ResourceBundleMessageSource来说明它的原理。
总体流程:
总体流程的代码:
MessageFormat messageFormat = resolveCode(code, locale);
if (messageFormat != null) {
synchronized (messageFormat) {
return messageFormat.format(argsToUse);
}
}
测试代码
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);
}
}
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>i18n/messages</value>
</list>
</property>
</bean>
// messages.properties文件内容
test=test
// mmessages_en_US.properties文件内容
test=test
// messages_zh_CN.properties文件内容
test=测试
原理分析
1、程序入口
ClassPathXmlApplicationContext容器加载的时候会去初始化国际化消息,这里会把我们配置好的ResourceBundleMessageSource对象初始化好,之后就是调用这个对象的方法完成功能。
// Initialize message source for this context.
// 7、为应用初始化message源,为了"国际化"。国际化的入口
initMessageSource();
随着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);
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;
}
4、应用javase的MessageFormat的format
直接看代码。
if (messageFormat != null) {
synchronized (messageFormat) {
// javase完成实际变量替换占位符参数
return messageFormat.format(argsToUse);
}
}
传送门:保姆式Spring5源码解析
欢迎与作者一起交流技术和工作生活