如何在另一个Spring项目中消耗一个具有较高Spring版本的Spring包依赖项的jar?

huangapple go评论76阅读模式
英文:

How to consume a jar that has spring as a packaged dependency in another spring project with higher spring version?

问题

以下是翻译好的部分:

有很多关于如何将一个 Spring 项目添加为另一个 Spring 项目的依赖的问题。但我正在寻找的是,在我们成功回答了“如何”部分之后,解决特定问题的方法。

假设我们有两个基于 Spring 的多模块 Maven 项目:Project-A 和 Project-B。
Project-A 中使用的 SpringFramework 版本是 4.2.3。从 Project-A 中提取出一些模块,使用 shade-plugin 构建成 uber-jar,没有特殊配置。这意味着,如果我打开这个 uber-jar,我可以找到所有的依赖项(我认为甚至包括传递性依赖项)都打包到了 uber-jar 中。(当然,这可能不太好看,但我不能改变这个 uber-jar。出于各种原因,重构并将其拆分为更小的可消耗依赖项是不可行的)。通过 uber-jar 中的一个模块公开的 API 访问底层资源。所以我被困在这个情况下。

现在,我想要在 Project-B 中将此 uber-jar 用作依赖项。我使用了 install 插件,并在验证阶段附加了安装此 uber-jar。

然而,在 Project-B 中,我希望使用 spring-data-jdbc。现在,spring-data-jdbc 是一个相当新的项目,正如在这里 的参考文档中所述,所需的最低 spring-framework 版本是 5.2.6.RELEASE。

因此,当我将 uber-jar 添加为依赖项时,尽管我可以成功地进行 maven 清理安装,但在将构件部署到 tomcat 8.5 时,我遇到了一些 NoSuchMethodException,如下所示:

