SpringBoot集成I18n國(guó)際化文件在jar包外生效問(wèn)題
i18n無(wú)法讀取jar包外國(guó)際化文件的根本原因
首先我們看一下i18n是如何綁定資源文件路徑的.
綁定資源文件路徑的方法是通過(guò)下面方法綁定的。
ResourceBundle.getBundle()
我們查看源碼:
最終發(fā)現(xiàn)i18n是通過(guò)類加載器加載國(guó)際化文件的。

然而類加載器是不能加載jar包外的資源文件的,所以我們要改變加載資源文件的方式,我們可以通過(guò)file加載jar包外的資源文件。
改變文件讀取方式
我們讀取源碼發(fā)現(xiàn),i18n通過(guò)將資源文件讀取為stream流存儲(chǔ)在ResourceBundle對(duì)象中,同時(shí)i18n存在緩存,將產(chǎn)生的stream對(duì)象存儲(chǔ)在緩存中。

首先重寫(xiě)下面方法,修改i18n的讀取方式。
這樣我們就可以讀取到j(luò)ar包外面的資源文件了。
private class I18nMessageSourceControl extends ResourceBundle.Control {
@Override
@Nullable
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException {
// Special handling of default encoding
if (format.equals("java.properties")) {
String bundleName = toBundleName(baseName, locale);
final String resourceName = toResourceName(bundleName, "properties");
InputStream inputStream;
try {
inputStream = AccessController.doPrivileged((PrivilegedExceptionAction<InputStream>) () -> getBufferedInputStream(resourceName));
} catch (PrivilegedActionException ex) {
throw (IOException) ex.getException();
}
if (inputStream != null) {
String encoding = getDefaultEncoding();
if (encoding != null) {
try (InputStreamReader bundleReader = new InputStreamReader(inputStream, encoding)) {
return loadBundle(bundleReader);
}
} else {
try (InputStream bundleStream = inputStream) {
return loadBundle(bundleStream);
}
}
} else {
return null;
}
} else {
// Delegate handling of "java.class" format to standard Control
return super.newBundle(baseName, locale, format, loader, reload);
}
}
}
/**
* 拼接url 并返回輸入流
*/
public InputStream getBufferedInputStream(String resourceName) throws FileNotFoundException {
String fileUrl = System.getProperty("user.dir")+ "/"+ resourceName;
System.out.println(fileUrl+"..*");
File file = new File(fileUrl);
if (file.exists()) {
return new FileInputStream(file);
}
return null;
}
尋找切入點(diǎn)
我們不難發(fā)現(xiàn),ResourceBundle.getBundle()這個(gè)方法就是為了獲取一個(gè)ResourceBundle對(duì)象,所以我們可以重寫(xiě)doGetBundle方法從而獲取ResourceBundle對(duì)象。
public class I18nConfig extends ResourceBundleMessageSource {
private final static Logger logger = LoggerFactory.getLogger(I18nConfig.class);
@Nullable
private volatile I18nMessageSourceControl control = new I18nMessageSourceControl();
/**
* Obtain the resource bundle for the given basename and Locale.
*
* @param basename the basename to look for
* @param locale the Locale to look for
* @return the corresponding ResourceBundle
* @throws MissingResourceException if no matching bundle could be found
* @see java.util.ResourceBundle#getBundle(String, Locale, ClassLoader)
* @see #getBundleClassLoader()
*/
public ResourceBundle doGetBundle(String basename, Locale locale) throws MissingResourceException {
ClassLoader classLoader = getBundleClassLoader();
Assert.state(classLoader != null, "No bundle ClassLoader set");
I18nMessageSourceControl control = this.control;
if (control != null) {
try {
return ResourceBundle.getBundle(basename, locale, classLoader, control);
} catch (UnsupportedOperationException ex) {
// Probably in a Jigsaw environment on JDK 9+
this.control = null;
String encoding = getDefaultEncoding();
if (encoding != null && logger.isInfoEnabled()) {
logger.info("ResourceBundleMessageSource is configured to read resources with encoding '" +
encoding + "' but ResourceBundle.Control not supported in current system environment: " +
ex.getMessage() + " - falling back to plain ResourceBundle.getBundle retrieval with the " +
"platform default encoding. Consider setting the 'defaultEncoding' property to 'null' " +
"for participating in the platform default and therefore avoiding this log message.");
}
}
}
// Fallback: plain getBundle lookup without Control handle
return ResourceBundle.getBundle(basename, locale, classLoader);
}
@Scheduled(fixedRate = 180000)
public void clearI18nCache() {
ResourceBundle.clearCache(Objects.requireNonNull(getBundleClassLoader()));
}
}
最后的clearI18nCache方法
因?yàn)閕18n存在緩存想要外部資源文件修改后生效,清除緩存,我們讀取源碼不難發(fā)現(xiàn)i18n為我們提供了清理緩存的方法。
我們可以定時(shí)清理緩存,也可以通過(guò)接口調(diào)取手動(dòng)清理緩存,根據(jù)自己需求來(lái)定。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- SpringBoot的@GetMapping路徑匹配規(guī)則、國(guó)際化詳細(xì)教程
- SpringBoot實(shí)現(xiàn)前后端分離國(guó)際化的示例詳解
- 史上最佳springboot Locale 國(guó)際化方案
- Springboot+AOP實(shí)現(xiàn)返回?cái)?shù)據(jù)提示語(yǔ)國(guó)際化的示例代碼
- 基于springboot i18n國(guó)際化后臺(tái)多種語(yǔ)言設(shè)置的方式
- 如何在springboot中實(shí)現(xiàn)頁(yè)面的國(guó)際化
- SpringBoot參數(shù)校驗(yàn)與國(guó)際化使用教程
- SpringBoot實(shí)現(xiàn)國(guó)際化過(guò)程詳解
- Spring如何實(shí)現(xiàn)輸出帶動(dòng)態(tài)標(biāo)簽的日志
相關(guān)文章
深入淺出分析Java抽象類和接口【功能,定義,用法,區(qū)別】
這篇文章主要介紹了Java抽象類和接口,結(jié)合實(shí)例形式深入淺出的分析了java抽象類與接口的功能功能,定義,用法及區(qū)別,需要的朋友可以參考下2017-08-08
詳解簡(jiǎn)單基于spring的redis配置(單機(jī)和集群模式)
這篇文章主要介紹了詳解簡(jiǎn)單基于spring的redis配置(單機(jī)和集群模式),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-02-02
基于Quartz定時(shí)調(diào)度任務(wù)(詳解)
下面小編就為大家?guī)?lái)一篇基于Quartz定時(shí)調(diào)度任務(wù)(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-11-11
SpringBoot @SpringBootTest加速單元測(cè)試的小訣竅
這篇文章主要介紹了SpringBoot @SpringBootTest加速單元測(cè)試的小訣竅,具有很好的參考價(jià)值,對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
Activiti7通過(guò)代碼動(dòng)態(tài)生成工作流實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了Activiti7通過(guò)代碼動(dòng)態(tài)生成工作流實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
Java 向上轉(zhuǎn)型和向下轉(zhuǎn)型的詳解
這篇文章主要介紹了 Java 向上轉(zhuǎn)型和向下轉(zhuǎn)型的詳解的相關(guān)資料,需要的朋友可以參考下2017-04-04
Java類成員訪問(wèn)權(quán)限控制知識(shí)總結(jié)
這篇文章主要介紹了Java類成員訪問(wèn)權(quán)限控制知識(shí)總結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04

