2017-09-27 67 views
0

由於性能方面的原因,我有一個存儲Map的類,其關鍵是Class<?>,它的值是該類字段的函數。根據調用對象的類型,代碼執行過程中會填充該映射。以上是一個概括/簡化收聽Java中的類重裝

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

你可以使用自定義Classloader來做到這一點嗎? – daniu

回答

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,如果這是針對此特定Class參數的第一個調用,則將調用computeValue,否則將返回已經存在的值。它確實已經關心線程的安全性並允許類讓垃圾收集。你不需要知道什麼時候實際上發生班級卸貨。

+0

看起來不錯,但不適用於我的情況。我已經簡化了很多示例,getHqlFor至少需要兩個類作爲參數。但我正在尋找如何適應這一點。謝謝 –

+0

現在看起來適用於我的情況:-) –

+0

這對於JRebel類重新加載的情況不起作用,因爲新版本的類將與之前的版本進行比較,因此它不會計算新值。您可以嘗試重新加載示例代碼,方法是取消註釋新字段並查看舊結果:https://pastebin.com/iKhA6RuX – Murka

3

JRebel有一個插件API,可以用來在類重新加載時觸發代碼。本教程附帶示例應用程序和插件:https://manuals.zeroturnaround.com/jrebel/advanced/custom.html

JRebel插件是一個獨立的jar,它針對JRebel SDK構建,該JRebel SDK通過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 ... 
} 

通過反射訪問它,或添加一個get /清除方法和清除地圖

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也具有在類層次結構上註冊重載偵聽器的方法。

+0

我的理解是,這需要類路徑中的Jrebel SDK。但是,這絕不應該用於生產。我的意思是,如果我要求使用JRebel的依賴關係來污染應用程序的類路徑,我的老闆會很不高興。我將深入研究它 –

+0

使用JRebel時,JRebel SDK已經在類路徑中。 – Murka

+0

當您使用Ant的javac命令進行編譯並且定義良好的類路徑時,不會這樣。無論如何,我只是試圖在我的自動完成中鍵入「com.zeroturnaround」,沒有任何顯示。 'Plugin'不會顯示JRebel世界的結果 –