在 JDK 1.6 中,新增了一个类 ResourceBundle.Control 可实现简单的自动重载,其实这是一个实现控制 ResourceBundle 缓存策略的类。该类有两个方法涉及到缓存:getTimeToLive、needsReload
其中 getTimeToLive 方法表示 ResourceBundle 实例在缓存中的生存时间,可以是某个具体的微秒值,也可以是不缓存(TTL_DONT_CACHE),或者永久缓存(TTL_NO_EXPIRATION_CONTROL)。
一旦设置了具体的微秒值时,假设 getTimeToLive 返回的是1000,那么每次在调用 getBundle 的时候,只要当前时间和载入资源的时间之差比TimeToLive要大,就要调用needsReload 方法来判断是否需要重新加载 ResourceBundle 。needsReload只返回 true 或者 false,表示是否重加载。
我们可以通过源码来看看具体的逻辑:(不感兴趣可以跳到最后直接看如何定时重载properties文件)
1、首先当我们没有禁用缓存时,首次加载资源文件时,系统会将文件缓存,此时会为缓存设置一个过期时间,而这个过期时间就是根据TimeToLive加上当前时间算出来的:
private static final void setExpirationTime(CacheKey cacheKey, Control control) {
long ttl = control.getTimeToLive(cacheKey.getName(),
cacheKey.getLocale());
if (ttl >= 0) {
// If any expiration time is specified, set the time to be
// expired in the cache.
long now = System.currentTimeMillis();
cacheKey.loadTime = now;
cacheKey.expirationTime = now + ttl;
} else if (ttl >= Control.TTL_NO_EXPIRATION_CONTROL) {
cacheKey.expirationTime = ttl;
} else {
throw new IllegalArgumentException("Invalid Control: TTL=" + ttl);
}
}
2、而我们调用getBundle(..)去获取资源包时,首先会去缓存中找,若找到了,还需要判断是否超时,超时了就要判断是否过期(是否需要重载)
synchronized (bundle) {
expirationTime = key.expirationTime;
if (!bundle.expired && expirationTime >= 0 &&
expirationTime <= System.currentTimeMillis()) {
try {// 超时之后判断needToReload,这个返回true才是真正的过期
bundle.expired = control.needsReload(key.getName(),
key.getLocale(),
key.getFormat(),
key.getLoader(),
bundle,
key.loadTime);
} catch (Exception e) {
cacheKey.setCause(e);
}
if (bundle.expired) {
// If the bundle needs to be reloaded, then
// remove the bundle from the cache, but
// return the bundle with the expired flag
// on.
bundle.cacheKey = null;
cacheList.remove(cacheKey, bundleRef);
} else {
// Update the expiration control info. and reuse
// the same bundle instance
setExpirationTime(key, control);
}
}
}
3、若需要重载的话,那就重新加载资源
package bundle.test;
import java.util.Locale;
import java.util.ResourceBundle;
/**
* 自动刷新资源文件
* @author Zhu
*
*/
public class ResourceTester {
private final static MyResourceBundleControl ctl = new MyResourceBundleControl();
/**
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
System.out.println(getBundle().getString("cancelKey"));
Thread.sleep(5000);
System.out.println(getBundle().getString("cancelKey"));
}
private static ResourceBundle getBundle() {
return ResourceBundle.getBundle("res", Locale.getDefault(), ctl);
}
/**
* 重载控制器
*/
private static class MyResourceBundleControl extends ResourceBundle.Control {
@Override
public long getTimeToLive(String baseName, Locale locale) {
return 1000;
}
@Override
public boolean needsReload(String baseName, Locale locale,
String format, ClassLoader loader, ResourceBundle bundle,
long loadTime) {
return true;
}
}
}
这样只要间隔时间超过1s去访问资源包,都能取到最新的~