简体   繁体   English

大型Java系统依赖管理

[英]Large Java System Dependency Management

We have a large (>500,000 LOC) Java system that depends on 40-50 OSS packages. 我们有一个大型(> 500,000 LOC)Java系统,它依赖于40-50个OSS包。 The system is built with Ant, and dependency management is handled manually at present. 系统使用Ant构建,目前手动处理依赖关系管理。 I'm investigating Ivy and/or Maven to automate dependencies. 我正在调查Ivy和/或Maven以自动化依赖关系。 We looked at Maven as a build automation system last year and rejected it because it would require totally restructuring our system to match Maven's architecture. 去年我们将Maven看作是一个构建自动化系统并拒绝它,因为它需要完全重构我们的系统以匹配Maven的架构。 Now I'm looking to automate just the dependency management tasks. 现在我正在寻求自动化依赖管理任务。

I've done some experimentation with Ivy but have run into problems. 我已经对常春藤进行了一些实验,但遇到了问题。 For example, when I specify ActiveMQ as a dependency, and tell Ivy to use the POMs in the Maven repository for dependency specification, Ivy retrieves a bunch of packages (Jetty, Derby and Geronimo for instance) that I know aren't needed to just use ActiveMQ. 例如,当我将ActiveMQ指定为依赖项,并告诉Ivy使用Maven存储库中的POM进行依赖项规范时,Ivy检索了一堆我知道不需要的包(例如Jetty,Derby和Geronimo)使用ActiveMQ。

If I set usepoms="false" in ivysettings.xml it fetches only activemq.jar, but that seems to defeat the purpose of Ivy and relegates it to a simple jar-fetcher with manually-built dependency specifications. 如果我在ivysettings.xml中设置usepoms =“false”,它只获取activemq.jar,但这似乎打败了Ivy的目的,并将其降级为具有手动构建的依赖规范的简单jar-fetcher。

There's a bigger issue here, what used to be called "DLL Hell" in Windows. 这里有一个更大的问题,在Windows中曾经被称为“DLL Hell”。 In some cases, two direct first-level dependencies will point to different versions of the same transitive dependency (for instance log4j.jar). 在某些情况下,两个直接的第一级依赖项将指向相同传递依赖项的不同版本(例如log4j.jar)。 Only one log4j.jar can be in the classpath, so dependency resolution involves manually determining which version is compatible with all of its clients in our system. 只有一个log4j.jar可以在类路径中,因此依赖性解析涉及手动确定哪个版本与我们系统中的所有客户端兼容。

I guess it all boils down to the quality of each package's dependency specification (the POM). 我想这一切都归结为每个包的依赖规范(POM)的质量。 In the case of ActiveMQ, there are no scope declarations, so any reference to ActiveMQ will download all of its dependencies unless we manually exclude the ones we know we don't want. 在ActiveMQ的情况下,没有范围声明,因此任何对ActiveMQ的引用都将下载其所有依赖项,除非我们手动排除我们不知道的那些依赖项。

In the case of log4j, automatic dependency resolution would require that all of log4j's clients (other packages that depend on log4j) validate against all prior versions of log4j and provide a range (or list) of compatible log4j versions in the POM. 在log4j的情况下,自动依赖性解析将要求所有log4j的客户端(依赖于log4j的其他包)对所有先前版本的log4j进行验证,并在POM中提供兼容的log4j版本的范围(或列表)。 That's probably too much to ask. 这可能太多了。

Is this the current state of affairs, or am I missing something? 这是目前的状况,还是我错过了什么?

You're absolutely right in saying that 你这么说是对的

I guess it all boils down to the quality of each package's dependency specification (the POM). 我想这一切都归结为每个包的依赖规范(POM)的质量。

The only thing I would add is to view the POM, or any other form of metadata, as a starting point. 我要添加的唯一内容是将POM或任何其他形式的元数据视为起点。 It's quite useful that eg ActiveMQ provides all the dependencies for you, but it's up to you to choose if it actually does suit your project. 它非常有用,例如ActiveMQ为您提供所有依赖项,但是您可以选择它是否确实适合您的项目。

After all, even taking the log4j version into account, would you have an external dependencies pick the version or choose the version you know works for you? 毕竟,即使考虑到log4j版本,您是否有外部依赖选择版本或选择您知道适合您的版本?


As for how you can choose to tailor dependencies, here's what you can do with Ivy: 至于你如何选择定制依赖关系,这里是你可以用常春藤做的:

