如何解决更深层次的jar-hell?

时间:2013-04-12 13:39:55

标签: java jar osgi

我有两个java项目Test1和Test2。 Test1在其eclipse Java Build Path中添加了一个库lib。 Test2在其构建路径中添加了一个jar。

Test1      Test2
/src        /src
lib         Referenced Libraries
 x-1.1.jar   a.jar
 y.jar        x-1.2
              z

Test1中的代码调用y.jar的函数/类,它依赖于x-1.1.jar。我不知道y.jar使用了x-1.1.jar的哪些函数/类,因为我没有jar的源代码。类似地,Test2调用a.jar的z包的函数/类,它依赖于jar的x-1.2版本。

现在我有一个Test项目,我需要项目Test1和Test2

Test
/src
 Test1 code
 Test2 code
 some other code which uses Test1 as well as Test2 libraries
lib
 x-1.1.jar
 y.jar
Referenced Libraries
 a.jar
  x-1.2
  z 

现在,当我运行Test项目时,我陷入了jar-hell的境地。我的研究中使用的两种方法是:

  1. Classpath:这种方法的问题在于,因为Test1,Test2的库/ jar都被添加到eclipse Java Build Path中,所以只访问x.jar的第一个加载版本,即使使用类加载器,Test2代码也会中断
  2. osgi:方法问题是我只能从osgi包中导出Test1和Test2的src文件夹中的包,而不能导出项目引用的包。但Test项目的代码使用Test1和Test2库。
  3. 希望我足够清楚。任何帮助是极大的赞赏。提前谢谢。

    在问题中添加更多信息: 我必须在我的项目中使用两个不同的java sdks,它们捆绑了不同的jar文件。矛盾的是:

    jar file                     Test1 ver  Test2 ver
    org.apache.commons.codecs    1.3        1.6
    org.apache.commons.logging   1.1.1      1.1.1
    org.apache.log4j             1.2.7      1.2.15
    httpclient                   4.1.1      4.0.3   
    httpcore                     4.1        4.1.4
    

    有什么方法可以做到这一点?

2 个答案:

答案 0 :(得分:1)

OSGI实际上很可能是一种解决方案,您有两种方法可以做到:

  1. 将依赖的jar转换为osgi包,定义它们导出的包的哪个版本(只是使用实际版本号)引用(OSGI方式)它们来自您的测试项目,当导入包指定确切的所需版本时。这样您就可以为每个库jar和每个项目创建osgi包。
  2. 创建两个osgi包:test1和test2。嵌入他们所需的库版本(作为嵌入式jar或内联)并且不导出依赖包。您可以从测试包中自由引用test1和test2。它们不会发生冲突,但会驻留在独立的类加载器(bundle classloader space)中。有关如何使用maven bundle插件嵌入依赖项的信息: http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html#ApacheFelixMavenBundlePlugin%28BND%29-EmbedDependencyandExportPackage
  3. 强调这种配置:

    <Embed-Dependency>
        *;scope=compile|runtime;inline=false
    </Embed-Dependency>
    

    如何使用BND工具将jar包装到osgi:http://java.dzone.com/articles/how-creategenerate-osgi

    顺便提一下,大多数(如果不是全部)提到的库应该已经准备好osgi,所以你甚至不需要将它们转换为osgi包。 这里唯一可能出现的问题是某些jar与osgi不兼容(例如那些对容器的类加载器架构做出假设的jar),但这种情况很少见。

答案 1 :(得分:0)

您可以通过实现一个带有两个URLClassLoader的自定义类加载机制来尝试模拟类路径分离,就像OSGi一样,每个x-1.*.jar一个。{/ p>

这是一般的想法:

File x1jar = new File("path/to/x-1.1.jar");
URLClassLoader x1loader = new URLClassLoader (x1ar.toURL(), this.getClass().getClassLoader());

File x2jar = new File("path/to/x-1.2.jar");
URLClassLoader x2loader = new URLClassLoader (x2ar.toURL(), this.getClass().getClassLoader());

// Test1 should look into x-1.1.jar ..
Class test1class = Class.forName("Test1", true, x1loader);

// .. Test2 should look into x-1.2.jar ..
Class test2class = Class.forName("Test2", true, x2loader);

// .. but both should see y.jar and z.jar via system class locader.

// Invoke whatever methods in testXclasses.

重要: x-1.1.jarx-1.2.jar不得在系统类路径中,即您应将它们放在单独的lib文件夹中,即您的类部署应如下所示:

+ myapp
  + lib
    myapp.jar
    y.jar
    z.jar
  + sandbox
    x-1.1.jar
    x-1.2.jar

必须直接引用Test1 / Test2类(使用Test1Test1.class),只能使用类名(在字符串"Test1"中)

查看this answerthis one