i18n无法读取jar包外国际化文件的根本原因
首先我们看一下i18n是如何绑定资源文件路径的.
绑定资源文件路径的方法是通过下面方法绑定的。
resourcebundle.getbundle()
我们查看源码:
最终发现i18n是通过类加载器加载国际化文件的。
然而类加载器是不能加载jar包外的资源文件的,所以我们要改变加载资源文件的方式,我们可以通过file加载jar包外的资源文件。
改变文件读取方式
我们读取源码发现,i18n通过将资源文件读取为stream流存储在resourcebundle对象中,同时i18n存在缓存,将产生的stream对象存储在缓存中。
首先重写下面方法,修改i18n的读取方式。
这样我们就可以读取到jar包外面的资源文件了。
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; }
寻找切入点
我们不难发现,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存在缓存想要外部资源文件修改后生效,清除缓存,我们读取源码不难发现i18n为我们提供了清理缓存的方法。
我们可以定时清理缓存,也可以通过接口调取手动清理缓存,根据自己需求来定。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论