Unneeded packages 不需要的包裹

Ivy retrieves a bunch of packages (Jetty, Derby and Geronimo for instance) that I know aren't needed to just use ActiveMQ. Ivy检索了一堆我知道不需要使用ActiveMQ的软件包(例如Jetty,Derby和Geronimo)。

This usually happens because of poor modularity in the application. 这通常是因为应用程序中的模块性差。 Some part of the application needs Jetty for instance, but you end up with this transitive dependency even if you don't use it. 例如,应用程序的某些部分需要Jetty,但即使您不使用它,也最终会产生这种传递依赖性。

You probably want to look into the ivy exclude mechanism : 您可能想要查看常春藤排除机制

<dependency name="A" rev="1.0">
  <exclude module="B"/>
</dependency>

Dependency versions 依赖版本

Only one log4j.jar can be in the classpath, so dependency resolution involves manually determining which version is compatible with all of its clients in our system. 只有一个log4j.jar可以在类路径中,因此依赖性解析涉及手动确定哪个版本与我们系统中的所有客户端兼容。

Perhaps I'm misreading this, but there is no manual element in Ivy's conflict resolution. 也许我误解了这一点,但是常春藤的冲突解决方案中没有手动元素。 There is a list of default conflict managers : 有一个默认冲突管理器列表:

  • all : this conflicts manager resolve conflicts by selecting all revisions. all :此冲突管理器通过选择所有修订来解决冲突。 Also called NoConflictManager, it does evict any module. 它也称为NoConflictManager,它可以驱逐任何模块。
  • latest-time : this conflict manager selects only the 'latest' revision, latest being defined as the latest in time. 最新时间 :此冲突管理器仅选择“最新”版本,最新版本定义为最新版本。 Note that latest in time is costly to compute, so prefer latest-revision if you can. 请注意,最新的计算成本很高,因此如果可以,请选择最新版本。
  • latest-revision : this conflict manager selects only the 'latest' revision, latest being defined by a string comparison of revisions. 最新修订版 :此冲突管理器仅选择“最新”修订版,最新版本由修订版的字符串比较定义。
  • latest-compatible : this conflict manager selects the latest version in the conflicts which can result in a compatible set of dependencies. 最新兼容 :此冲突管理器选择冲突中的最新版本,这可能导致一组兼容的依赖项。 This means that in the end this conflict manager does not allow any conflict (like the strict conflict manager), except that it follows a best effort strategy to try to find a set of compatible modules (according to the version constraints); 这意味着最终这个冲突管理器不允许任何冲突(比如严格的冲突管理器),除了它遵循尽力而为的策略来尝试找到一组兼容的模块(根据版本约束);
  • strict : this conflict manager throws an exception (ie causes a build failure) whenever a conflict is found. strict :每当发现冲突时,此冲突管理器都会抛出异常(即导致构建失败)。

If needed, you can provide your own conflict manager . 如果需要,您可以提供自己的冲突管理器

Thats pretty much it. 几乎就是这样。 The maven dependency system (which Ivy more or less follows) leaves it up to the individual projects to do a good job of adding the necessary meta data for their dependencies. maven依赖系统(Ivy或多或少遵循)将它留给单个项目来做好为依赖项添加必要的元数据。 Most don't. 大多数人没有。

If you go that route, expect to spend time setting up exclusions. 如果你走这条路,期望花时间设置排除。

To the posters recommending OSGi, the OP said that he is not willing to re-architect his build system for Maven, I wouldn't think he would want to re-architect his application to be OSGi compliant. 对于推荐OSGi的海报,OP表示他不愿意为Maven重新构建他的构建系统,我不认为他会想要重新构建他的应用程序以符合OSGi。 Furthermore, a lot of OSS projects that are OSGi compliant (and there are not as many as you'd hope) have as bad or worse meta data than in Maven 此外,许多符合OSGi标准的OSS项目(并且没有您希望的那么多)具有与Maven相比更糟或更差的元数据

Of the dependencies you list, the following are defined as optional in the activemq-core pom (also see the relevant section from the Maven book). 在您列出的依赖项中,以下在activemq-core pom中定义为optional (另请参阅Maven书中的相关部分 )。

  • org.apache.derby:derby org.apache.derby:德比
  • org.apache.geronimo.specs:geronimo-jta_1.0.1B_spec org.apache.geronimo.specs:Geronimo的jta_1.0.1B_spec

