不管是在如今的互联网公司还是传统的应用性企业,特别是在Java领域,Maven是随处可见的,Maven的仓库管理、依赖管理、继承和聚合等特性为项目的构建提供了一整套完善的解决方案,如果说还不清楚Maven的使用的话,那么遇到了多模块的项目,分布式的系统,那么你会非常的头疼,依赖冲突会让你不知所措,之所以写这篇文章,其意义也在于去探索关于Maven在项目中使用的一些具体的心得体会,和遇到的一些坑是如何去解决的
回想当初使用Maven的时候,也是因为项目的需要而使用,首先会配置Maven(MAVEN_HOME、path),再根据公司的项目需要,更换对应的settings.xml文件,修改本地仓库地址路径,然后再在IDEA或者Eclipse中进行Maven插件配置,接着在项目中的pom.xml里面添加<dependency>标签来管理jar包,在Maven规范的目录结构下,进行编写代码,最后通过插件的方式来进行测试、打包、部署、运行。
1、Maven的仓库
Maven相比于传统的jar包放在项目路径下的形式,Maven提供了仓库的这一概念

然后默认的仓库路径是${user.home}/.m2/repository下,如果是使用的eclipse的话

就可以点开上述的操作,查看对应的本地的仓库路径
为什么会有仓库的这一概念,在于你需要的jar包,不需要每次都联网去下载,这样太费劲了,所以本地仓库就是相当于加了一层jar包缓存,先去本地仓库里查看,如果找不到的话,就去私服上找,如果私服也找不到的话,那么就去中央仓库上找,找到jar包后,就把jar的信息同步到私服和本地仓库中
1)、私服
就是公司内部局域网的一条服务器,私服中存储了本公司的内部专用的jar,也充当了中央仓库的镜像,相当于一个代理
2)、中央仓库
该仓库存储了互联网上的jar,由Maven团队来维护,地址是:http://repo1.maven.org/maven2/
2、<dependency>标签的使用
这里的话,举个例子:


从这个标签中就可以看到jar的查找坐标:groupId,artfactId,version
其中的version分为开发版本(Snapshot)和发布版本(Release)
在实际的开发中,一般会遇到这样的问题,A服务依赖于B服务,A和B在同时开发,B在开发中遇到了bug,修改后,再讲版本升级由1.0升级到2.0,那么A也必须得跟着做版本升级,过了一阵子后,B又出现了问题,进行修改后,升级版本发布,然后再通知A进行升级,这是导致开发中版本不稳定的因素之一
这里的话,Maven已经替我们想好了解决方案,那就是使用Snapshot版本,B发布的版本标志为Snapshot版本,A进行依赖的时候选择的也是Snapshot版本,那么每次B发布的时候,会在私服仓库中,形成带有时间戳的Snapshot版本,而A构建的时候,会自动下载B最新时间戳的Snapshot版本

3、Maven是如何解决依赖冲突的
首先Maven中为什么会出现依赖的冲突呢,那就要说到Maven的依赖管理了,Maven有以下的几个原则:
1)、依赖是使用Maven坐标来定位的,而Maven坐标主要由GAV(groupId, artifactId, version)构成。如果两个相同的依赖包,如果groupId, artifactId, version不同,那么maven也认为这两个是不同
2)、依赖会传递,A依赖了B,B依赖了C,那么A的依赖中就会出现B和C
3)、Maven对同一个groupId, artifactId的冲突仲裁,不是以version越大越保留,而是依赖路径越短越优先,然后进行保留
4)、依赖的scope会影响依赖的影响范围
那如何去定位冲突呢,比如系统出现了NoSuchMethodError,LinkageError 很有可能是你系统中出现了依赖冲突。出现冲突以后,有以下的几种解决的方式:
1)、确定出了问题的jar包名称。通常可以在eclipse中查找冲突的类有在哪些依赖包里面出现了。并确定实际要使用的是那个包,冲突的包有哪些。
2)、通过mvn dependency:tree > tree.txt 导出全部的依赖。
3)、在导出的依赖文件中,查找问题相关的jar。确定这些jar是如何被依赖进来的,是直接依赖的还是通过传递依赖引入的。
4)、找到相互冲突的并需要排除的依赖的顶级依赖,并分析冲突的原因,冲突的原因可能是以下几种:
同一个jar包但groupId, artifactId不同,这种冲突只能通过设定依赖的<exclusions> 来进行排除
需要的版本jar包依赖路径较长,这种冲突可以把想要版本的依赖直接什么在依赖中,这样路径就最短了优先级最高。
5)、最后可以通过打包mvn install 来确认打出来的war包中是否有被排除的依赖。
4、Maven规范化目录结构

需要注意两个点:
1:src/main下内容最终会打包到Jar/War中,而src/test下是测试内容,并不会打包进去
2:src/main/resources中的资源文件会COPY至目标目录,这是Maven的默认生命周期中的一个规定的动作,比如hibernate/mybatis的映射XML需要放入resources下,而不能放在其他地方了
5、关于scope依赖范围
Maven的生命周期存在编译、测试、运行这些过程,那有些依赖只用于测试,比如Junit;有些依赖编译用不到,只有运行的时候才能用到,比如mysql的驱动包在编译器就用不到(编译器用的是JDBC接口),而是在运行时用到的
compile:默认的scope,运行期有有效,需要打入包中
provided:编译器有效,运行期不需要提供,不会打入包中
runtime:编译不需要,在运行期有效,需要导入包中
test:测试需要,不会打入包中
system:非本地仓库引入,存在系统的某个路径下的jar