java.lang.nosuchmethoderror:org.springframework.util.reflectionutils.accessibleconstructor(ljava/lang/class;[ljava/lang/class;)

这与 这个问题 中描述的问题类似。因此,我使用 maven 依赖插件查看了 Project-B 的依赖树,但我无法看到列出 spring 版本 4.2.3。在 uber-jar 下没有列出任何传递性依赖项。

我甚至查看了不同目标下的构建类路径,仍然只能找到 Project-B 中列出的 spring 版本 5.2.6.RELEASE。然而,很明显在部署过程中,我只从 uber-jar 中使用 spring-farmework 4.2.3。因为无法找到的方法仅从 spring 5.0 开始提供,如此处 的 Java 文档中所述。

升级 Project-A 的 spring 版本是不可能的(同样出于 x、y、z 原因)。我尝试从 uber-jar 添加 uber-jar 时排除所有依赖项,但没有效果。

问题:

  1. 我们能否告诉 Project-B 在使用其项目中的 spring-framework 依赖项(5.2.6.RELEASE)时不使用 uber-jar 中的依赖项,同时让 uber-jar 中的类使用 uber-jar 中的 spring-framework?
  2. 甚至提出上面的问题令人不安,因为我认为我知道两个版本都被添加到了类路径中。并且在部署过程中,容器会选择它看到的第一个版本,从而引发这个问题。这是正确的吗?
  3. 在我描述的模型中(如构建 uber-jar),依赖关系管理是如何工作的?也就是说,因为我可以看到所有的依赖关系都打包到了 uber-jar 中,当我将这个 uber-jar 用作依赖项时,所有的打包依赖关系(jar)是否也会添加到使用项目的类路径中?
  4. 在验证阶段构建 Project-B 时,我正在(Maven)安装 uber-jar,然后编译 Project-B,这是否导致了问题?
  5. 如果这是一个混乱的情况,我能否得到一个替代 spring-data-jdbc 的建议?以便我完全放弃在 Project-B 中使用更高版本的 spring。我不需要 Java 持久性 API 的所有功能,如延迟加载、缓存等等。我只想使用仓库模式来轻松执行一些 CRUD 操作,以及简单的事务管理,这就是我选择 spring-data-jdbc 的原因。

我已经花了一整天的时间阅读、学习和研究有关 Maven 依赖解析、shade 插件工作原理等等的内容。感觉就像一个我无法整合的大拼图。谢谢任何指导。

更新:因此,依赖树如下所示:

  • Project-A,Uber-Jar 模块的依赖树

org.railomaya.uberJarModule:uberJar:jar:2.0
[INFO] +- org.railomaya:OtherModule_1:jar:2.0
[INFO] - org.railomaya:OtherModule_2:jar:1.0:compile
[INFO] +- org.railomaya:otherModule_3:jar:1.0-SNAPSHOT:compile
[INFO] | +- org.springframework:spring-jms:jar:4.2.3.RELEASE:compile
[INFO] | | +- org

英文:

There are numerous questions on 'How to' add one spring project as a dependency into another spring project. But what I am looking for is solving a specific problem once we have successfully answered the 'How to' part.

Say we have two spring based multi-module maven projects. Project-A and Project-B.
SpringFramework version used in Project-A is 4.2.3. Some modules from Project-A are combined and built into a uber-jar using the shade-plugin with NO special configuration. What this means is, if I open the uber-jar, I can find all the dependencies (I think even the transitive dependencies) are packaged into the uber-jar. (Sure, this might be ugly, but I cannot change this uber-jar. Refactoring and breaking it up into smaller consumable dependencies is out of question for x,y,z reasons). Access to underlying resources is through the API's that one of the modules in uber-jar exposes. So I am stuck with this.

Now, I want to use this uber-jar from project-A as a dependency in project-B. I used the install plugin and attached installing this uber-jar during validate phase.

However, in Project-B I wish to use spring-data-jdbc. Now, spring-data-jdbc is a fairly new project and as described here in the reference doc, the minimum spring-framework version required is 5.2.6.RELEASE.

So when I added the uber-jar as the dependency, although I could do a successful maven clean install, I ran into some NoSuchMethodException like this:
java.lang.nosuchmethoderror:org.springframework.util.reflectionutils.accessibleconstructor(ljava/lang/class;[ljava/lang/class;) when the artifact was being deployed into tomcat 8.5

Which is similar to the problem described in this question. So using maven dependency plugin I looked at the dependency tree of Project-B, but I could not see spring-version 4.2.3 being listed. No transitive dependencies were being listed under the uber-jar.

I even looked at the build classpath under different goals and still could only find spring-version 5.2.6.RELEASE from Project-B being listed. However, it is pretty clear that during deployment, I am only consuming spring-farmework 4.2.3 from the uber-jar. Because the methid which cannot be found is available only from spring 5.0 onwards as described in the java-doc here

Upgrading the spring-version of Project-A is not possible (again for x,y,z reasons). I tried to exclude all dependencies from the uber-jar when adding the uber-jar as a dependency, but that did not help.

Questions:

  1. Can we tell project-B to use spring-framework dependency (5.2.6.RELEASE) of its project and not use
    the one from the uber-jar while letting the classes from project-A (in the uber jar) use
    spring-framework packaged within the uber-jar?
  2. Its unsettling to even ask above question because I think I know that both versions are being added onto the class path. And during deployment the container is picking up the first one it sees causing this problem. Is this correct?
  3. In the model I have described, (like building the uber-jar) how does dependency management work? Meaning, since I can see all the dependencies packaged into the uber-jar, when I use this uber-jar as a dependency, do all the packaged dependencies (jars) also get added to the consuming projects classpath?
  4. I am (maven) installing the uber-jar while builindg project-b during validate phase, and then compiling project-b, is this whats causing the problem?
  5. If this is a messed up situation, can I get a recommendation for an alternative to spring-data-jdbc? so that I can totally ditch the idea of using higher spring version in project-b. I don't need all the features of Java Persistence API like lazy loading, caching and all that. I just want to use the repository pattern to easily perform some CRUD operations with simple transaction management which is why I chose spring-data-jdbc.

I have spent an entire day reading, learning, researching everything from how maven dependency resolution happens, how the shade plugin works and so much more. Feels like one big puzzle that I cannot put it all together. Thanks for any guidance.

Update: So the dependency tree would look like below:

  • Project-A, Uber-Jar module dependency tree

> org.railomaya.uberJarModule:uberJar:jar:2.0
> [INFO] +- org.railomaya:OtherModule_1:jar:2.0
> [INFO] - org.railomaya:OtherModule_2:jar:1.0:compile
> [INFO] +- org.railomaya:otherModule_3:jar:1.0-SNAPSHOT:compile
> [INFO] | +- org.springframework:spring-jms:jar:4.2.3.RELEASE:compile
> [INFO] | | +- org.springframework:spring-messaging:jar:4.2.3.RELEASE:compile
> [INFO] | | - org.springframework:spring-tx:jar:4.2.3.RELEASE:compile
> [INFO] | - org.apache.activemq:activemq-spring:jar:5.13.2:compile
> [INFO] +- org.springframework:spring-context-support:jar:4.2.3.RELEASE:compile
> [INFO] | +- org.springframework:spring-beans:jar:4.2.3.RELEASE:compile
> [INFO] | +- org.springframework:spring-context:jar:4.2.3.RELEASE:compile
> [INFO] | | +- org.springframework:spring-aop:jar:4.2.3.RELEASE:compile
> [INFO] | | | - aopalliance:aopalliance:jar:1.0:compile
> [INFO] | | - org.springframework:spring-expression:jar:4.2.3.RELEASE:compile
> [INFO] | - org.springframework:spring-core:jar:4.2.3.RELEASE:compile
> [INFO] | - commons-logging:commons-logging:jar:1.2:compile
> [INFO] +- org.springframework.boot:spring-boot-starter:jar:1.5.1.RELEASE:compile
> [INFO] | +- org.springframework.boot:spring-boot:jar:1.5.1.RELEASE:compile
> [INFO] | +- org.springframework.boot:spring-boot-autoconfigure:jar:1.5.1.RELEASE:compile
> [INFO] | +- org.springframework.boot:spring-boot-starter-logging:jar:1.5.1.RELEASE:compile
> [INFO] | | +- ch.qos.logback:logback-classic:jar:1.1.9:compile
> [INFO] | | | - ch.qos.logback:logback-core:jar:1.1.9:compile
> [INFO] | | +- org.slf4j:jcl-over-slf4j:jar:1.7.22:compile
> [INFO] | | +- org.slf4j:jul-to-slf4j:jar:1.7.22:compile
> [INFO] | | - org.slf4j:log4j-over-slf4j:jar:1.7.22:compile
> [INFO] | - org.yaml:snakeyaml:jar:1.17:runtime
> [INFO] +- org.springframework:spring-test:jar:4.2.3.RELEASE:test
> [INFO] +- javax.servlet:servlet-api:jar:2.5:compile
> [INFO] +- org.apache.commons:commons-lang3:jar:3.3.1:compile
> [INFO] +- commons-io:commons-io:jar:2.4:compile

Project-B dependency tree where the above uber-jar is used as dependency:

org.railomaya:project-B:war:2.0-SNAPSHOT
[INFO] +- org.railomaya.uberJarModule:uberJar:jar:2.0:compile
[INFO] +- org.apache.commons:commons-dbcp2:jar:2.0.1:compile
[INFO] |  \- commons-logging:commons-logging:jar:1.1.3:compile
[INFO] +- org.apache.commons:commons-pool2:jar:2.2:compile
[INFO] +- org.apache.logging.log4j:log4j-api:jar:2.1:compile
[INFO] +- org.apache.logging.log4j:log4j-core:jar:2.1:compile
[INFO] +- org.apache.logging.log4j:log4j-web:jar:2.1:compile
[INFO] +- javax.mail:mail:jar:1.4.5:compile
[INFO] |  \- javax.activation:activation:jar:1.1:compile
[INFO] +- javax.servlet:servlet-api:jar:2.5:provided
[INFO] +- mysql:mysql-connector-java:jar:5.1.26:compile
[INFO] +- org.quartz-scheduler:quartz:jar:2.2.1:compile
[INFO] |  +- c3p0:c3p0:jar:0.9.1.1:compile
[INFO] |  \- org.slf4j:slf4j-api:jar:1.6.6:compile
[INFO] +- org.springframework:spring-beans:jar:5.2.6.RELEASE:compile
[INFO] +- org.springframework:spring-context:jar:5.2.6.RELEASE:compile
[INFO] |  \- org.springframework:spring-aop:jar:5.2.6.RELEASE:compile
[INFO] +- org.springframework:spring-context-support:jar:5.2.6.RELEASE:compile
[INFO] +- org.springframework:spring-core:jar:5.2.6.RELEASE:compile
[INFO] |  \- org.springframework:spring-jcl:jar:5.2.6.RELEASE:compile
[INFO] +- org.springframework:spring-expression:jar:5.2.6.RELEASE:compile
[INFO] +- org.springframework:spring-jdbc:jar:5.2.6.RELEASE:compile
[INFO] +- org.springframework:spring-test:jar:5.2.6.RELEASE:test
[INFO] +- org.springframework:spring-tx:jar:5.2.6.RELEASE:compile
[INFO] +- org.springframework:spring-web:jar:5.2.6.RELEASE:compile
[INFO] \- org.springframework:spring-webmvc:jar:5.2.6.RELEASE:compile

答案1

得分: 1

我们能否让项目B使用其项目中的spring-framework依赖项(5.2.6.RELEASE),而不使用来自uber-jar的依赖项,同时让来自项目A(在uber-jar中)的类使用嵌在uber-jar中的spring-framework?

不容易。请见下文。

我认为我知道两个版本都被添加到了类路径中。在部署过程中,容器会选择找到的第一个版本,从而引发此问题。这样说对吗?

是的。

当我将这个uber-jar作为一个依赖项时,所有打包的依赖项(jar包)是否也会被添加到使用该jar的项目的类路径中?

是的。

在验证阶段构建项目B时,我(使用Maven)安装了uber-jar,然后编译项目B,这是导致问题的原因吗?

是的。

如果这是一个混乱的情况,我能否得到一个替代spring-data-jdbc的建议?

您总是可以使用Spring的JdbcTemplate,并使用它自己实现您的存储库。
您甚至可以考虑像Spring Data JDBC一样对聚合进行建模。

回到第一个问题:

首先,让我们明确一点,不能保证项目A能够与更新的Spring版本一起工作,但假设它可以。

您需要做的是将uber-jar拆开,即移除所有不属于项目A本身的部分,并将其安装到Maven仓库中,然后依赖于该版本,并且还要依赖于正确版本(TM)的Spring和所有其他依赖项。

英文:

> Can we tell project-B to use spring-framework dependency (5.2.6.RELEASE) of its project and not use the one from the uber-jar while letting the classes from project-A (in the uber jar) use spring-framework packaged within the uber-jar?

Not easily. See below.

> I think I know that both versions are being added onto the class path. And during deployment the container is picking up the first one it sees causing this problem. Is this correct?

Yes.

> when I use this uber-jar as a dependency, do all the packaged dependencies (jars) also get added to the consuming projects classpath?

Yes.

> I am (maven) installing the uber-jar while builindg project-b during validate phase, and then compiling project-b, is this whats causing the problem?

Yes.

> If this is a messed up situation, can I get a recommendation for an alternative to spring-data-jdbc?

You can always use Springs JdbcTemplate and implement your repositories yourself using it.
You might even consider modelling aggregates as Spring Data JDBC does.

Back to the first question:

First lets make clear that there is no guarantee that Project A will work with a newer Spring version, but lets assume it does.

What you need to to is to take apart the ueber-jar i.e. remove everything that isn't Project A itself, and install that in you maven repository, then depend on that and also depend on the right version (TM) of Spring and all other dependencies.

huangapple
  • 本文由 发表于 2020年5月3日 14:57:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/61570729.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定