我正在使用JDiagram JAR,如下所示
Diagram myDigram = new Diagram();
myDigram.routeAllLinks();
使用JRE 7运行时此代码可以正常工作,但是当它与JRE 8一起运行时,会抛出以下错误:
java.lang.StackOverflowError
at java.util.Collections.sort(Unknown Source)
at com.mindfusion.common.ExtendedArrayList.sort(Unknown Source)
at java.util.Collections.sort(Unknown Source)
at com.mindfusion.common.ExtendedArrayList.sort(Unknown Source)
at java.util.Collections.sort(Unknown Source)
at com.mindfusion.common.ExtendedArrayList.sort(Unknown Source)
我跟着堆栈跟踪到JDiagram反编译代码。观察到routeAllLinks()在另一个对象(比如路由器)上调用RouteLinks(),并且在另一个级别深度调用错误堆栈跟踪中出现的ExtendedArrayList.sort()。 JDiagram中的“ExtendedArrayList”扩展了ArrayList,并包含一个名为“sort()”的方法,该方法具有以下定义。
public void sort(Comparator<? super T> paramComparator)
{
Collections.sort(this, paramComparator);
}
在Google上,我发现JRE 8引入了List.sort()并将Collections.sort()调用委托给集合(在我的情况下为ExtendedArrayList)排序方法。因此,库ExtendedArrayList.sort()成为了一个覆盖。它会创建一个无限递归,从而导致堆栈溢出。我现在可以用一小段代码重现这个问题。
也
- 我们创建JDiagram对象的原始类在运行时由我们产品中的其他组件加载。我们对程序的加载几乎没有控制权。
- 我们发现最新版本的JDiagram通过用sortJ7()方法替换sort()来解决这个问题。但是,此时我们无法升级库。 JDiagram是一个许可的API。
- ExtendedArrayList由JDiagram在内部实例化,因此我们无法从代码中更改它。
我们尝试过以下目前无效的解决方案
- Java代理:因为我们的代码不直接调用ExtendedArrayList
而且'Diagram'没有任何界面。
- Spring AOP:我们是
不使用spring,我们的程序也由其他程序加载运行时
零件。
- AspectJ:到目前为止,这显然是一个解决方案。然而,
它也没有用,因为我们无法编织我们的程序
运行。不确定是否有人可以使它工作。
如果有任何要点需要详细说明,请告诉我。
欢迎任何帮助。谢谢。
UPDATE
到目前为止,javassist是最好的方法,但JDiagram混淆阻止解决方案正常工作。考虑到我们的发布日期,我们有点认为不可能(不得不说)解决。我们已经开始升级库的过程。同时从我们的应用程序中删除了一个由routeAllLinks()方法提供的小功能.. :-(
谢谢大家的帮助。我将继续研究这个问题,因为我发现它真的很有趣并且具有挑战性。如果我能解决它,我会更新帖子。我会给@gontard奖励我的javassist方法,因为我'继续我的研究。谢谢。
我用一个基本的例子重现了你的问题:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class ExtendedArrayList<E> extends ArrayList<E> {
@Override
public void sort(Comparator<? super E> c) {
Collections.sort(this, c);
}
}
import java.util.Arrays;
public class Main {
public static void main(String[] args) throws Exception {
ExtendedArrayList<String> arrayList = new ExtendedArrayList<String>();
arrayList.addAll(Arrays.asList("z", "y", "x"));
arrayList.sort(String::compareTo); // -> java.lang.StackOverflowError
}
}
我能绕过了 java.lang.StackOverflowError
通过使用重命名方法 了Javassist:
import java.util.Arrays;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
public class Main {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.get("ExtendedArrayList");
CtClass[] sortParams = new CtClass[]{ pool.get("java.util.Comparator")};
CtMethod sortMethod = ctClass.getDeclaredMethod("sort", sortParams);
sortMethod.setName("sortV7"); // rename
ctClass.toClass();
ExtendedArrayList<String> arrayList = new ExtendedArrayList<String>();
arrayList.addAll(Arrays.asList("z", "y", "x"));
System.err.println(arrayList); // print [z, y, x]
arrayList.sort(String::compareTo);
System.err.println(arrayList); // print [x, y, z]
}
}
我没试过你的版本 JDiagram
因为我的网站上只有最后一个(Java 8兼容)版本。
想一想 反编译库 并自己解决问题。您可以使用此固定包作为解决方法。
替代 会是的 在您的代码中放置类的固定版本。与库中相同的包和当然相同的类名:
com.mindfusion.common.ExtendedArrayList
也许你必须配置类加载器来加载你的类,而不是先在库中查找错误的类。像“父亲优先”这样的选项或者只是在调用库之前从代码中访问一次类就可以达成交易。
我看到您的程序加载不受您控制。
执行以下步骤:
包括 罐(javassist)该程序可以改变 "sort"
方法名称可以避免覆盖排序方法的任何其他名称
加载 罐子的 (javassist)主类通过反射 class.forName("");
在程序的主要方法的开头
打电话给 罐子的 (javassist)方法在方法上执行所需的更改
这样你就可以确定任何jar(javassist)已经加载并且可以使用了。