出于性能原因,我有一个存储Map
的类,其键为Class<?>
,其值是该类字段的函数。根据调用对象的类型在代码执行期间填充映射。以上是概括/简化
public class Cache {
private static final Map<Class<?>, String> fieldsList = ...;
//Synchronization omitted for brevity
public String getHqlFor(Class<?> entity){
if (!fieldsList.containsKey(entity))
fieldsList.put(entity,createHql(entity));
return fieldsList.get(entity);
}
}
在开发期间,感谢Jrebel的帮助,我经常通过更改整个属性或仅更改名称来修改类。我可以继续开发就好了。但是,如果我已将值放入缓存中,它将永远失效。
我在这里要问的是,是否可以拦截类路径中的类已更改的事件。非常广泛...但我的具体问题非常简单:因为我只在开发过程中有这样的需求,所以我只想在我的类路径中的任何类更改时擦除该缓存。
我怎样才能做到这一点?我不需要做任何特别的事情而不是拦截事件,只需擦除缓存
答案 0 :(得分:3)
JRebel有一个插件API,可用于在类重新加载时触发代码。本教程完成了示例应用程序和插件:https://manuals.zeroturnaround.com/jrebel/advanced/custom.html
JRebel插件是一个针对JRebel SDK构建的自包含jar,它通过JVM参数-Drebel.plugins=/path/to/my-plugin.jar
附加到正在运行的应用程序。附加到应用程序的JRebel代理将从此参数加载并启动插件
如果未使用JRebel代理启动应用程序,则不会加载插件。
在您的示例中,您希望注册一个ClassEventListener
,以清除Cache.fieldsList
地图。由于它是私有字段,您需要通过反射访问它或通过ClassBytecodeProcessor
public class MyPlugin implements Plugin {
void preinit() {
ReloaderFactory.getInstance().addClassReloadListener(new ClassEventListenerAdapter(0) {
@Override
public void onClassEvent(int eventType, Class<?> klass) throws Exception {
Cache.clear();
}
});
}
// ... other methods ...
}
清除地图
public class CacheCBP extends JavassistClassBytecodeProcessor {
public void process(ClassPool cp, ClassLoader cl, CtClass ctClass) {
ctClass.addMethod(CtMethod.make("public static void clear() { fieldsList.clear(); }", ctClass));
}
}
但是,如果可能的话,更好的选择是仅在类重新加载时清除/重新计算单个类条目。该示例没有显示从一个类计算的信息是否依赖于超类信息,但如果这是真的,则JRebel SDK还具有在类层次结构上注册重载监听器的方法。
答案 1 :(得分:0)
现有的课程ClassValue
已经为您完成了工作:
public class Cache {
private final ClassValue<String> backend = new ClassValue<String>() {
@Override
protected String computeValue(Class<?> entity) {
return createHql(entity);
}
};
public String getHqlFor(Class<?> entity){
return backend.get(entity);
}
}
当您致电get
时,如果这是对此特定computeValue
参数的第一次调用,则会调用Class
,否则返回已存在的值。它确实关注线程安全并允许类收集垃圾。您不需要知道何时实际发生类卸载。