一、背景
- 组件类工程A,打包后生成jar包t,该jar包可以作为组件供其他工程使用;
- 组件类工程A依赖jar包a,jar包a默认依赖版本n的jar包b;
- 工程B依赖jar包t。
二、详细配置
- 工程A的pom配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.titan</groupId>
<artifactId>testa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>testa</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
- 工程A的依赖情况
- 工程B的pom配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.titan</groupId>
<artifactId>testb</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>testb</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.titan</groupId>
<artifactId>testa</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
- 工程B的pom依赖情况
- 结论
工程B依赖jar包com.titan:testa:jar:0.0.1-snapshot,继承该jar包的依赖关系,依赖jar包org.slf4j:slf4j-log4j12:jar:1.7.21,进而依赖jar包org.slf4j:slf4j-api:jar:1.7.21。
三、遇到问题
- 工程A的开发团队突然发现版本n的jar包b(即样例中的org.slf4j:slf4j-api:jar:1.7.21)存在安全或者性能问题,需要替换为版本m的新jar包(org.slf4j:slf4j-api:jar:1.7.25);
- 在工程A升级之后,重新打包B时,除修改引入依赖A的版本之外,不需要再修改其他配置,就能够保证工程B顺利使用正确的jar包依赖。
四、解决方案
- 工程A的pom配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.titan</groupId>
<artifactId>testa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>testa</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
- 工程A的依赖情况
由此可以看到,org.slf4j:slf4j-api:jar的版本已经被设置为1.7.25。
- 工程B的pom依赖情况(修改工程A的pom后,注意install工程A)
发现工程B并未受到工程A中配置的dependency management的影响。
只有对工程A进行优化配置,在dependency中明确指定org.slf4j:slf4j-api的jar包版本,这样就能够传递到工程B中。
- 优化后工程A的pom配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.titan</groupId>
<artifactId>testa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>testa</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
- 优化后工程A的依赖情况
提示org.slf4j:slf4j-api:jar该jar包版本冲突,同时如我们所愿被强制设置使用1.7.25。
- 优化后工程B的pom依赖情况
根据上述内容,可以发现工程B的依赖发生了冲突,并且该冲突没有明确指明使用哪个版本的jar包,这种情况在持续集成过程中是不允许存在的。
所以,只能对工程A进行二次优化,过滤掉工程A对org.slf4j:slf4j-api:jar:1.7.21的依赖。
- 二次优化后工程A的pom配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.titan</groupId>
<artifactId>testa</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>testa</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.21</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
- 二次优化后工程A的依赖情况
- 二次优化后工程B的依赖情况
差点感觉自己药丸,终于通过配置工程A的pom,使工程A升级时,能够在不修改工程B的情况下,保障工程B能够使用正确的jar包。
五、总结
- dependency manage中的配置,只对当前工程有效。(分析maven的依赖处理逻辑,首先maven会将所有的依赖进行树形管理,生成一个虚拟依赖树形结构,然后通过dependency manage对该树形依赖结构进行管理维护,生成经DM管理维护之后的树形依赖结构,如果为间接依赖,则DM无法发挥作用)
- dependency中的exclude是可以进行传递的,当其他工程依赖某组件时,若该组件中存在exclude,那么该配置是可以被传递到此工程的。
- 对于变更了默认依赖的组件工程,首先应该明确所需版本,因为默认版本已不适用,通过要排除掉默认版本,避免在其他工程中发生冲突。
六、延伸
- 在持续交付过程中,很难保证开发人员在进行组件开发时,如果发生以上情况,一定会配置exclude信息;
- 必须存在必要的校验规则,才能避免依赖该组件的工程出现包冲突,进而误用包版本的情况;
鉴于以上两点,在对组件类工程打包导入到maven私服仓库前,应该生成一个虚拟工程,引入该组件进行依赖树校验,如果发现该虚拟工程的依赖树中存在jar包版本冲突,则认为该组件类的工程jar包引入不明确,需要重新配置,通过校验后,方可打包进入到maven私服仓库中。