SpringBoot集成I18n國際化文件在jar包外生效問題
i18n無法讀取jar包外國際化文件的根本原因
首先我們看一下i18n是如何綁定資源文件路徑的.
綁定資源文件路徑的方法是通過下面方法綁定的。
ResourceBundle.getBundle()
我們查看源碼:
最終發(fā)現(xiàn)i18n是通過類加載器加載國際化文件的。
然而類加載器是不能加載jar包外的資源文件的,所以我們要改變加載資源文件的方式,我們可以通過file加載jar包外的資源文件。
改變文件讀取方式
我們讀取源碼發(fā)現(xiàn),i18n通過將資源文件讀取為stream流存儲在ResourceBundle對象中,同時i18n存在緩存,將產(chǎn)生的stream對象存儲在緩存中。
首先重寫下面方法,修改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; }
尋找切入點
我們不難發(fā)現(xiàn),ResourceBundle.getBundle()這個方法就是為了獲取一個ResourceBundle對象,所以我們可以重寫doGetBundle方法從而獲取ResourceBundle對象。
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方法
因為i18n存在緩存想要外部資源文件修改后生效,清除緩存,我們讀取源碼不難發(fā)現(xiàn)i18n為我們提供了清理緩存的方法。
我們可以定時清理緩存,也可以通過接口調(diào)取手動清理緩存,根據(jù)自己需求來定。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java動態(tài)線程池插件dynamic-tp集成zookeeper
ZooKeeper是一個分布式的,開放源碼的分布式應(yīng)用程序協(xié)調(diào)服務(wù),是Google的Chubby一個開源的實現(xiàn),是Hadoop和Hbase的重要組件。它是一個為分布式應(yīng)用提供一致性的軟件,提供的功能包括:配置維護、域名服務(wù)、分布式同步、組服務(wù)等2023-03-03springBoot?啟動指定配置文件環(huán)境多種方案(最新推薦)
springBoot?啟動指定配置文件環(huán)境理論上是有多種方案的,一般都是結(jié)合我們的實際業(yè)務(wù)選擇不同的方案,比如,有pom.xml文件指定、maven命令行指定、配置文件指定、啟動jar包時指定等方案,今天我們一一分享一下,需要的朋友可以參考下2023-09-09JAVA讀取文件流,設(shè)置瀏覽器下載或直接預(yù)覽操作
這篇文章主要介紹了JAVA讀取文件流,設(shè)置瀏覽器下載或直接預(yù)覽操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10解決ApplicationContext獲取不到Bean的問題
這篇文章主要介紹了解決ApplicationContext獲取不到Bean的問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06