大公司如何解决程序包依赖性冲突问题?

时间:2018-11-01 10:33:49

标签: java maven classloader dependency-management

enter image description here 如图所示,一个应用程序(Java)引用了两个第三方程序包jar(packageA和packageB),并且分别引用了packageC-0.1和packageC-0.2。如果packageC-0.2与packageC-0.1兼容,它将很好地工作。但是有时packageA使用了packageC-0.2中不支持的内容,而Maven只能使用jar的最新版本。此问题也称为“ Jar Hell”。

实际上很难重写程序包A或迫使其开发人员将程序包C更新到0.2。

您如何解决这些问题?这通常在大型公司中发生。

  

我必须声明,这个问题主要发生在BIG公司中,这是因为大公司有很多部门,每当某些开发人员使用新功能时,让整个公司更新一个依赖关系将是非常昂贵的。一些依赖罐的新版本。对于小公司而言,这并不重要。

任何回应都会受到赞赏。

让我扔掉一块砖,以便首先获得宝石。

阿里巴巴是世界上最大的电子商务之一。我们通过创建一个名为Pandora的隔离容器来解决这些问题。它的原理很简单:将这些中间件打包在一起并用不同的ClassLoader加载它们,以便即使它们引用具有不同版本的相同包也可以很好地协同工作。但这需要由Pandora提供的运行时环境,该环境作为tomcat进程运行。我不得不承认这是一个沉重的计划。 Pandora是基于JVM通过类加载器和类名识别一个类的事实而开发的。

如果您知道某人可能知道答案,请与他/她分享链接。

6 个答案:

答案 0 :(得分:3)

我们是一家大公司,很多问题。我们有遍布多个开发人员组的大型依赖树。我们的工作:

  • 我们通过jar的维护者发布的BOM(“推荐版本”)的BOM(MavendependencyManagement列表)来管理版本。这样,我们确保使用了最新版本的工件。

  • 我们尝试通过将开发人员组内部使用的功能与其提供给其他组的功能分开来减少大型依赖树。

但是我承认我们仍在尝试寻找更好的策略。让我还提到,使用“微服务”是解决此问题的策略,但在许多情况下,这对我们而言不是有效的策略(主要是因为我们再也无法在数据库上进行全局事务处理了。)

答案 1 :(得分:2)

这是Java世界中的常见问题。

您最好的选择是定期维护和更新packageA和packageB的依赖项

如果您可以控制这些应用程序,请花些时间来做。如果您没有控制权,请要求供应商或作者进行定期更新。

如果内部使用packageA和packageB,则可以使用以下做法:让公司中的所有内部项目都引用定义了“最新”版本的Maven parent中的pom.xml常用的第三方库。

例如:

    <framework.jersey>2.27</framework.jersey>
    <framework.spring>4.3.18.RELEASE</framework.spring>
    <framework.spring.security>4.2.7.RELEASE</framework.spring.security>

因此,如果您的项目“ A”使用spring,并且他们使用公司的“父” pom的最新版本,则它们都应使用4.3.18.RELEASE。

发布并希望发行新版本的spring时,您将更新公司的父pom,并强制所有其他项目使用该最新版本。

这将解决许多依赖项不匹配的问题。

不用担心,这在Java世界中很常见,您并不孤单。只需在Google上搜索“ jar hell”,您就可以在更广泛的背景下理解该问题。

mvn dependency:tree是您隔离这些依赖性问题的朋友。

答案 2 :(得分:2)

我同意@JF Meier的回答,在Maven多模块项目中,在进行统一版本管理时,依赖项管理节点通常在父POM文件中定义。节点类声明的依赖项节点的内容与统一定义的资源版本有关。直接定义的依赖项节点中的资源不需要引入版本阶段。海关的内容如下:

在父pom中

<dependencyManagement> 
    <dependencies > 
      <dependency > 
        <groupId>com.devzuz.mvnbook.proficio</groupId> 
        <artifactId>proficio-model</artifactId> 
        <version>${project.version}</version> 
      </dependency > 
    </dependencies > 
  </dependencyManagement>

在您的模块中,您无需设置版本

<dependencies > 
    <dependency > 
      <groupId>com.devzuz.mvnbook.proficio</groupId> 
       <artifactId>proficio-model</artifactId> 
    </dependency > 
  </dependencies > 

这将避免不一致的问题。

答案 3 :(得分:1)

通常无法回答此问题。 过去,我们通常只是不使用不同版本的依赖项。如果版本被更改,则需要在团队/公司范围内进行重构。我怀疑大多数构建工具是否有可能。

但是要回答你的问题.. 简单的答案:不要在一个编译单元(通常是一个模块)中使用一个依赖项的两个版本

但是,如果确实需要执行此操作,则可以编写一个包装程序模块,以引用该库的旧版本。

但是我个人的观点是,在一个模块内不需要这些结构,因为“一个模块”应该相对较小以便于管理。否则,它可能是该项目可以使用一些模块化重构的有力指标。但是,我非常清楚,“大型公司”的某些项目可能会陷入混乱,而没有“好的”选择。我猜您正在谈论的情况是,packageA属于另一个团队,而不是packageB。由于缺乏分隔和固有的依赖性问题,这通常是一个非常糟糕的设计决策。

答案 4 :(得分:1)

首先,请尝试避免该问题。如@Henry的评论中所述,请勿将第三方库用于琐碎的任务。

但是,我们都使用库。有时,我们最终遇到您描述的问题,我们需要同一个库的两个不同版本。如果库'C'已删除并在两个版本之间添加了一些API,并且'A'需要删除的API,而'B'需要新的API,那么您就遇到了问题。

在我的公司中,我们在OSGi容器中运行Java代码。使用OSGi,您可以在“捆绑包”中模块化代码,捆绑包是jar文件,清单文件中带有一些特殊指令。每个捆绑罐都有自己的类加载器,因此两个捆绑可以使用同一库的不同版本。在您的示例中,您可以将使用“ packageA”的应用程序代码拆分为一个包,而将使用“ packageB”的应用程序拆分为另一个包。这两个捆绑包可以互相调用API,只要您的捆绑包在另一个捆绑包使用的方法签名中不使用“ packageC”类(称为API泄漏),它们就可以正常工作。

要开始使用OSGi,您可以例如看看OSGi enRoute

答案 5 :(得分:0)

让我扔掉一块砖头以获得宝石。

阿里巴巴是世界上最大的电子商务之一。我们通过创建一个名为Pandora的隔离容器来解决这些问题。它的原理很简单:将这些中间件打包在一起并用不同的ClassLoader加载它们,以便即使它们引用具有不同版本的相同包也可以很好地协同工作。但这需要由Pandora提供的运行时环境,该环境作为tomcat进程运行。我不得不承认这是一个沉重的计划。

Pandora是基于JVM通过类加载器和类名识别一个类的事实而开发的。