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

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

首先重寫下面方法,修改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ì)象,所以我們可以重寫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í)清理緩存,也可以通過接口調(diào)取手動(dòng)清理緩存,根據(jù)自己需求來定。
總結(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ó)際化過程詳解
- Spring如何實(shí)現(xiàn)輸出帶動(dòng)態(tài)標(biāo)簽的日志
相關(guān)文章
Java動(dòng)態(tài)線程池插件dynamic-tp集成zookeeper
ZooKeeper是一個(gè)分布式的,開放源碼的分布式應(yīng)用程序協(xié)調(diào)服務(wù),是Google的Chubby一個(gè)開源的實(shí)現(xiàn),是Hadoop和Hbase的重要組件。它是一個(gè)為分布式應(yīng)用提供一致性的軟件,提供的功能包括:配置維護(hù)、域名服務(wù)、分布式同步、組服務(wù)等2023-03-03
springBoot?啟動(dòng)指定配置文件環(huán)境多種方案(最新推薦)
springBoot?啟動(dòng)指定配置文件環(huán)境理論上是有多種方案的,一般都是結(jié)合我們的實(shí)際業(yè)務(wù)選擇不同的方案,比如,有pom.xml文件指定、maven命令行指定、配置文件指定、啟動(dòng)jar包時(shí)指定等方案,今天我們一一分享一下,需要的朋友可以參考下2023-09-09
JAVA讀取文件流,設(shè)置瀏覽器下載或直接預(yù)覽操作
這篇文章主要介紹了JAVA讀取文件流,設(shè)置瀏覽器下載或直接預(yù)覽操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-10-10
MyBatis動(dòng)態(tài)SQL標(biāo)簽的用法詳解
這篇文章主要介紹了MyBatis動(dòng)態(tài)SQL標(biāo)簽的用法詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
解決ApplicationContext獲取不到Bean的問題
這篇文章主要介紹了解決ApplicationContext獲取不到Bean的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06
Java中ReUtil正則表達(dá)式工具庫(kù)的使用
ReUtil是Hutool庫(kù)中的正則表達(dá)式工具類,提供了多種常用正則表達(dá)式操作方法,下面就來介紹一下ReUtil的使用,具有一定的參考價(jià)值,感興趣的可以了解一下2025-02-02
MyBatis查詢時(shí)屬性名和字段名不一致問題的解決方法
這篇文章主要給大家介紹了關(guān)于MyBatis查詢時(shí)屬性名和字段名不一致問題的解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01

