一、聚合工程
如图所示:
SpringCloud_CH3为聚合工程,eurekaclientarticleservice为被聚合工程。
聚合工程为一个maven工程:聚合类的pom.xml文件为:
<parent>标签为整个工程使用的springboot版本;<modules>标签中的值,每次新建一个Maven Modules,都会新增一个<module>标签。
聚合工程的pom.xml文件最重要的是定义项目用到的依赖以及版本,在被聚合工程根据需要引用(不需要版本号直接引用聚合工程中声明好的包),被聚合工程可以根据需要引用。聚合工程中仅仅是对包的声明,并没有真正引入 jar包。
如果在聚合工程中的pom文件中没有使用<dependencyManagement>,使用的是<dependencies></dependencies>
dependencies即使在子项目中不写该依赖项,那么子项目仍然会从父项目中继承该依赖项(全部继承,被动继承)
这样做的好处:统一管理项目的版本号,确保应用的各个项目的依赖和版本一致,才能保证测试的和发布的是相同的成果,因此,在顶层pom中定义共同的依赖关系。同时可以避免在每个使用的子项目中都声明一个版本号,这样想升级或者切换到另一个版本时,只需要在父类容器里更新,不需要任何一个子项目的修改;如果某个子项目需要另外一个版本号时,只需要在dependencies中声明一个版本号即可。子类就会使用子类声明的版本号,不继承于父类版本号。
在pom.xml文件中引入插件的方法及作用:
1)资源拷贝插件
在执行maven编译的时候,如果我把mybatis的mapper.xml和java类文件放在一起,会导致xml文件不会被复制过去,可能就会报mybatis的unbind异常
所以这个时候会用到这个插件:
配置需要拷贝的文件(注意:如果配置了复制src/main/java下面的配置文件,src/main/resources下面的配置文件也需要手动配置)
2)Maven给我们提供了一个插件: JDK编译插件,在工程pom文件中引入该插件可以使用指定版本的JDK,我们在 pom 文件中添加如下配置信息:
保存pom文件,等待工程自动更新完毕,注意:如果更新完毕后,工程仍然显示错误的红叉,我们需要手动更新工程,手动更新工程的操作步骤为:
在工程上右键,选择 “Maven-->Update Project...”在弹出的窗口中,勾选我们的工程,点击 "OK" 即可。
Maven中另外一种切换JDK版本的方式
除了上述使用插件切换JDK版本的方式,Maven还提供了另外一种常用的方式:在pom文件中通过<properties>元素的方式进行配置,配置方式是在 pom 文件中添加如下信息:
<properties
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
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
jar包产生冲突的原因:
在以上依赖关系中项目除了会引入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包冲突则使用<exclusions><exclusion></exclusion></exclusions>将多余的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中找到这个依赖,做其它的调整:
<dependency>
<groupId>org.unitils</groupId>
<artifactId>unitils-dbmaintainer</artifactId>
<version>${unitils.version}</version>
<exclusions>
<exclusion>
<artifactId>dbunit</artifactId>
<groupId>org.dbunit</groupId>
</exclusion>
<!-- 这个就是我们要加的片断 -->
<exclusion>
<artifactId>asm</artifactId>
<groupId>asm</groupId>
</exclusion>
</exclusions>
</dependency>
再分析一下,你可以看到传递依赖没有了:
[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插件安装:
打开pom文件,在左下角的tab页的Dependency Analyzer(安装成功Maven Helper才会出现这个tab页)。
Conflicts 为筛选出jar包重复或者版本冲突;
All Dependencies as List(列表形式查看所有依赖)
All Dependencies as Tree(树结构查看所有依赖)
点击筛选出的包右键
点击Exclude,实现的工程跟<exclusion>是一样的,如果出现问题,可以直接在pom文件界面ctrl+z。
附加:
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画的图。