使用
Javassist
我正在尝试将一行代码插入方法的主体中。这是对 intellij-IDE
内标签文本颜色的简单修改。尝试这样做时出现以下错误:
引起:java.lang.LinkageError:加载器 com.intellij.util.lang.UrlClassLoader @d2cc05a 尝试为 com.intellij.ui.components.labels.LinkLabel 重复类定义。 (com.intellij.ui.components.labels.LinkLabel 位于加载器 com.intellij.util.lang.UrlClassLoader @d2cc05a、父加载器“platform”的未命名模块中)
这意味着我试图修改的类已经被 Java 类加载器加载了(至少我喜欢这样认为..)。 代码示例如下:
private static void test() {
try {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("com.intellij.ui.components.labels.LinkLabel");
CtMethod cm = cc.getDeclaredMethod("getTextColor");
cm.insertBefore("System.out.println(\"helloworld\");");
cc.toClass(); // <--- the problem
} catch (final Throwable e) {
e.printStackTrace();
}
}
作为参考,这里是访问的类:intellij 链接
Rafael Winterhalter 几年前评论过类似的问题(link)。不幸的是,我对
Javassist
的了解限制了我找到合适的解决方案。
关于如何解决这个问题有什么想法吗?
更新:可以在这里找到一个最小的、可重复的示例:github链接
我知道这是一个老问题,但当你搜索“重复的类定义javassist”时,它是谷歌上的最高结果,所以我不妨分享可能的解决方案。
您遇到的问题是因为该类已经加载到类池中。在类加载到类池之前,您必须修改/重新定义类。
在您的特定问题中,您正在尝试重新定义
com.intellij.ui.components.labels.LinkLabel
。这个class有一个名为ourVisitedLinks
的静态字段。此静态字段将强制在第一次提及 LinkLabel
类时加载 LinkLabel
类。
当您调用
cc.toClass()
时,您实际上是在引用 LinkLabel
类,从而将该类加载到类池中,因为静态字段 ourVisitedLinks
已初始化。因此,在加载该类的修改版本之前,LinkLabel
类会隐式加载到类池中。
我承认我不明白 javassist 内部发生的一切。我只知道由于静态字段被初始化,
cc.toClass()
会抢先将类加载到类池中。
您必须致电
cc.toClass(SomeNeighbor.class)
。 SomeNeighbor.class
是我为示例虚构的一个类。它必须是与您尝试重新定义的类位于同一包中的类。
LinkLabel
类具有以下邻居,它们共享相同的包com.intellij.ui.components.labels
:
您可以选择这 5 个类中的任何一个作为邻居,只要该类不具有
LinkLabel
的任何直接或传递静态依赖项。
这是问题中示例的更正代码。遗憾的是,问题中共享的 github 链接无法再访问,因此我无法提供完美的可执行示例。
private static void test() {
try {
ClassPool cp = ClassPool.getDefault();
CtClass cc = cp.get("com.intellij.ui.components.labels.LinkLabel");
CtMethod cm = cc.getDeclaredMethod("getTextColor");
cm.insertBefore("System.out.println(\"helloworld\");");
cc.toClass(BoldLabel.class); // <--- the correction
} catch (final Throwable e) {
e.printStackTrace();
}
}