在Java中听类重载

时间:2017-09-27 12:41:35

标签: java caching classloader jrebel

出于性能原因,我有一个存储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的帮助,我经常通过更改整个属性或仅更改名称来修改类。我可以继续开发就好了。但是,如果我已将值放入缓存中,它将永远失效。

我在这里要问的是,是否可以拦截类路径中的类已更改的事件。非常广泛...但我的具体问题非常简单:因为我只在开发过程中有这样的需求,所以我只想在我的类路径中的任何类更改时擦除该缓存。

我怎样才能做到这一点?我不需要做任何特别的事情而不是拦截事件,只需擦除缓存

2 个答案:

答案 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

添加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));
  }
}

但是,如果可能的话,更好的选择是仅在类重新加载时清除/重新计算单个类条目。该示例没有显示从一个类计算的信息是否依赖于超类信息,但如果这是真的,则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,否则返回已存在的值。它确实关注线程安全并允许类收集垃圾。您不需要知道何时实际发生类卸载。