IT俱乐部 Java SpringBoot集成I18n国际化文件在jar包外生效问题

SpringBoot集成I18n国际化文件在jar包外生效问题

i18n无法读取jar包外国际化文件的根本原因

首先我们看一下i18n是如何绑定资源文件路径的.

绑定资源文件路径的方法是通过下面方法绑定的。

1
ResourceBundle.getBundle()

我们查看源码:

最终发现i18n是通过类加载器加载国际化文件的。

然而类加载器是不能加载jar包外的资源文件的,所以我们要改变加载资源文件的方式,我们可以通过file加载jar包外的资源文件。

改变文件读取方式

我们读取源码发现,i18n通过将资源文件读取为stream流存储在ResourceBundle对象中,同时i18n存在缓存,将产生的stream对象存储在缓存中。

首先重写下面方法,修改i18n的读取方式。

这样我们就可以读取到jar包外面的资源文件了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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) () -> 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对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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为我们提供了清理缓存的方法。

我们可以定时清理缓存,也可以通过接口调取手动清理缓存,根据自己需求来定。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持IT俱乐部。

本文收集自网络,不代表IT俱乐部立场,转载请注明出处。https://www.2it.club/code/java/10842.html
上一篇
下一篇
联系我们

联系我们

在线咨询: QQ交谈

邮箱: 1120393934@qq.com

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部