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

 更新时间:2024年04月03日 10:58:20   作者:锋璠  
这篇文章主要介绍了SpringBoot集成I18n国际化文件在jar包外生效问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

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为我们提供了清理缓存的方法。

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

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 详解如何将springboot项目导出成war包

    详解如何将springboot项目导出成war包

    这篇文章主要介绍了详解如何将springboot项目导出成war包,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-10-10
  • springboot-dubbo cannot be cast to问题及解决

    springboot-dubbo cannot be cast to问题及解决

    这篇文章主要介绍了springboot-dubbo cannot be cast to问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • maven的pom文件与打包详解

    maven的pom文件与打包详解

    pom文件定于了一个maven项目的maven配置,一般pom文件的放在项目或者模块的根目录下。本文详细的介绍了pom文件配置,感兴趣的可以了解一下
    2021-08-08
  • RocketMQ-延迟消息的处理流程介绍

    RocketMQ-延迟消息的处理流程介绍

    这篇文章主要介绍了RocketMQ-延迟消息的处理流程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • 关于SpringMVC在Controller层方法的参数解析详解

    关于SpringMVC在Controller层方法的参数解析详解

    在SpringMVC中,控制器Controller负责处理由DispatcherServlet分发的请求,下面这篇文章主要给大家介绍了关于SpringMVC在Controller层方法的参数解析的相关资料,需要的朋友可以参考下
    2021-12-12
  • 浅谈Java中Properties类的详细使用

    浅谈Java中Properties类的详细使用

    properties类继承自hashtable,通常和io流结合使用。它最突出的特点是将key/value作为配置属性写入到配置文件中以实现配置持久化,或从配置文件中读取这些属性。它的这些配置文件的规范后缀名为".properties"。表示了一个持久的属性集
    2021-06-06
  • Java异常处理 如何跟踪异常的传播路径

    Java异常处理 如何跟踪异常的传播路径

    这篇文章主要介绍了Java异常处理 如何跟踪异常的传播路径,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-07-07
  • SpringBoot学习之基于注解的缓存

    SpringBoot学习之基于注解的缓存

    spring boot对缓存支持非常灵活,我们可以使用默认的EhCache,也可以整合第三方的框架,只需配置即可,下面这篇文章主要给大家介绍了关于SpringBoot学习之基于注解缓存的相关资料,需要的朋友可以参考下
    2022-03-03
  • jenkins+maven+svn自动部署和发布的详细图文教程

    jenkins+maven+svn自动部署和发布的详细图文教程

    Jenkins是一个开源的、可扩展的持续集成、交付、部署的基于web界面的平台。这篇文章主要介绍了jenkins+maven+svn自动部署和发布的详细图文教程,需要的朋友可以参考下
    2020-09-09
  • Java实现简单的学生教师管理系统

    Java实现简单的学生教师管理系统

    这篇文章主要为大家详细介绍了Java实现简单的学生教师管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02

最新评论