11

我正在使用 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() 变成了一个覆盖。它创建了一个无限递归,导致stackoverflow。我现在也可以用一小段代码重现这个问题。

  • 我们创建 JDiagram 对象的原始类在运行时由我们产品中的其他一些组件加载。我们对程序的加载几乎没有控制权。
  • 我们发现最新版本的 JDiagram 已通过将 sort() 替换为 sortJ7() 方法来解决此问题。但是,我们目前无法升级库。JDiagram 是一个许可的 API。
  • ExtendedArrayList 在内部由 JDiagram 实例化,因此我们无法从代码中更改它。

我们尝试了以下迄今为止不起作用的解决方案

  • Java 代理:因为我们的代码没有直接调用 ExtendedArrayList,而且 'Diagram' 也没有任何接口。
  • Spring AOP:我们没有使用 spring,而且我们的程序是由其他组件在运行时加载的。
  • AspectJ:到目前为止,这显然是一个解决方案。但是,它也不起作用,因为我们无法在运行时编织我们的程序。不确定是否有人可以使它工作。

请让我知道是否需要详细说明。欢迎任何帮助。谢谢。

更新 到目前为止,javassist 是最好的方法,但是 JDiagram 混淆会阻止解决方案正常工作。考虑到我们的发布日期,我们有点假设不可能(不得不说)修复。我们已经开始升级库的过程。同时从我们的应用程序中删除了一个由 routeAllLinks() 方法提供的小功能.. :-( 感谢大家的帮助。我将继续研究这个问题,因为我发现它非常有趣和具有挑战性.. 我'如果我能解决它,我会更新这篇文章。我会为@gontard 的 javassist 方法提供赏金,因为我正在继续我的研究。谢谢。

4

3 回答 3

6

我用一个基本的例子重现了你的问题:

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 兼容)版本。

于 2015-03-18T14:48:20.517 回答
2

考虑反编译库并自己解决问题。您可以使用此固定包作为解决方法。

另一种方法在您的代码中放置一个固定版本的类。与库中相同的包和相同的类名当然:

com.mindfusion.common.ExtendedArrayList

也许您必须配置类加载器来加载您的类,而不是首先在库中查找错误的类。诸如“父优先”之类的选项或仅在调用库之前从您的代码中访问该类一次都可以达成交易。

于 2015-03-24T12:23:45.110 回答
1

我看到你的程序的加载不是由你控制的。执行以下步骤:

  1. 包括程序的jar (javassist),该程序可以将"sort"方法名称更改为可以避免覆盖排序方法的任何其他名称

  2. 在程序的 main 方法的开头 通过反射加载jar 的(javassist)主类class.forName("");

  3. 调用jar 的(javassist) 方法以对方法执行所需的更改

通过这种方式,您可以确定任何 jar (javassist) 都已加载并可以使用。

于 2015-03-24T15:53:44.427 回答