如何清除ResourceBundle缓存

4
这是一个运行在Tomcat上,使用Guice的Web应用程序。根据文档,我们应该能够调用ResourceBundle.clearCache();来清除ResourceBundle缓存,并从bundle属性文件中获取最新的内容。
我们还尝试了以下方法:
Class klass = ResourceBundle.getBundle("my.bundle").getClass().getSuperclass();
Field field = klass.getDeclaredField("cacheList");
field.setAccessible(true);
ConcurrentHashMap cache = (ConcurrentHashMap) field.get(null);
cache.clear(); // If i debug here I can see the cache is now empty!

并且

ResourceBundle.clearCache(this.class.getClassLoader());

我期望的行为是:
  1. 启动Tomcat并访问页面,页面应显示“Hello World”
  2. 更改包含“Hello World”的属性文件为“Goodbye Earth”
  3. 使用servlet清除缓存
  4. 再次访问页面,页面应显示“Goodbye Earth”
那么问题来了,ResourceBundle.clearCache()实际上是如何工作的?是否还需要清除一些通用的文件缓存?

ResourceBundle.clearCache() 是在 Java 1.6 中添加的。我曾经在一个 Java 1.4 服务器上工作,这就是 clearCache() 没有按预期工作的原因。 - Devrim
6个回答

6
这对我有用:
ResourceBundle.clearCache();
ResourceBundle resourceBundle= ResourceBundle.getBundle("YourBundlePropertiesFile");
String value = resourceBundle.getString("your_resource_bundle_key");

注意:

  1. ResourceBundle.clearCache() 在Java 1.6中添加。
  2. 不要使用静态resourceBundle属性,而是在调用clearCache()方法后使用ResourceBundle.getBundle()方法。

5

我不认为您可以影响已创建的ResourceBundle实例的重新加载,因为其内部控制类已经被创建。您可以尝试以下替代方法来初始化您的bundle:

ResourceBundle.getBundle("my.bundle", new ResourceBundle.Control() {
    @Override
    public long getTimeToLive(String arg0, Locale arg1) {
        return TTL_DONT_CACHE;
    }
});

尽管这个解决方案是完全关闭缓存,但我尝试了它,它仍然没有起作用。我的感觉是Tomcat正在缓存属性文件?真烦人... - Ben George
那很可能是这种情况。让我想另外一种解决方法。 - Perception
每当您想读取修改后的文件时,都必须再次使用相同的方法获取Bundle。TTL_DONT_CACHE并不意味着它总是查找原始文件,而是在下一次调用getBundle()时读取原始文件。 - Keshan Fernando

1

我找到了这个解决方案(适用于Tomcat):

  • 使用自定义的ResourceBundle.Control(因为我需要UTF8)
  • 将getTimeToLive添加为“感知”描述
  • 强制重新加载标志
  • "ResourceBundle.clearCache"不起作用

如何调用:

ResourceBundle bundle = ResourceBundle.getBundle("yourfile", new UTF8Control());

自定义类:

public class UTF8Control extends Control
{
    public ResourceBundle newBundle(
        String baseName,
        Locale locale,
        String format,
        ClassLoader loader,
        boolean reload)
    throws IllegalAccessException, InstantiationException, IOException
    {
        // The below is a copy of the default implementation.
        String bundleName = toBundleName(baseName, locale);
        String resourceName = toResourceName(bundleName, "properties");
        ResourceBundle bundle = null;
        InputStream stream = null;

        // FORCE RELOAD because needsReload doesn't work and reload is always false
        reload = true;

        if (reload) {
            URL url = loader.getResource(resourceName);
            if (url != null) {
                URLConnection connection = url.openConnection();
                if (connection != null) {
                    connection.setUseCaches(false);
                    stream = connection.getInputStream();
                }
            }
        }
        else {
            stream = loader.getResourceAsStream(resourceName);
        }

        if (stream != null) {
            try {
                // Only this line is changed to make it to read properties files as UTF-8.
                bundle = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8"));
            }
            finally {
                stream.close();
            }
        }
        return bundle;
    }

    // ASK NOT TO CACHE
    public long getTimeToLive(String arg0, Locale arg1) {
        return TTL_DONT_CACHE;
    }
}

我喜欢这种方法。我将ConcurrentHashMap缓存资源包与文件监视器相结合,以在文件更改时删除包。Pebble模板引擎实现了自己的上述副本,因此我不得不覆盖它。 - Andrew Grothe

0

只有在你能够拦截资源包的第一次创建时,这才有效:

while (true) {
    ResourceBundle resourceBundle = ResourceBundle.getBundle("SystemMessages", new Locale("hu", "HU"),
            new ResourceBundle.Control() {
                @Override
                public List<String> getFormats(String baseName) {
                    return ResourceBundle.Control.FORMAT_PROPERTIES;
                }

                @Override
                public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload) throws IllegalAccessException, InstantiationException, IOException {
                    System.err.println(this.toBundleName(baseName, locale) + ": " + format + " - " + reload);
                    return super.newBundle(baseName, locale, format, loader, reload);
                }

                @Override
                public long getTimeToLive(String baseName, Locale locale) {
                    long ttl = 1000;
                    System.err.println(this.toBundleName(baseName, locale) + " - " + ttl + "ms");
                    return ttl;
                }

                @Override
                public boolean needsReload(String baseName, Locale locale, String format, ClassLoader loader, ResourceBundle bundle, long loadTime) {
                    System.err.println(baseName + "_" + locale + " - " + new Date(loadTime));
                    return true;
                }
            });
    System.out.println(resourceBundle.getString("display.first_name") + ": John");
    System.out.println(resourceBundle.getString("display.last_name") + ": Doe");
    Thread.sleep(5000);
}

如果可能的话,只需使用Spring的ReloadableResourceBundleMessageSource。 - gabor

0

0
this is one more possibility to clear cache 
Class<ResourceBundle> type = ResourceBundle.class;
        try {
            Field cacheList = type.getDeclaredField("cacheList");
            cacheList.setAccessible(true);

            ((Map<?, ?>) cacheList.get(ResourceBundle.class)).clear();
        }
        catch (Exception e) {

           system.out.print("Failed to clear ResourceBundle cache" + e);
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接