maven实现依赖的“全局排除”
大多数java应用源码构建和依赖管理是使用maven来实现的,maven也是java构建和依赖管理的事实上的标准。我们的应用系统也都是基于maven构建的,maven虽然在依赖管理方面确实很牛叉,但是并不能很优雅地解决所有依赖的问题,比如此次谈及的“全局排除”功能。
之前包括现在都在经历这样的事情,想禁止一个依赖被依赖进来,如果这个依赖属于冷门的依赖,很少类库会间接依赖它,那么进行一次排除完全OK,但是如果一个依赖是热门依赖,比如常用的apache的commons系列工具库,单独排除也可以实现,只是比较啰嗦,而且以后引入新的依赖就要时刻关心是否会带来不被允许的依赖,对维护人员来说简直是灾难。
首先谈下为什么有些依赖是一定不能允许的。在日志详解的博文中已经提到了依赖的一个典型特性——互斥,就是说有的依赖之间是不能共存的,比如提到过的slf4j-log4j和logback,guava和google-collection等等,一旦应用选择了使用logback就不能再引入slf4j-log4j依赖,原因可以看日志详解,而guava和google-collection会存在jar冲突,这样的例子还有很多。所以对于经常碰到这种冲突的开发人员来说,强烈希望改善这种局面。
想象下这样一个场景:你的应用不能依赖slf4j-log4j,别的开发不清楚,引入了其他类库,间接引入了这个依赖,之后应用除了问题,你负责去排查,看到了这个问题,排除了slf4j-log4j,收工。那下次出现了,你再去排除一次,那下下次,嗯哼……对了,我们需要全局声明下,这个依赖不能进来,好想法,只是……可惜……maven目前还不支持,虽然承诺未来会支持(最新的3.1.0依然未提供)。那能不能让我做了一次排除之后可以有个地方记录下确实不能有这个依赖进来,如果出现的话,构建神马的操作就报错提示。嗯,好想法,maven插件可以这个,写个吧。好在maven提供了相应的插件——maven-enforcer-plugin,去尝试帮助开发人员解决这个问题,其中一项比较有用的功能是bannedDependencies,可以设置依赖黑白名单,如果有依赖匹配了黑名单中的依赖设置,那么maven会停止(可以配置)当前操作(打包构建,甚至是mvn eclipse:eclipse),打印错误日志提示,配置的样式如下:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<id>enforce-versions</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireMavenVersion>
<version>2.1.0</version>
</requireMavenVersion>
<requireJavaVersion>
<version>1.6</version>
</requireJavaVersion>
</rules>
</configuration>
</execution>
<execution>
<id>enforce-banned-dependencies</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<bannedDependencies>
<excludes>
<exclude>junit:junit</exclude>
<exclude>org.testng:testng</exclude>
<exclude>com.google.collections:google-collections</exclude>
<exclude>commons-logging:commons-logging</exclude>
</excludes>
<includes>
<include>junit:junit:4.8.2:jar:test</include>
<include>cglib:cglib-nodep:jar:2.2</include>
</includes>
</bannedDependencies>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>
从这个插件配置上可以看出,还可以限制java版本以及maven版本。重点看看对依赖黑白名单的生命,黑名单中规定不能引入commons-logging,那么一旦依赖了这个,操作会提示,至于是不是会停止操作,取决于下边fail标签中的配置。比较特殊的是黑名单中排除了junit,而白名单中更加详细地描述junit,这个可以这么解读:不允许依赖junit,除了版本是4.8.2的scope为test的junit,从此可以看出,白名单是对黑名单的补充,这样更加灵活。当我看到这个插件的时候,它还在襁褓中(beta版),但是强烈吸引到了我的注意,这娃必成大器。使用这个,就可以只排除一次冲突,并记录到黑白名单,下次被破坏的时候,自然会提示信息,这样算是把经验总结下来,一次辛苦,万世留名,我们系统到现在还在使用这个,利器。
到此,看起来我们解决这个全局排除的难题,真的吗?仔细想想,这个也只是一个防御的方式,正像之前说的一样,一个热门的依赖会经常被间接依赖进来,那是不是会经常就构建失败了,有木有一种方式更加彻底(让不允许被依赖的jar直接进不来)呢?当然,方法是有的。不过在将这个之前,还是得穿插一下maven依赖仲裁的原则:maven在解析依赖的时候,有两个原则,第一原则是路径最短有限原则,例如A–>B–>C-1.0(A依赖B,B依赖C的1.0版本),同时A–>D–>E–>C-2.0,那么从A来看,最终会依赖C的1.0版本进来,因为路径最短,最可信,这个例子也推翻了“高版本覆盖低版本”的错误言论。第二原则是优先声明原则(pom中的声明顺序),这是对第一原则的补充,就是路径长度相同(第一原则好无力)的情况下,第二原则开始决策微调,不过这个原则是在maven2.1.0才加入的,之前的版本如果第一原则无力的情况下,就是不可调控的,所以码农门升级吧。原则清楚之后,我们就拿阐述第一原则的例子开始,加入A就是你的应用,C就是不允许被依赖进来的一个依赖,咋办?排除&加入黑名单。嗯,很好。那如果我根据第一原则,在A的pom中直接声明C呢?啥?你疯啦?不允许依赖,你还直接声明之?嗯,我确实不想活了,看下声明
<dependency>
<groupId>C</groupId>
<artifactId>C</artifactId>
<version>2.0</version>
</dependency>
这样是不是意味着,间接依赖的C都不顶用了,但是直接依赖了C,还是违背了不能依赖C的大前提,好,继续看下,把依赖声明改下
<dependency>
<groupId>C</groupId>
<artifactId>C</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
啊哈,这样C就不会打进最终的war包啦,也就间接起到了“全局排除”的目的。但是这样会给人造成迷惑,会什么是2.0,其实这个版本号已经没有任何含义了。但是问题还是在的,虽然声明了provided,但是编译时,这个jar依然还会出现在classpath下的,那代码依然开始可以引用的。那就再进一步,弄一个C的空壳,里边啥都没有,比如可以自己新建一个空的maven工程,C:C:empty_version,然后上传到私服(一般公司都会有自己的私服的)上,引用的地方改成把版本改成empty_version。这样对于A来说,因为是直接依赖C的empty_version版本,那么间接依赖全部自动被仲裁掉了,以后也不用担心别人引入依赖间接导致问题了。这样的做法并不完美,毕竟需要在私服上上传一个“垃圾”依赖,应用代码还需要直接依赖这个“垃圾”依赖。
综合黑白名单的方式和最后一种方式,总结下,如何结合使用。对于不那么热门的依赖,建议走黑白名单,毕竟很少出现,出现一次,手工排除下很简单。对于热门的依赖,也分下情况,像guava&google-collections这种情况,因为前者的代码是后者的超集,所以当依赖了guava的情况下,可以直接声明google-collections为provided,不需要上传一个空包,但是假如guava删掉了一些api,即它不再是超集的时候,就会出现问题。像logback&slf4j-log4j&slf4j-jdk14这种依赖,没有谁是谁的子集的说法,所以建议直接上传空包排除即可。当然这些原则还是需要开发者自己去权衡的。一定不是“一刀切”的原则,一刀切固然可以很好执行,但是往往不是最优的,两者或者多者结合,取长补短才是合理的,但是也会带来迷惑,因为有了选择。不过可以参考上边的总结,我们的应用也是两者结合的方式,选择的原则也是按照上边描述的,当然,最终的选择权还是交给你,enjoy it。
maven实现依赖的“全局排除”的更多相关文章
- maven依赖的全局排除
今天遇到要全局排除一个maven依赖,因为Maven本身没有全局排除依赖的办法, 参考了同事人英写的一篇博文(可以看这里http://my.oschina.net/liuyongpo/blog/177 ...
- maven可选依赖(Optional Dependencies)和依赖排除(Dependency Exclusions)
我们知道,maven的依赖关系是有传递性的.如:A-->B,B-->C.但有时候,项目A可能不是必需依赖C,因此需要在项目A中排除对A的依赖.在maven的依赖管理中,有两种方式可以对依赖 ...
- Spring Boot 全局排除 spring-boot-starter-logging 依赖
https://blog.csdn.net/u013314786/article/details/90412733 项目里使用了log4j2做日志处理,要排除掉Spring Boot 很多jar里边默 ...
- maven 检查依赖冲突和版本冲突
maven 检查依赖冲突和版本冲突 在项目发布的时候,一般都需要进行依赖冲突检查或者重复类的检查,这个时候我一般会使用下面的两个命令: 1 2 3 mvn -U clean package - ...
- Maven入门-4.Maven的依赖
1.Maven的依赖1.1 添加依赖1.2 依赖范围(sope)依赖范围与classpath的关系1.3 依赖的传递性1.2.1 依赖传递性的冲突问题1. 第一种情况2. 第二种情况1.2.2 通过e ...
- maven的依赖特性
若排版紊乱可查看我的个人博客原文地址 maven的依赖特性很多很杂,这里大概总结一下,maven的依赖特性主要是依赖范围和传递依赖,前者会影响后者,这篇文章会介绍传递依赖的传递原则,出现冲突传递依赖默 ...
- 着重基础之—构建工具—Maven的依赖管理
着重基础之—构建工具—Maven的依赖管理 项目构建利器Maven给我们开发人员带来了极大的便利,从繁琐的jar包管理中脱身的程序员终于可以有时间再进入另一个坑了. 我今天要给大家分享的内容是我在实际 ...
- Maven 3-Maven依赖版本冲突的分析及解决小结 (阿里,美团,京东面试)
举例A依赖于B及C,而B又依赖于X.Y,而C依赖于X.M,则A除引B及C的依赖包下,还会引入X,Y,M的依赖包(一般情况下了,Maven可通过<scope>等若干种方式控制传递依赖).这里 ...
- maven项目依赖包问题
问题 maven传递依赖 解决方案 前段时间,开发中遇到一个关于maven依赖包的问题:由于业务需要,支付网关对账代码中的slf4j-api包需要更新,原包为1.5.8版本,需要更新到1.6.4版 ...
随机推荐
- linux 下安装gsl
访问 http://ftp.club.cc.cmu.edu/pub/gnu/gsl/下载最新版本的,现在最新的是gsl-1.16.tar.gz,已经是2013年更新的了.然后下载 安装 简便的安装过程 ...
- jdk8飞行记录器配置
jdk8提供了jmc工具,应该比visualvm厉害吧 下面贴一份tomcat的配置,自己留个备份,把下面的内容粘贴到tomcat setenv.sh就可以了 nowday=`date +%Y%m%d ...
- 非常好!!!【从头开始写操作系统系列】实现一个-GDT(1)【转】
转自:http://blog.csdn.net/luoyhang003/article/details/47338019 权声明:本文为博主原创文章,未经博主允许不得转载.(文章来源:http://b ...
- Docker CPU 资源限制——CPU固定核功能测试
Docker使用Linux cgroup来实现资源的限制,对于CPU的限制有两种方法: 1.cpuset CPU Set限定容器使用某个固定的CPU核.使用默认的libcontainer引擎时,可以通 ...
- HDU 3853:LOOPS(概率DP)
http://acm.split.hdu.edu.cn/showproblem.php?pid=3853 LOOPS Problem Description Akemi Homura is a M ...
- sass初步认识3
sass的混合器是对大段的样式代码进行命名,定义为混合器,以便被多次引用.混合器的格式为:“@mixin 样式名称{样式代码}”:调用混合器的格式为:“@include 样式名称”.例:@minin ...
- ACM题目————zoj问题
题目1006:ZOJ问题 时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:20322 解决:3560 题目描述: 对给定的字符串(只包含'z','o','j'三种字符),判断他是否能AC. ...
- EffectiveJava笔记(第一部分)
考虑用静态构造方法代替构造器的好处: 1.静态构造方法有名字 BigInteger.probablePrime(int, int, Random)比 new BigInteger(int, i ...
- 20150618_Andriod _set Dialog_弹出式菜单
参考地址: http://blog.csdn.net/zhyl8157121/article/details/8169172 http://blog.csdn.ne ...
- 【20160924】GOCVHelper 图像增强部分(2)
//填充孔洞 //fillholes Mat fillHoles(Mat src){ Mat dst = getInnerHoles(src); ...