Java —— MessageFormat類 處理國際化
一、MessageFormat 概覽
java.text包中的 Fomart 介面是所有處理格式的基礎介面,有三個子類:DateFormat、MessageFormat、NumberFormat。
MessageFormat 是專門處理文字格式的類,且沒有子類。
二、MessageFormat 細節
1、建構函式:
MessageFormat(String pattern); //pattern為字串模式;使用預設的Locale.Category常量對應的語言格式
MessageFormat(String pattern,Locale locale); //使用指定的locale對應的語言
2、靜態方法:
MessageFormat.format(String pattern,Ojbect... arguments); //建立一次性使用的格式字串
3、例項方法:
主要涉及三個功能:設定或返回pattern、設定或返回locale、設定或返回Format例項。
//設定與返回當前的pattern public void applyPattern(String pattern); public String toPattern(); //設定與返回當前的locale public void setLocale(Locale locale); public Locale getLocale(); //一次設定單個格式 public void setFormat(int formatElementIndex, Format newFormat); public void setFormatByArgumentIndex(int argumentIndex, Format newFormat); //一次設定多個格式 public void setFormats(Format[] newFormats); public void setFormatsByArgumentIndex(Format[] newFormats); //獲取設定的格式 public Format[] getFormats(); public Format[] getFormatsByArgumentIndex();//不推薦。如果一個ArgumantsInex沒有用於任何格式元素,則返回null。 //format。後兩個引數是可選的 public String format(Object[] arguments[,StringBuffer result,FieldPosition position]);
三、基礎實踐
1、使用構造方法指定pattern與locale
public MessageFormat(String pattern,Locale local);
模式引數 pattern 的語法:
語法即:真個表示式為一個字串,需要替換的引數資訊放在花括號{}中。
"{引數索引} 字串 \"{引數索引,格式型別,格式風格} 字串\""
FormatType 與 FormatStyle 說明:
指定date與time要求傳入的arguments 中對應物件為 Date型別,number 為數字型別。
Locale 類:管理語言,用其常量可實現國際化。
實踐:
Calendar calendar= Calendar.getInstance(); calendar.setTime(new Date());//calendar 只是管理時間,所以需要傳入時間 String date=String.valueOf(calendar.get(Calendar.YEAR))+"." +String.valueOf(calendar.get(Calendar.MONTH)+1)+"." +String.valueOf(calendar.get(Calendar.DATE)); Object[] objects={"貴陽",date,"晴朗"}; //只指定應用物件:objects MessageFormat mf= new MessageFormat("當前時間:{1},地點:{0},天氣:{2}");//索引對應於objects元素索引 String result=mf.format(objects); System.out.println(result);
注意:引數對準!上面只是將字串填入對應引數位置,而沒有用到FormatType、FormatStyle。獲取美國時間:
Object[] objects={new Date(),"美國","晴朗"};//指定date或time,傳入Date 例項
//只指定應用物件:objects
MessageFormat mf= new MessageFormat("當前時間:{0,time},地點:{1},天氣:{2}",Locale.US);
String result=mf.format(objects);
System.out.println(result);
輸出為:當前時間:4:00:03 PM,地點:美國,天氣:晴朗
四、Spring 中國際化實現
Spring 中國際化是通過實現MessageSource 根介面實現的,繼承關係如下:
說明:圖中除了標記的介面下面的類中沒實現MessageSource 介面中的方法外,其它類都實現或通過繼承實現了。
com.springframemork.context.MessageSource 介面中定義的方法:
String getMessage(String code, Object[] args, String defaultMessage, Locale locale);//code即要國際化的資訊,args為物件引數陣列
String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;
String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;
該介面的具體實現細節是在com.springframework.context.support 包中的 MessageSourceSupport 抽象類中實現的。關鍵程式碼如下:
package org.springframework.context.support;
...
import org.springframework.util.ObjectUtils;
public abstract class MessageSourceSupport {
private static final MessageFormat INVALID_MESSAGE_FORMAT = new MessageFormat("");
protected final Log logger = LogFactory.getLog(getClass());
private boolean alwaysUseMessageFormat = false;
private final Map<String, Map<Locale, MessageFormat>> messageFormatsPerMessage =
new HashMap<String, Map<Locale, MessageFormat>>(); //標記1:定義儲存國際化文字資訊的結構
...
protected String formatMessage(String msg, Object[] args, Locale locale) { //國際化真正實現函式,被getMessage方法呼叫
if (msg == null || (!this.alwaysUseMessageFormat && ObjectUtils.isEmpty(args))) {
return msg;
}
MessageFormat messageFormat = null;
synchronized (this.messageFormatsPerMessage) { //標記2:MessageFormat 類不是同步的,如果多個執行緒
//訪問同一個Format,要求必須同步。
Map<Locale, MessageFormat> messageFormatsPerLocale = this.messageFormatsPerMessage.get(msg);
if (messageFormatsPerLocale != null) { //標記3:當前msg的國際化資訊Set集合還未初始化
messageFormat = messageFormatsPerLocale.get(locale);
}
else { //標記4:國際化資訊Set集合中裝入local為鍵,Message //-Format為值的Map 集合。
messageFormatsPerLocale = new HashMap<Locale, MessageFormat>();
this.messageFormatsPerMessage.put(msg, messageFormatsPerLocale);
}
if (messageFormat == null) {
try {
messageFormat = createMessageFormat(msg, locale); //標記5:上面建立結構,此處建立MessageFormat例項
}
catch (IllegalArgumentException ex) {
// invalid message format - probably not intended for formatting,
// rather using a message structure with no arguments involved
if (this.alwaysUseMessageFormat) {
throw ex;
}
// silently proceed with raw message if format not enforced
messageFormat = INVALID_MESSAGE_FORMAT;
}
messageFormatsPerLocale.put(locale, messageFormat);
}
}
if (messageFormat == INVALID_MESSAGE_FORMAT) {
return msg;
}
synchronized (messageFormat) {
return messageFormat.format(resolveArguments(args, locale)); //標記7:呼叫locale對應的MessageFormat 的format
}
}
protected MessageFormat createMessageFormat(String msg, Locale locale) { //標記6:返回locale對應的MessageFormat
return new MessageFormat((msg != null ? msg : ""), locale);
}
protected Object[] resolveArguments(Object[] args, Locale locale) {
return args;
}
}
簡單實踐:
第一步:
配置資源操作類(org.springframework.context.support.ResourceBundleMessageSource或ReloadableResourceBundleMessageSource)bean,指定properties屬性配置原始檔(即namebases屬性)
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>com/milan/mymain/myProperties</value>
</list>
</property>
<property name="useCodeAsDefaultMessage" value="false"/>
<property name="defaultEncoding" value="UTF-8"/>
<property name="cacheSeconds" value="60"/>
</bean>
第二步:
編輯myProperties 中程式要訪問的屬性,其實是MessageFormat 類的引數pattern ,以key=value形式存放:
HelloWorld=Time:{0,date} Hello {1}
第三步:
主函式中使用並執行:
ApplicationContext appContext=new ClassPathXmlApplicationContext("com/milan/mymain/applicationContext.xml");
Object[] object={new Date(),"小明"};
String msg=appContext.getMessage("HelloWorld", object, Locale.US);//HellowWorld 即屬性檔案中pattern 對應的key。以美國方式顯示日期
System.out.println(msg);
結果:
五、Spring 中國際化報錯
1、報錯日誌:
org.springframework.context.NoSuchMessageException: No message found under ...
簡單歸納了出現情況:
第一種:
配置 ResourceBundleMessageSource 類的bean 時id 沒有設成"messageSource"。可通過更前面的日誌記錄可知,沒有找到id為messageSource的bean時,會使用預設的ReourceBundleMessageSource 物件,我們沒有對他進行配置,當然找不到properties 檔案。(可以開啟ResoureBundleMessageSource類 原始碼,會發現bean工廠會使用預設的值為messageSource的字串來查詢該類的bean)
第二種:
.properties 檔案路徑配置出錯。查詢properties檔案預設路徑為src 資料夾下,所以如果檔案放在包中,要加上包路徑(如上面實踐)。此外,有的說要在前面加"classpath:",但我加了反而提示錯誤,估計與spring版本有關。
第三種:
.properties 檔案中確實沒有定義所要查詢的模式字串pattern 對應的key。
2、學習如何定位錯誤位置:
簡單的錯誤資訊,如缺少包等可直接解決,如果不知錯誤原因,可仔細檢視上面的日誌記錄,找到與錯誤相關的日誌,檢視來源,開啟相關類的原始碼分析。
如上面因為我的bean的id沒設成messageSource 而報的錯誤,可在日誌記錄找到一條:
[DEBUG] 2017-04-16 19:50:30,005
method:org.springframework.context.support.AbstractApplicationContext.initMessageSource(AbstractApplicationContext.java:807)
Unable to locate MessageSource with name 'messageSource': using default [[email protected]decc]
分析日誌:
提示無法定位messageSource 的bean,所以使用了預設的同樣用於國際化的DelegateingMessageSource 類的例項,且能檢視到日誌來源。開啟來源函式,如下:
protected void initMessageSource() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) { //MESSAGE_SOURCE_BEAN_NAME為方法所屬類定義的常量,為messageSource
//標記2:以MessageSource類形式獲取id為messageSource的bean
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
// Make MessageSource aware of parent MessageSource.
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
if (hms.getParentMessageSource() == null) {
// Only set parent context as parent MessageSource if no parent MessageSource
// registered already.
hms.setParentMessageSource(getInternalParentMessageSource());
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using MessageSource [" + this.messageSource + "]");
}
}
else {
// Use empty MessageSource to be able to accept getMessage calls.
DelegatingMessageSource dms = new DelegatingMessageSource();
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isDebugEnabled()) { //標記1:這就是列印日誌的位置
logger.debug("Unable to locate MessageSource with name '" + MESSAGE_SOURCE_BEAN_NAME +
"': using default [" + this.messageSource + "]");
}
}
}
知道了錯誤原因,ok,改ResourceBundleMessageSource bean的id 為messageSource,之後執行正確!
六、Sping 中實現國際化的各類 介面或類的關係梳理
學習框架,檢視原始碼很有必要,能加深對框架各部分協作關係的理解。自己開始學習spring 時,看到的全是介面,竟然找不到介面中的方法是在哪實現的,如何呼叫的!
那麼Spring 中實現國際化主要涉及到哪些介面或類,它們又是怎麼協作的呢?
1、涉及介面或類:(spring-context-***.jar 中)
第一類:
是 com.springframework.context 包中的MessageSource 介面及其子介面或類:MessageSource 中定義了三個getMessage 方法
說明:圖中可見,DelegatingMessageSource 實現了MessageSource 介面,找不到我們定義的id應該取名為messageSource 的ResourceBundleMessageSource的bean 時,預設使用的便是該類的例項。
ApplicationContext 例項可以直接呼叫 getMessage 方法獲得國際化後的字串,但我們可能會更關注getMessage 方法的實現細節。通過eclipse 可發現,getMessage 是在圖所示的AbstractMessageSource 抽象類中實現的,但它實際上卻是依賴了繼承於MessageSourceSupport 類的方法。AbstractMessageSource 宣告頭:
public abstract class AbstractMessageSource extends MessageSourceSupport implements HierarchicalMessageSource
第二類:
同一jar包的 com.springframework.context.support包中的 MessageSourceSupport 類及其子類:定義了getMessage 方法所依賴的國際化真正實現方法
說明:MssageSourceSupport 關鍵程式碼已經在上面 四、Spring 中國際化實現 部分說明了。
2、各部分協作關係
原來畫一張清晰的圖也好難,隨便畫畫:
說明:getMessage 方法的真正實現是在右邊第一、三個框中,所以作為AbstractMessageSource 的子類的 ResourceBundleMessageSource 類也具有了國際化功能。左邊的ApplicationContext 的子類就是通過操作 具有國際化功能的這些類而實現國際化的。
那麼有個問題,為什麼ApplicationContext 還要實現MessageSource 介面呢,它根本沒實現真正實現MessageSource 中的getMessage 方法啊。原因是我們並不是直接操作ApplicationContext 的例項,而是將它的子類如 FileSystemXmlApplicationContext、ClassPathXmlApplicationContext 等的例項上轉型給ApplicationContext 物件。為了讓上轉型得到的 ApplicationContext 物件能直接呼叫getMessage 方法,那麼前提是ApplicationContext 介面自身必須要有這個方法,所以必須繼承。
Spring 的核心思想就是依賴注入,通過注入介面同時利用介面的多型性來使各部分只關注自己的業務,同時降低耦合。
相關推薦
Java —— MessageFormat類 處理國際化
一、MessageFormat 概覽 java.text包中的 Fomart 介面是所有處理格式的基礎介面,有三個子類:DateFormat、MessageFormat、NumberFormat。 MessageFormat 是專門處理文字格式的類,且沒有子類。 二、M
Java 使用BigDecimal類處理高精度計算
positive urn 使用 println highlight 轉換 posit exception val Java在java.math包中提供的API類BigDecimal,用來對超過16位有效位的數進行精確的運算。雙精度浮點型變量double可以處理16位有效數,
java中小數處理,bigDecimal類應用
add opened 對象 spl eof 轉換成 () double ply 1、構造一個BigDecimal對象: 1 //創建一個BigDecimal對象,初始化必須使用字符串,因為用數值初始化會得到近似值,不準確 2 BigDec
java基礎類庫學習(四.2)異常處理的陷阱
前言 java的異常類Exception繼承自非正常情況類Throwable,異常類下又分為兩大類:checked異常和runtime異常, 其中發生checked異常的類如果不做處理程式會發生編譯錯誤,導致程式中斷編譯 而runtime異常的類只有在執行階段才會發生,如果不做處理,
Java 常用類之異常處理
Java異常類是對於程式中可能出現的錯誤或者異常的一種處理方式。在設計程式的過程中,對於可能出現的異常錯誤,比如說使用者輸入錯誤,裝置錯誤,磁碟滿了或者程式碼錯誤等等,通常採用異常處理的方式來進行處理可能的錯誤。 JAVA的異常處理機制:如果某個方法不能按照正常的途徑完成任務,就可以通過另一
Apache PDFBox 2.0.13 釋出,Java 的 PDF 處理類庫
Apache PDFBox 2.0.13 已釋出,這是針對 2.0.12 版本的 Bug 修復版本,包含一些修復和小改進。 部分更新內容如下: [PDFBOX-4335] - Overlay should implement Closeable [PDFBOX-43
java中子類繼承父類和實現介面有同名方法怎麼處理
Java是一門單繼承語言,但是,在子類的繼承關係中,會存在父類和介面有同名方法的情況,這種情況該怎麼處理呢? 我們來一步步驗證: 1.子類繼承父類,實現介面,父類和介面有同名方法 public int
JAVA工具類分享之《JSON處理:JsonUtil》
前言 今天給大家分享的是操作json的工具類,使用的是jackson,如果你使用的是spring boot的話直接引入spring-boot-starter-parent響應的包會自動引入。 <parent> <
java中Excel處理工具類
/** 該工具類會返回處理結果和封裝之後的資料,獲取資料直接從 **/ import java.io.IOException; import java.io.InputStream; import java.text.DecimalFormat; import ja
java中自定義使用{0}佔位符功能之MessageFormat類
MessageFormat提供一種語言無關的方式來組裝訊息,它允許你在執行時刻用指定的引數來替換掉訊息字串中的一部分。你可以為MessageFormat定義一個模式,在其中你可以用佔位符來表示變化的部分,例如在下面的測試類中: package cn.lz.life.uti
JAVA工具類時間處理類DateUtils
廢話不說,直接上碼: package util; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Calenda
Java中最好使用BigDecimal類處理加減乘除運算
Java中,對於不需要任何準確計算精度的數字可以直接使用float或double運算,但是如果需要精確計算的結果,則必須使用類,而且使用類也可以進行大數的操作。ava.math.BigDecimal。BigDecimal一共有4個夠造方法,讓我先來看看其中的兩種用法:第一種:
JAVA 數字格式化處理方法; 國際化
有時我們需要控制輸出的數字的格式,如何使用java的類庫做到這個呢?例如數字“1234.56”如何以“1234.560”、“1,234.56”格式輸出,在此你可以找到答案例子:例如數字:1、1234.56以1234.560格式輸出DecimalFormat df1 = new
Java的數字類處理
數字格式化:Java主要對浮點型資料進行數字格式化操作,其中浮點型包括float(單精度)型和double(雙精度)型,在 Java中使用Java.text.DecimalFormat格式化數字; 在Java中沒有格式化的數字遵循以下原則; 1.如果資料絕
JAVA高階應用之異常類處理
異常分類 Throwable(異常最頂端的類) Error(伺服器奔潰 資料庫奔潰) Exception(異常類) RunTimeException 解決異常
Java 資料精確度處理&Mach類
Math.rint() a = Math.rint(a); 返回最接近引數的整數,如果有2個數同樣接近,則返回偶數的那個。它有兩個特殊的情況:1)如果引數本身是整數,則返回本身。2)如果不是數字或無窮大或正負0,則結果為其本身。 Math.round() a = Ma
Java之異常處理與工具類
異常處理 ① Error(錯誤) Error(錯誤) 一般指比較嚴重的問題,不做針對性處理,無法挽救; OutOfMemoryError 記憶體溢位 ② Exc
課堂動手動腦驗證以及自定義異常類實現對異常處理——java異常類
異常(exception):發生在程式執行期間,表明出現了一個非法執行的情況。許多JDK中的方法在檢測到非法情況時,都會丟擲一個異常物件。例如:陣列越界和被0除。 程式碼驗證: package test; import javax.swing.*; class AboutException { p
java工具類,在Windows,Linux系統獲取電腦的MAC地址、本地IP、電腦名
copy iter 去掉m [] equals linu stat cli catch package com.cloudssaas.util; import java.io.BufferedReader; import java.io.IOException;
轉載--創建java常量類的方法
ret get ntc www 引用 lsi public field log 1 /** 2 * Method One 3 */ 4 interface ConstantInterface { 5 String SUNDAY