I didn't see a direct dependency on Jetty, so it may be transitively included from one of the optional dependencies. 我没有看到对Jetty的直接依赖,因此可能会从一个可选的依赖项中传递它。

In Maven optional dependencies are handled automatically. 在Maven中,可选的依赖项是自动处理的。 Essentially any dependency that is declared optional must be redeclared in your pom for it to be used. 基本上任何声明为可选的依赖项必须在您的pom中重新声明才能使用它。 From the documentation linked above: 从上面链接的文档:

Optional dependencies are used when it's not really possible (for whatever reason) to split a project up into sub-modules. 当不可能(无论出于什么原因)将项目拆分为子模块时,使用可选的依赖项。 The idea is that some of the dependencies are only used for certain features in the project, and will not be needed if that feature isn't used. 我们的想法是,某些依赖项仅用于项目中的某些功能,如果不使用该功能,则不需要。 Ideally, such a feature would be split into a sub-module that depended on the core functionality project...this new subproject would have only non-optional dependencies, since you'd need them all if you decided to use the subproject's functionality. 理想情况下,这样的功能将被拆分为一个依赖于核心功能项目的子模块......这个新的子项目只有非可选的依赖项,因为如果你决定使用子项目的功能,你需要它们。

However, since the project cannot be split up (again, for whatever reason), these dependencies are declared optional. 但是,由于项目无法拆分(无论出于何种原因),因此这些依赖项被声明为可选项。 If a user wants to use functionality related to an optional dependency, they will have to redeclare that optional dependency in their own project. 如果用户想要使用与可选依赖项相关的功能,则必须在自己的项目中重新声明该可选依赖项。 This is not the most clear way to handle this situation, but then again both optional dependencies and dependency exclusions are stop-gap solutions. 这不是处理这种情况最明确的方法,但是可选的依赖关系和依赖关系排除都是止损解决方案。

I'm not sure if you can configure Ivy to ignore the optional dependencies, but you can configure it to exclude dependencies . 我不确定您是否可以配置Ivy来忽略可选的依赖项,但您可以将其配置为排除依赖项 For example: 例如:

<dependency name="A" rev="1.0">
  <exclude module="B"/>
</dependency>

This isn't entirely satisfactory I know. 我知道这并不完全令人满意。 It may be that Ivy does support optional dependencies (I will have a further look and update if I find anything), but the exclusions mechanism at least allows you to manage them. 可能是Ivy确实支持可选的依赖项(如果我发现任何内容,我会进一步查看和更新​​),但排除机制至少允许您管理它们。


Regarding the last part of your question. 关于你问题的最后部分。 Maven will resolve the dependency versions for log4j and if the versions are compatible it will automatically select the 'nearest' of the listed versions. Maven将解析log4j的依赖版本,如果版本兼容,它将自动选择所列版本的“最近”版本。

From the Introduction to the Dependency Mechanism : 介绍到依赖机制

  • Dependency mediation - this determines what version of a dependency will be used when multiple versions of an artifact are encountered. 依赖关系中介 - 确定在遇到工件的多个版本时将使用哪个版本的依赖关系。 Currently, Maven 2.0 only supports using the "nearest definition" which means that it will use the version of the closest dependency to your project in the tree of dependencies. 目前,Maven 2.0仅支持使用“最接近的定义”,这意味着它将在依赖树中使用与项目最接近的依赖项版本。 You can always guarantee a version by declaring it explicitly in your project's POM. 您可以通过在项目的POM中明确声明它来保证版本。 Note that if two dependency versions are at the same depth in the dependency tree, until Maven 2.0.8 it was not defined which one would win, but since Maven 2.0.9 it's the order in the declaration that counts: the first declaration wins. 请注意,如果两个依赖关系版本在依赖关系树中处于相同的深度,则直到Maven 2.0.8没有定义哪一个会赢,但是自Maven 2.0.9开始,它就是声明中的顺序:第一个声明获胜。

    • "nearest definition" means that the version used will be the closest one to your project in the tree of dependencies, eg. “最接近的定义”表示所使用的版本将是依赖树中与项目最接近的版本,例如。 if dependencies for A, B, and C are defined as A -> B -> C -> D 2.0 and A -> E -> D 1.0, then D 1.0 will be used when building A because the path from A to D through E is shorter. 如果A,B和C的依赖关系被定义为A - > B - > C - > D 2.0和A - > E - > D 1.0,则在构建A时将使用D 1.0,因为从A到D的路径是E更短。 You could explicitly add a dependency to D 2.0 in A to force the use of D 2.0 您可以在A中向D 2.0明确添加依赖项以强制使用D 2.0

