问题描述
我正在使用Java进行ERP系统的自定义。在我的自定义中,我想使用Apache POI 3.10.1。因此我整合了罐子poi-3.10.1-20140818.jar和poi-ooxml-3.10.1-20140818.jar。
I am working on a customization for an ERP system in Java. In my customization I want to use Apache POI 3.10.1. Therefore I have integrated the jars poi-3.10.1-20140818.jar and poi-ooxml-3.10.1-20140818.jar.
但是,这些罐子包含几个类这已经包含在ERP系统的核心代码中,但有差异。
However, these jars contains several classes that are already included in the core code of the ERP System, but have differences.
如果核心ERP类覆盖POI类,则自定义会引发运行时异常。如果POI类覆盖核心类,核心功能可能会发生相同的情况。
If the core ERP classes override the POI classes, the customization throws a Runtime exception. Possibly the same will happens with a core functionality if the POI classes override the core classes.
处理这类问题的最佳做法是什么?
What is a best practice for dealing with a problem like this?
我的定制是一个相对孤立的功能。
My customization is a relatively isolated functionality.
推荐答案
有两种方法可以解决这个问题:
There are two approaches to solving this problem:
-
您可以将库与
ClassLoader
隔离开来加载其他版本的POI。现在,我假设ERP系统位于类路径上,因此您需要将库与系统类加载器隔离开来。您可以通过创建然后指向包含较新版本POI的jar文件。确保还添加例如commons-codec以避免类加载问题。另请注意,瞬态依赖性本身可能具有瞬态依赖性。
You can isolate the library from the
ClassLoader
that loads the other version of POI. for now, I assume that the ERP system is on the class path such that you need to isolate the library from the system class loader. You can do so by creating a new instance of anURLClassLoader
which you then point to the jar files containing the newer version of POI. Make sure to also add all transient dependencies such as for example commons-codec to avoid class loading issues. Also, note that transient dependencies can have transient dependencies by themselves.
为了从类加载器隐藏类路径,您可以将引导类加载器设置为直接父级,由 null :
In order to hide the class path from a class loader, you would set the bootstrap class loader as a direct parent which is represented by null
:
new URLClassLoader(new URL[]{ new URL("poi-3.10.1-20140818.jar"), ... }, null);
使用此类加载器,您可以通过类似
With this class loader, you can query for the newer version POI classes by something like
Class.forName("org.apache.poi.hssf.usermodel.HSSFWorkbook", true, urlClassLoader);
用于检索新版本的 HSSFWorkbook
。但请注意,文字的任何对 HSSFWorkbook
的直接引用都将由执行类的类加载器解析,该类加载器当然会链接类的旧的,不兼容的版本。因此,您需要对所有代码使用反射。或者,您将一个类添加到 URLCLassLoader
,其中包含您的所有逻辑,并且仅通过反射调用此类。总的来说,这是一种更清洁的方法。例如,您可以添加一个实现引导类的类,例如 Callable
,然后您可以从任何不同的上下文中使用它,例如:
for retreiving the new version of the HSSFWorkbook
. Note however that any direct reference to HSSFWorkbook
by a literal would be resolved by the class loader of the executing class which would of course link the old, incompatible version of a class. Thus, you need to use reflection for all your code. Alternatively, you add a class to the URLCLassLoader
which contains all your logic and only invoke this class via reflection. This is a cleaner approach, in general. For example, you could add a class that implements a bootstrap class such as Callable
which you then can use from any different context as for example:
Callable<File> sub = (Callable<File>) Class.forName("pkg.Subroutine",
true,
urlClassLoader);
File convertedFile = sub.call();
或者,您可以将第二个POI依赖关系重新打包到另一个名称空间。执行此操作后,类不再冲突,因为它们的名称不再相等。这可能是一种更简洁的方法,因为您可以使用来自同一类加载器的两个库,并避免反射。
Alternatively, you can repackage the second POI dependency into another name space. After doing this, the classes are not conflicting anymore as their names are not longer equal. This is probably a cleaner approach as you can then use both libraries from the same class loader and you avoid reflection.
为了将依赖项重新打包到另一个名称空间,有一些工具,如可以帮助您完成此任务。 的替代方案是或 //www.gradle.org/rel =nofollow> Gradle 。
For repackaging a dependency into another name space, there are tools like the Maven Shade plugin that can help you with this task. Alternatives are jarjar for ant or the Shadow plugin for Gradle.
这篇关于如何包含同一依赖项的两个不同版本?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!