在 Java 中收听 class 重新加载
Listening to class reload in Java
出于性能原因,我有一个 class 存储一个 Map
,其键是一个 Class<?>
,它的值是 class 字段的函数。根据调用对象的类型,在代码执行期间填充映射。上面是一个generalization/simplification
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 的帮助,我经常通过更改整个属性或仅更改它们的名称来修改 classes。我可以继续开发就好了。但是,如果我已经将一个值放入缓存中,它将永远失效。
这里想问的是,是否可以拦截class路径中的某个class发生变化的事件。非常广泛......但我的具体问题非常简单:因为我只在开发期间有这样的需求,所以我只想擦除该缓存以防 any class 在我的class路径更改。
我怎样才能做到这一点?除了拦截事件并简单地 擦除 缓存
之外,我不需要做任何特别的事情
已有 class 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
时,如果这是第一次调用此特定 Class
参数,它将调用 computeValue
,否则将调用 return 已经存在的值。它确实已经关心线程安全并允许 classes 收集垃圾。您不需要知道 class 卸载实际发生的时间。
JRebel 有一个插件 API,您可以使用它在 class 重新加载时触发代码。本教程包含示例应用程序和插件,可在此处获取: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
添加 get/clear 方法
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));
}
}
然而,如果可能的话,更好的选择是在 class 重新加载时仅 clear/recalculate 单个 class 条目。该示例没有显示从 class 计算的信息是否依赖于 superclass 信息,但如果这是真的,JRebel SDK 有方法在 class 上注册一个重新加载侦听器层次结构也是如此。
出于性能原因,我有一个 class 存储一个 Map
,其键是一个 Class<?>
,它的值是 class 字段的函数。根据调用对象的类型,在代码执行期间填充映射。上面是一个generalization/simplification
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 的帮助,我经常通过更改整个属性或仅更改它们的名称来修改 classes。我可以继续开发就好了。但是,如果我已经将一个值放入缓存中,它将永远失效。
这里想问的是,是否可以拦截class路径中的某个class发生变化的事件。非常广泛......但我的具体问题非常简单:因为我只在开发期间有这样的需求,所以我只想擦除该缓存以防 any class 在我的class路径更改。
我怎样才能做到这一点?除了拦截事件并简单地 擦除 缓存
之外,我不需要做任何特别的事情已有 class 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
时,如果这是第一次调用此特定 Class
参数,它将调用 computeValue
,否则将调用 return 已经存在的值。它确实已经关心线程安全并允许 classes 收集垃圾。您不需要知道 class 卸载实际发生的时间。
JRebel 有一个插件 API,您可以使用它在 class 重新加载时触发代码。本教程包含示例应用程序和插件,可在此处获取: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));
}
}
然而,如果可能的话,更好的选择是在 class 重新加载时仅 clear/recalculate 单个 class 条目。该示例没有显示从 class 计算的信息是否依赖于 superclass 信息,但如果这是真的,JRebel SDK 有方法在 class 上注册一个重新加载侦听器层次结构也是如此。