Where the versions are not compatible you have a bigger problem than dependency resolution. 如果版本不兼容,则问题比依赖项解决更大。 I believe Ivy operates on a similar model but I am no expert. 我相信常春藤在类似的模型上运作,但我不是专家。

I think this is indeed the current state of affairs. 我认为这确实是当前的事态。 OSGi and the proposed new packaging system for java 1.7 (has discussion on that one come to a conclusion already?) are attempts at fixing at least the depending-on-different-versions-of-a-library issue, but I don't think they will be able to fix your issue right now. OSGi和java 1.7的新包装系统(已经讨论过这个问题已经得出结论吗?)试图至少修复一个不同版本的库的问题,但我不这样做认为他们现在能够解决你的问题。

I'm currently using Ivy to manage more than 120 OSS and proprietary libraries for several projects (some standalone, some dependents). 我目前正在使用Ivy为几个项目(一些独立的,一些家属)管理120多个OSS和专有库。 Back in 2005 (when Ivy was still from Jayasoft) I decided (or had to) to write the ivy.xml files for each integrated package. 早在2005年(当时Ivy仍然来自Jayasoft)我决定(或不得不)为每个集成包编写ivy.xml文件。

The biggest advantage is that I have full control over the various configurations. 最大的优点是我可以完全控制各种配置。 This may sound overkill for some but our build system has been working reliably for over 4 years now and adding a new library is typically a 5 minutes job. 对于一些人来说这可能听起来有点过头了,但是我们的构建系统已经可靠地工作了4年多,并且添加新库通常是5分钟的工作。

"Is this the current state of affairs?" “这是目前的状况吗?”

Not with OSGi. 不是OSGi。 You may want to look at OSGi containers and bundles. 您可能想要查看OSGi容器和包。 A bundle is like a jar file, but it supports meta data detailing its version, and the versions of related bundles it requires (by putting attributes in the META-INF file). bundle就像一个jar文件,但它支持详细描述其版本的元数据,以及它所需的相关bundle的版本(通过将属性放在META-INF文件中)。 So your log4j bundle will indicate its version, and dependent bundles will detail what versions of log4j they require. 因此,您的log4j包将指示其版本,依赖包将详细说明它们所需的log4j版本。

Furthermore, a non-hierarchical classloader mechanism is supported, such that you can load multiple versions of log4j, and different bundles can specify, and bind to, those different versions. 此外,支持非分层类加载器机制,这样您就可以加载多个版本的log4j,不同的bundle可以指定并绑定到这些不同的版本。

Javaworld has a very good introduction here . Javaworld 在这里有一个非常好的介绍。

There is the whole idea of dependency injection - which would invariably lead to the program needing to be restructured. 有依赖注入的整个想法 - 这总是会导致程序需要重组。 I have been hearing some noise about GUICE being good in this regard. 我听到一些关于GUICE在这方面表现良好的声音。 In deployment perspective I have had reasonable success with deploying only the .jar we built with dependency .jars being fetched from original projects via jnlp. 在部署透视图中,我已经取得了合理的成功,只部署了我们构建的.jar,并通过jnlp从原始项目中获取依赖项.jars。 The build system behind this involved manual tracking of new versions of dependencies and updating in the build system. 这背后的构建系统包括手动跟踪新版本的依赖项并在构建系统中进行更新。

"Is this the current state of affairs" “这是目前的状况”

Yes. 是。

This is the open-source tradeoff. 这是开源权衡。

A closed-source framework (ie, .Net) will solve all of this for you. 闭源框架(即.Net)将为您解决所有这些问题。

An open source solution means you have to solve it (and resolve it) all the time. 开源解决方案意味着您必须始终解决(并解决它)。

You might be able to find some pre-built configurations and pay someone to keep those up-to-date. 您可能能够找到一些预先构建的配置并向某人付费以保持这些配置的最新状态。 For example, you could elect to use Red Hat Enterprise Linux. 例如,您可以选择使用Red Hat Enterprise Linux。 If you stick to precisely what they support (and nothing more) then configuration is solved. 如果你坚持他们所支持的东西(仅此而已),那么配置就会得到解决。

Odds are good, however, that no packaged configuration meets your requirements. 但是,没有包装配置符合您的要求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM