Maven构建聚合工程以及jar包冲突解决使用总结

一、聚合工程

Maven构建聚合工程以及jar包冲突解决使用总结_第1张图片

如图所示:

SpringCloud_CH3为聚合工程,eurekaclientarticleservice为被聚合工程。

聚合工程为一个maven工程:聚合类的pom.xml文件为:

Maven构建聚合工程以及jar包冲突解决使用总结_第2张图片

标签为整个工程使用的springboot版本;标签中的值,每次新建一个Maven Modules,都会新增一个标签。

Maven构建聚合工程以及jar包冲突解决使用总结_第3张图片

聚合工程的pom.xml文件最重要的是定义项目用到的依赖以及版本,在被聚合工程根据需要引用(不需要版本号直接引用聚合工程中声明好的包),被聚合工程可以根据需要引用。聚合工程中仅仅是对包的声明,并没有真正引入 jar包。

如果在聚合工程中的pom文件中没有使用,使用的是

dependencies即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项(全部继承,被动继承)

 这样做的好处:统一管理项目的版本号,确保应用的各个项目的依赖和版本一致,才能保证测试的和发布的是相同的成果,因此,在顶层pom中定义共同的依赖关系。同时可以避免在每个使用的子项目中都声明一个版本号,这样想升级或者切换到另一个版本时,只需要在父类容器里更新,不需要任何一个子项目的修改;如果某个子项目需要另外一个版本号时,只需要在dependencies中声明一个版本号即可。子类就会使用子类声明的版本号,不继承于父类版本号。

在pom.xml文件中引入插件的方法及作用:

1)资源拷贝插件

Maven构建聚合工程以及jar包冲突解决使用总结_第4张图片

在执行maven编译的时候,如果我把mybatis的mapper.xml和java类文件放在一起,会导致xml文件不会被复制过去,可能就会报mybatis的unbind异常

所以这个时候会用到这个插件:

配置需要拷贝的文件(注意:如果配置了复制src/main/java下面的配置文件,src/main/resources下面的配置文件也需要手动配置)

Maven构建聚合工程以及jar包冲突解决使用总结_第5张图片

2)Maven给我们提供了一个插件: JDK编译插件,在工程pom文件中引入该插件可以使用指定版本的JDK,我们在 pom 文件中添加如下配置信息:

Maven构建聚合工程以及jar包冲突解决使用总结_第6张图片

保存pom文件,等待工程自动更新完毕,注意:如果更新完毕后,工程仍然显示错误的红叉,我们需要手动更新工程,手动更新工程的操作步骤为:

在工程上右键,选择 “Maven-->Update Project...”在弹出的窗口中,勾选我们的工程,点击 "OK" 即可。

Maven中另外一种切换JDK版本的方式

除了上述使用插件切换JDK版本的方式,Maven还提供了另外一种常用的方式:在pom文件中通过元素的方式进行配置,配置方式是在 pom 文件中添加如下信息:

    1.8
        1.8
    

3)Maven中tomcat插件的配置使用

springboot内嵌tomcat可以不使用这个插件。

https://blog.csdn.net/qq_32534441/article/details/87028863

 

二、解决jar包冲突问题

简介:处理jar包依赖冲突,首先,对于多个jar包都引用同一jar包的情况,最好是在程序中显式定义被共同引用的jar包的依赖,来统一版本号,方便维护

如果A和B都依赖同一jar包C,可能会出现两种情况

1.A和B引用的C版本相同,这时按照pom定义顺序选择第一个即可,没有冲突问题,如果在项目的maven中显示定义了C依赖,那么用选择项目定义的依赖,反正version都一样,没有影响

2.A和B依赖的C版本不同,选择版本高的那个,这时会出现两种结果

  (1) 高版本兼容低版本,所以不会出现问题

  (2)高版本不兼容低版本,假如A依赖C2版本,B依赖C3版本,C3不兼容C2,maven选择了高版本C3,对A来说会出现问题

  有3种解决方法

  [1]提升A版本,找到依赖C3的A版本

  [2]如果B版本也可依赖C2,在项目的maven中显示定义对C2的依赖,这样所有都使用C2版本

  [3]如果B版本不支持C2版本,只能降低B版本,找到依赖C2的B版本

  从功能性和可维护性考虑,高版本提供的功能更多,bug更少,优先考虑1

  再考虑2

  最后考虑3

搞懂这7个Maven问题,带你吊打面试官!

jar包产生冲突的原因:

Maven构建聚合工程以及jar包冲突解决使用总结_第7张图片

 

在以上依赖关系中项目除了会引入B、C还会引入X、Y、M的依赖包,但是如果B依赖的X版本会1.0而C依赖的X版本为2.0时,那最后项目使用的到底是X的1.0版本还是2.0版本就无法确定了。这是就要看ClassLoader的加载顺序,假设ClassLoader先加载1.0版本那就不会加载2.0版本,反之同理
使用mvn -Dverbose dependency:tree排查冲突

 

D:\code\spring-cloud-master\spring-cloud-master\Spring-Cloud-Book-Code-2\ch-2\SpringCloud_CH3>mvn dependency:tree -Dverbose -Dincludes=commons-collections
[INFO] Scanning for projects...
[WARNING]
[WARNING] Some problems were encountered while building the effective model for esshop:esshop:war:0.0.1-SNAPSHOT
[WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-compiler-plugin is missing. @ line 621,
[WARNING]
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING]
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
[WARNING]
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building esshop Maven Webapp 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ esshop ---
[INFO] esshop:esshop:war:0.0.1-SNAPSHOT
[INFO] +- commons-collections:commons-collections:jar:3.2.1:compile
[INFO] +- org.hibernate:hibernate:jar:3.2.2.ga:compile
[INFO] |  \- (commons-collections:commons-collections:jar:2.1.1:compile - omitted for conflict with 3.2.1)
[INFO] +- org.hibernate:hibernate-annotations:jar:3.4.0.GA:compile
[INFO] |  \- org.hibernate:hibernate-core:jar:3.3.0.SP1:compile
[INFO] |     \- (commons-collections:commons-collections:jar:3.1:compile - omitted for conflict with 3.2.1)
[INFO] +- org.springframework.security:spring-security-core:jar:2.0.4:compile
[INFO] |  \- (commons-collections:commons-collections:jar:3.2:compile - omitted for conflict with 3.2.1)
[INFO] +- org.apache.velocity:velocity:jar:1.5:compile
[INFO] |  \- (commons-collections:commons-collections:jar:3.1:compile - omitted for conflict with 3.2.1)
[INFO] \- net.sf.json-lib:json-lib:jar:jdk15:2.4:compile
[INFO]    \- (commons-collections:commons-collections:jar:3.2.1:compile - omitted for duplicate)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

这里需要总结一下什么是版本冲突、重复依赖但不影响工程启动的。

omitted for duplicate  为重复依赖但版本号一致的。

omitted for conflict with *** 为版本号冲突的。

若发现有jar包冲突则使用将多余的jar包去掉。

举个例子:

但显示出来的东西太多,头晕目眩,有没有好法呢?当然有了,加上Dincludes或者Dexcludes说出你喜欢或讨厌,dependency:tree就会帮你过滤出来:
引用
Dincludes=org.springframework:spring-tx
过滤串使用groupId:artifactId:version的方式进行过滤,可以不写全啦,如:

mvn dependency:tree -Dverbose -Dincludes=asm:asm  

就会出来asm依赖包的分析信息:
 

[INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ ridge-test ---
[INFO] com.ridge:ridge-test:jar:1.0.2-SNAPSHOT
[INFO] +- asm:asm:jar:3.2:compile
[INFO] \- org.unitils:unitils-dbmaintainer:jar:3.3:compile
[INFO]    \- org.hibernate:hibernate:jar:3.2.5.ga:compile
[INFO]       +- cglib:cglib:jar:2.1_3:compile
[INFO]       |  \- (asm:asm:jar:1.5.3:compile - omitted for conflict with 3.2)
[INFO]       \- (asm:asm:jar:1.5.3:compile - omitted for conflict with 3.2)
[INFO] ------------------------------------------------------------------------

对asm有依赖有一个直接的依赖(asm:asm:jar:3.2)还有一个传递进入的依赖(asm:asm:jar:1.5.3)

承上,假设我们不希望asm:asm:jar:1.5.3出现,根据分析,我们知道它是经由org.unitils:unitils-dbmaintainer:jar:3.3引入的,那么在pom.xml中找到这个依赖,做其它的调整:

     
        org.unitils  
        unitils-dbmaintainer  
        ${unitils.version}  
          
              
                dbunit  
                org.dbunit  
              
              
              
                asm  
                asm  
              
          
      

再分析一下,你可以看到传递依赖没有了:

[INFO]  
    [INFO] --- maven-dependency-plugin:2.1:tree (default-cli) @ ridge-test ---  
    [INFO] com.ridge:ridge-test:jar:1.0.2-SNAPSHOT  
    [INFO] \- asm:asm:jar:3.2:compile  
    [INFO] ------------------------------------------------------------------------  
    [INFO] BUILD SUCCESS  

有时,你以为解决了,但是偏偏还是报类包冲突(典型症状是java.lang.ClassNotFoundException或Method不兼容等异常),这时你可以设置一个断点,在断点处通过下面这个我做的工具类来查看Class所来源的JAR包:

public class ClassLocationUtils {
    public static String where(final Class clazz) {
        if (clazz == null) {
            throw new IllegalArgumentException("null input: cls");
        }
        URL result = null;
        final String clazzAsResource = clazz.getName().replace('.', '/').concat(".class");
        final ProtectionDomain protectionDomain = clazz.getProtectionDomain();
        if (protectionDomain != null) {
            final CodeSource codeSource = protectionDomain.getCodeSource();
            if (codeSource != null) result = codeSource.getLocation();
            if (result != null) {
                if ("file".equals(result.getProtocol())) {
                    try {
                        if (result.toExternalForm().endsWith(".jar") || result.toExternalForm().endsWith(".zip")) {
                            result = new URL("jar:".concat(result.toExternalForm()).concat("!/").concat(clazzAsResource));
                        } else if (new File(result.getFile()).isDirectory()) {
                            result = new URL(result, clazzAsResource);
                        }
                    } catch (MalformedURLException ignore) {

                    }
                }
            }
        }
        if (result == null) {
            final ClassLoader clsLoader = clazz.getClassLoader();
            result = clsLoader != null ? clsLoader.getResource(clazzAsResource) : ClassLoader.getSystemResource(clazzAsResource);
        }
        return result.toString();
    }
}

随便写一个测试,设置好断点,在执行到断点处按alt+F8动态执行代码(intelij idea),假设我们输入:
Java代码  收藏代码

ClassLocationUtils.where(org.objectweb.asm.ClassVisitor.class)  

即可马上查出类对应的JAR了:

这就是org.objectweb.asm.ClassVisitor类在运行期对应的JAR包,如果这个JAR包版本不是你期望你,就说明是你的IDE缓存造成的,这时建议你Reimport一下maven列表就可以了,如下所示(idea):

Reimport一下,IDE会强制根据新的pom.xml设置重新分析并加载依赖类包,以得到和pom.xml设置相同的依赖。(这一步非常重要哦,经常项目组pom.xml是相同的,但是就是有些人可以运行,有些人不能运行,俗称人品问题,其实都是IDE的缓存造成的了
idea清除缓存,为了提高效率不建议采用reimport重新起开启项目的方式,建议采用idea自带的功能,File->Invalidate Caches 功能直接完成清除idea cache

三、使用mvn helper插件

1)Maven Helper插件安装:

Maven构建聚合工程以及jar包冲突解决使用总结_第8张图片

Maven构建聚合工程以及jar包冲突解决使用总结_第9张图片

打开pom文件,在左下角的tab页的Dependency Analyzer(安装成功Maven Helper才会出现这个tab页)。

Maven构建聚合工程以及jar包冲突解决使用总结_第10张图片

Conflicts  为筛选出jar包重复或者版本冲突;

All Dependencies as List(列表形式查看所有依赖)

All Dependencies as Tree(树结构查看所有依赖)

点击筛选出的包右键

Maven构建聚合工程以及jar包冲突解决使用总结_第11张图片

点击Exclude,实现的工程跟是一样的,如果出现问题,可以直接在pom文件界面ctrl+z。

 

附加:

Maven构建聚合工程以及jar包冲突解决使用总结_第12张图片

compile

默认就是compile,什么都不配置也就是意味着compile。compile表示被依赖项目需要参与当前项目的编译,当然后续的测试,运行周期也参与其中,是一个比较强的依赖。打包的时候通常需要包含进去。

test

scope为test表示依赖项目仅仅参与测试相关的工作,包括测试代码的编译,执行。比较典型的如junit。

runntime

runntime表示被依赖项目无需参与项目的编译,不过后期的测试和运行周期需要其参与。与compile相比,跳过编译而已,说实话在终端的项目(非开源,企业内部系统)中,和compile区别不是很大。比较常见的如JSR×××的实现,对应的API jar是compile的,具体实现是runtime的,compile只需要知道接口就足够了。Oracle jdbc驱动架包就是一个很好的例子,一般scope为runntime。另外runntime的依赖通常和optional搭配使用,optional为true。我可以用A实现,也可以用B实现。

provided

provided意味着打包的时候可以不用包进去,别的设施(Web Container)会提供。事实上该依赖理论上可以参与编译,测试,运行等周期。相当于compile,但是在打包阶段做了exclude的动作。

system

从参与度来说,也provided相同,不过被依赖项不会从maven仓库抓,而是从本地文件系统拿,一定需要配合systemPath属性使用。

scope的依赖传递

A–>B–>C。当前项目为A,A依赖于B,B依赖于C。知道B在A项目中的scope,那么怎么知道C在A中的scope呢?答案是: 
当C是test或者provided时,C直接被丢弃,A不依赖C; 
否则A依赖C,C的scope继承于B的scope。

下面是一张nexus画的图。 

你可能感兴趣的:(Maven,SpringBoot,SpringCloud)