简体   繁体   English

监听Java中的类重载

[英]Listening to class reload in Java

For performance reasons, I have a class that stores a Map whose key is a Class<?> and its value is function of that class's fields. 出于性能原因,我有一个存储Map的类,其键是Class<?> ,其值是该类字段的函数。 The map is populated during code execution according to the type of the calling object. 根据调用对象的类型,在代码执行期间填充映射。 The above is a 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);
    }

}

During development, thanks to the help of Jrebel, I often make modifications to classes by changing entire properties or just their names. 在开发过程中,借助于Jrebel的帮助,我经常通过更改整个属性或仅更改其名称来对类进行修改。 I can continue development just fine. 我可以继续发展。 However, if I already put a value into the cache it will be stale forever. 但是,如果我已经将一个值放入缓存,它将永远过时。

What I am asking here is if it is possible to intercept the event that a class in the classpath has changed. 我在这里问的是,是否有可能拦截类路径中的类已更改的事件。 Very broad... But my specific problem is very simple: since I have such a need only during development, I just want to wipe that cache in case any class in my classpath changes. 非常广泛...但是我的特定问题非常简单:由于仅在开发期间才有此需求,因此我只想擦除该缓存,以防我的类路径中的任何类发生更改。

How can I accomplish this? 我该怎么做? I don't need to do anything special than intercepting the event and simply wiping the cache 除了拦截事件并清除缓存之外,我不需要执行任何其他特殊操作

JRebel has a plugin API that you can use to trigger code on class reloads. JRebel有一个插件API,可用于在类重载时触发代码。 The tutorial complete with example application and plugin available here: https://manuals.zeroturnaround.com/jrebel/advanced/custom.html 该教程包含示例应用程序和插件,可在以下位置找到: https : //manuals.zeroturnaround.com/jrebel/advanced/custom.html

The JRebel plugin is a self-contained jar built against the JRebel SDK, which is attached to the running application via the JVM argument -Drebel.plugins=/path/to/my-plugin.jar . JRebel插件是针对JRebel SDK构建的自包含jar,它通过JVM参数-Drebel.plugins=/path/to/my-plugin.jar附加到正在运行的应用程序。 The JRebel agent attached to the application will load and start plugins from this argument. 附加到应用程序的JRebel代理将从此参数加载并启动插件。
If the application is not started with the JRebel agent, the plugin is simply not loaded. 如果应用程序不是使用JRebel代理启动的,则不会加载该插件。

In your example you want to register a ClassEventListener that will clear the Cache.fieldsList map. 在您的示例中,您要注册一个ClassEventListener ,它将清除Cache.fieldsList映射。 As it is a private field, you need to access it via reflection or add a get/clear method via a ClassBytecodeProcessor 由于它是一个私有字段,因此您需要通过反射来访问它或通过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 ...
}

And to clear the map 并清除地图

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

However a better option is to only clear/recalculate the single class entry on class reload if possible. 但是,更好的选择是,如果可能的话,仅在类重载时清除/重新计算单个类条目。 The example didn't display whether the info computed from one class depended on superclass infos, but if this is true, the JRebel SDK has methods to register a reload listener on the class hierarchy as well. 该示例未显示从一个类计算出的信息是否依赖于超类信息,但如果为true,则JRebel SDK也具有在类层次结构上注册重新加载侦听器的方法。

There is an existing class ClassValue which already does the job for you: 现有的类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);
    }
}

When you call get , it will call computeValue if this is the first call for this specific Class argument or return the already existing value otherwise. 当您调用get ,如果这是对此特定Class参数的第一次调用,它将调用computeValue ,否则将返回已经存在的值。 It does already care thread safety and for allowing classes to get garbage collected. 它确实已经在关注线程安全,并允许类收集垃圾。 You don't need to know when class unloading actually happens. 您无需知道何时真正卸载类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM