【发布时间】:2020-02-10 21:27:59
【问题描述】:
我有一个扩展 GroovyClassLoader 的自定义 ClassLoader,它将源代码编译为磁盘上的 .class 文件,然后加载生成的类:
class MyClassLoader extends GroovyClassLoader {
File cache = new File( './cache' )
Compiler compiler
MyClassLoader() {
CompilerConfiguration cc = new CompilerConfiguration( targetDirectory:cache )
compiler = new Compiler( cc )
addClasspath cache.path
}
@Override
Class findClass( name ) {
try{
parent.findClass name
}catch( ClassNotFoundException e ){
compiler.compile name, getBodySomehow()
byte[] blob = loadFromFileSystem name
Class c = defineClass name, blob, 0, blob.length
setClassCacheEntry c
c
}
}
@Override
void removeClassCacheEntry(String name) {
Class c = cache[ name ]
super.removeClassCacheEntry(name)
GroovySystem.metaClassRegistry.removeMetaClass c
deleteFiles name
}
}
Class clazz = myClassLoader.loadClass 'some.pckg.SomeClass'
现在,如果我更改源代码,请致电myClassLoader.removeClassCacheEntry(name) 并再次尝试myClassLoader.loadClass(),我得到:
java.lang.LinkageError: loader (com/my/MyClassLoader 的实例): 尝试为名称 some/pckg/SomeClass 重复类定义
我阅读了互联网的大半部分,找到了一个“解决方案”来为每个类初始化一个类加载器:
MyClassLoader myClassLoader = new MyClassLoader()
Class clazz = myClassLoader.loadClass 'some.pckg.SomeClass'
这似乎有效,但引起了我的性能问题......
重新加载类的正确方法是什么?如何重用相同的类加载器?我错过了什么?
【问题讨论】:
-
我认为
removeClassCacheEntry删除了作为groovy加载的类,findClass使用java类加载器加载.class的问题 -
@daggett 需要
classCache以避免一遍又一遍地重新编译相同的源代码。从技术上讲,我可以使用相同的源代码多次调用gcl.parseClass(),但问题仍然存在:旧类没有从 JVM 中显式删除 -
那么不要使用java本地方式从缓存(类路径等)加载
.class文件。你必须加载它们,因为它是 groovy 加载的,然后 groovy lethodremoveClassCacheEntry应该可以工作 -
@daggett 我很确定
gcl.parseClass()在每次运行时都会实例化一个新的内部加载器。如果我多次运行println gcl.parseClass( body ).classLoader,它会打印出不同的哈希码 -
我现在倾向于认为我的代码是完美的,或者换句话说,它是我能摆脱最糟糕的最好的代码