How to include two different versions of the same dependency?

穿精又带淫゛_ 提交于 2019-12-02 05:33:09

问题


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.

However, these jars contains several classes that are already included in the core code of the ERP System, but have differences.

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.


回答1:


There are two approaches to solving this problem:

  1. 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 an URLClassLoader 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.

    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);
    

    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();
    
  2. 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.

    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.




回答2:


If you are using Servlet 3.0 API and you can change some configuration, "web fragments" can be utilized for such kind of situation. Following is the explanation: http://www.oracle.com/technetwork/articles/javaee/javaee6overview-part2-136353.html#webfrags



来源:https://stackoverflow.com/questions/25989409/how-to-include-two-different-versions-of-the-same-dependency

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!