2014-09-23 159 views
2

我正在为Java中的ERP系统进行定制。在我的定制中,我想使用Apache POI 3.10.1。因此,我整合了罐子poi-3.10.1-20140818.jar和poi-ooxml-3.10.1-20140818.jar。如何包含两个不同版本的相同依赖项?

但是,这些罐子包含几个已经包含在ERP系统核心代码中的类别,但有所不同。

如果核心ERP类覆盖了POI类,则定制会引发运行时异常。如果POI类覆盖核心类,则核心功能可能也会发生同样的情况。

处理类似问题的最佳做法是什么?

我的自定义是一个相对独立的功能。

回答

5

有两种方法来解决这个问题:

  1. 您可以从加载POI的其他版本的ClassLoader库隔离。现在,我假设ERP系统在类路径上,这样您需要将库与系统类加载器隔离。你可以通过创建一个URLClassLoader的新实例来实现,然后你指向包含更新版本POI的jar文件。确保还要添加all transient dependencies,例如commons-codec以避免类加载问题。另外请注意,瞬态依赖关系本身可能具有瞬态依赖关系。

    为了隐藏一个类加载器的类路径中,您将设置引导类加载器作为由null代表的直接父:

    new URLClassLoader(new URL[]{ new URL("poi-3.10.1-20140818.jar"), ... }, null); 
    

    有了这个类加载器,你可以查询该较新版本的POI类似

    Class.forName("org.apache.poi.hssf.usermodel.HSSFWorkbook", true, urlClassLoader); 
    

    用于检索新版本的HSSFWorkbook。但是请注意,任何直接引用HSSFWorkbook的文字都可以通过执行类的类加载器来解决,该类加载器当然会链接旧的不兼容版本的类。因此,您需要对所有代码使用反射。或者,您可以向URLCLassLoader添加一个包含所有逻辑的类,并仅通过反射调用此类。一般而言,这是一种更清洁的方法。例如,您可以添加一个类实现的自举类如Callable你那么可以从任何不同的上下文中使用,例如:或者

    Callable<File> sub = (Callable<File>) Class.forName("pkg.Subroutine", 
                    true, 
                    urlClassLoader); 
    File convertedFile = sub.call(); 
    
  2. ,你可以重新打包第二POI依赖到另一个名字空间。做完这些之后,这些类不再相互冲突,因为它们的名称不再相同。这可能是一种更清洁的方法,因为您可以使用来自同一个类加载器的两个库,并避免反射。

    要重新打包一个依赖项到另一个名称空间中,有一些工具可以帮助您完成此任务,如Maven Shade plugin。对于antShadow pluginGradle,替代方案为jarjar

相关问题