我按照http://www.jacoco.org/jacoco/trunk/doc/examples/java/CoreTutorial.java的CoreTutorial示例类来了解如何将Jacoco合并到项目中。
但是,我面临的java.lang.LinkageError问题与使用的MemoryClassLoader类有关。
/**
* A class loader that loads classes from in-memory data.
*/
public static class MemoryClassLoader extends ClassLoader {
private final Map<String, byte[]> definitions = new HashMap<String, byte[]>();
/**
* Add a in-memory representation of a class.
*
* @param name
* name of the class
* @param bytes
* class definition
*/
public void addDefinition(final String name, final byte[] bytes) {
definitions.put(name, bytes);
}
@Override
protected Class<?> loadClass(final String name, final boolean resolve)
throws ClassNotFoundException {
final byte[] bytes = definitions.get(name);
if (bytes != null) {
return defineClass(name, bytes, 0, bytes.length);
}
return super.loadClass(name, resolve);
}
}
具体来说,我有两个类,MyClass和MySubClass,其中MySubClass扩展了MyClass。我对这两个课程进行测试,以便获得与每个课程相关的覆盖信息。我发现如果我首先测试MySubClass,MemoryClassLoader将调用带有MySubClass的检测字节的defineClass。但是,在这样做时,父类的MyClass也会被父类加载器加载。因此,当我下一个仪器并加载MyClass时,我收到java.lang.LinkageError,声称已经有一个由ClassLoader加载的MyClass的定义。这里的问题是加载的MyClass的初始版本没有检测。
如果我以相反的顺序加载类,则不会出现此问题。
我已经尝试了一些不同的东西:https://github.com/huangwaylon/randoop/blob/bloodhound/src/main/java/randoop/main/MemoryClassLoader.java通过在递归调用loadClass时立即尝试检测父类。但是,由于我还没有想到的原因,这并不完全正常。我观察到,如果我使用这种错误的方法,两个类的覆盖率信息都不会改变。
我的总体问题是,我如何在父母,子女关系中相互关联的类中进行检测和加载,以使行为不受订单的影响?我可以替换类的定义吗?另一个问题似乎是super.load,它是MemoryClassLoader的父类ClassLoader,我无法控制。
答案 0 :(得分:1)
首先,您应该覆盖findClass
而不是loadClass
。具有特定名称的类只能在加载器中定义一次的限制始终适用,并且您可能总是遇到多次请求相同类的情况(在任何非平凡的情况下)。
从loadClass
继承的ClassLoader
实现已经处理了这个并返回现有的定义(如果有的话)。因为它还首先查询父加载器,所以如果加载器应该定义名称也被其他类加载器使用的类,则应该注意指定正确的父加载器。使用loadClass
的默认实现,您的findClass
方法可以保持这么简单:
public static class MemoryClassLoader extends ClassLoader {
private final Map<String, byte[]> definitions = new HashMap<>();
public void addDefinition(final String name, final byte[] bytes) {
definitions.put(name, bytes);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
final byte[] bytes = definitions.get(name);
if (bytes != null) {
return defineClass(name, bytes, 0, bytes.length);
}
return super.findClass(name);
}
}
确保所有预期的课程都经过检测,可以通过两种方式完成。
按需对其进行检测,即让findClass
方法触发所请求类的检测。
在第一次调用loadClass
之前,检测所有预期类的字节代码并将结果放入加载器。
final MemoryClassLoader memoryClassLoader = new MemoryClassLoader();
memoryClassLoader.addDefinition("MyClass", instrumentedMyClass);
memoryClassLoader.addDefinition("MySubClass", instrumentedMySubClass);
final Class<?> myClass = memoryClassLoader.loadClass("MyClass");
此处,addDefinition
调用的顺序无关紧要,您是先调用loadClass("MyClass")
还是loadClass("MySubClass")
也无关紧要。这甚至适用于循环依赖。