- Maven简介
- 构建
- 依赖管理
- 项目信息管理
- 中央仓库
- 约定优于配置
- 安装Maven
- 检查JDK安装
- 安装Maven
- M2_HOME
- ~/.m2
- Maven安装最佳实践
- 1 设置MAVEN_OPTS环境变量
- 2 配置用户范围settings.xml
- 3 不要使用IDE内嵌的Maven
- 编写POM
- 编写主代码
- 编写测试代码
- 配置maven-compiler-plugin
- 打包和运行
- compile-test-package-install
- Maven坐标
- 依赖配置
- 依赖范围
- 传递性依赖
- 传递性依赖和依赖范围
- 依赖调解
- 可选依赖
- 最佳实践
- 排除依赖
- 归类依赖
- 优化依赖
- 跨平台的项目管理工具(无论是在Windows上,还是在Linux或者Mac上,都可以使用同样的命令);
- 主要服务于基于Java平台的项目构建、依赖管理和项目信息管理;
- 编译、运行单元测试、生成文档、打包和部署等;
- Maven是一个强大的构建工具,能够帮我们自动化构建过程,从清理、编译、测试到生成报告,再到打包和部署;
- Maven能帮助我们标准化构建过程;
- Maven通过一个坐标系统准确地定位每一个构件(artifact),也就是通过一组坐标Maven能够找到任何一个Java类库(如jar文件);
- Maven给这个类库世界引入了经纬,让它们变得有秩序,于是我们可以借助它来有序地管理依赖,轻松地解决那些繁杂的依赖问题;
- Maven还能帮助我们管理原本分散在项目中各个角落的项目信息,包括项目描述、开发者列表、版本控制系统地址、许可证、缺陷管理系统地址等;
- 除了直接的项目信息,通过Maven自动生成的站点,以及一些已有的插件,我们还能够轻松获得项目文档、测试报告、静态分析报告、源码版本日志报告等非常具有价值的项目信息;
- Maven为全世界的Java开发者提供了一个免费的中央仓库,在其中几乎可以找到任何的流行开源类库;
- 通过一些Maven的衍生工具(如Nexus),我们还能对其进行快速地搜索;
- 只要定位了坐标,Maven就能够帮我们自动下载,省去了手工劳动;
Maven对于项目目录结构、测试用例命名方式等内容都有既定的规则,只要遵循了这些成熟的规则,用户在项目间切换的时候就免去了额外的学习成本。
安装Maven 检查JDK安装打开Windows的命令行,运行如下的命令来检查Java安装:
echo %JAVA_HOME% java -version
上述命令首先检查环境变量JAVA_HOME是否指向了正确的JDK目录,接着尝试运行java命令。如果Windows无法执行java命令,或者无法找到JAVA_HOME环境变量,就需要检查Java是否安装了,或者环境变量是否设置正确。
安装Maven- 下载Maven安装文件;
- 解压安装文件到指定目录,如D:binapache-maven-3.0;
- 设置环境变量,将Maven安装配置到操作系统环境中:打开系统属性面板(在桌面上右击“我的电脑”→“属性”),单击高级系统设置,再单击环境变量,在系统变量中新建一个变量,变量名为M2_HOME,变量值为Maven的安装目录D:binapache-maven-3.0。单击“确定”按钮,接着在系统变量中找到一个名为Path的变量,在变量值的末尾加上%M2_HOME%bin;。注意:多个值之间需要有分号隔开,然后单击“确定”按钮。至此,环境变量设置完成;
值得注意的是Path环境变量。当我们在cmd中输入命令时,Windows首先会在当前目录中寻找可执行文件或脚本,如果没有找到,Windows会接着遍历环境变量Path中定义的路径。由于将%M2_HOME%bin添加到了Path中,而这里%M2_HOME%实际上是引用了前面定义的另一个变量,其值是Maven的安装目录。因此,Windows会在执行命令时搜索目录D:binapache-maven-3.0bin,而mvn执行脚本的位置就是这里。
- 检查Maven的安装情况:打开一个新的cmd窗口,运行以下命令:
echo %M2_HOME% mvn -v
echo %M2_HOME%用来检查环境变量M2_HOME是否指向了正确的Maven安装目录;mvn -v执行了第一条Maven命令,以检查Windows是否能够找到正确的mvn执行脚本。
- 升级Maven:下载新的Maven安装文件,解压至本地目录,然后更新M2_HOME环境变量。
M2_HOME环境变量指向Maven的安装目录,该目录的结构和内容:
- bin
- boot
- conf
- lib
- LICENSE.txt
- NOTICE.txt
- README.txt
- bin:包含了mvn运行的脚本,这些脚本用来配置Java命令,准备好classpath和相关的Java系统属性,然后执行Java命令。其中mvn是基于UNIX平台的shell脚本,mvn.bat是基于Windows平台的bat脚本。在命令行输入任何一条mvn命令时,实际上就是在调用这些脚本。
- boot:该目录只包含一个文件,以maven 3.0为例,该文件为plexus-classworlds-2.2.3.jar。plexus-classworlds是一个类加载器框架,相对于默认的java类加载器,它提供了更丰富的语法以方便配置,Maven使用该框架加载自己的类库。
- conf:包含了一个非常重要的文件settings.xml。直接修改该文件,就能在机器上全局地定制Maven的行为。一般情况下,我们更偏向于复制该文件至~/.m2/目录下(~表示用户目录),然后修改该文件,在用户范围定制Maven的行为;
- lib:该目录包含了所有Maven运行时需要的Java类库,Maven本身是分模块开发的,因此用户能看到诸如maven-core-3.0.jar、maven-model-3.0.jar之类的文件。此外,这里还包含一些Maven用到的第三方依赖,如common-cli-1.2.jar、google-collection-1.0.jar等;
- 其他:LICENSE.txt记录了Maven使用的软件许可证Apache License Version 2.0;NOTICE.txt记录了Maven包含的第三方软件;而README.txt则包含了Maven的简要介绍,包括安装需求及如何安装的简要指令等。
输入mvn help:system,在执行该命令的过程中,会生成~/.m2文件夹(~ 代表操作系统的当前用户目录),也就是本地仓库,并且会从Maven官网下载必要的依赖包到本地仓库。
- ~表示用户目录;
- 该文件夹下放置了Maven本地仓库.m2/repository,所有的Maven构件都被存储到该仓库中,以方便重用;
- Maven根据一套规则来确定任何一个构件在仓库中的位置;
- 默认情况下,~/.m2目录下除了repository仓库之外就没有其他目录和文件了,不过大多数Maven用户需要复制M2_HOME/conf/settings.xml文件到~/.m2/settings.xml。这是一条最佳实践;
- 运行mvn命令实际上是执行了Java命令,既然是运行Java,那么运行Java命令可用的参数当然也应该在运行mvn命令时可用;
- 通常需要设置MAVEN_OPTS的值为-Xms128m-Xmx512m,因为Java默认的最大可用内存往往不能够满足Maven运行的需要,比如在项目较大时,使用Maven生成项目站点需要占用大量的内存,如果没有该配置,则很容易得到java.lang.OutOfMemeoryError。因此,一开始就配置该变量是推荐的做法;
- 在Windows下创建一个名为MAVEN_OPTS的环境变量,设置其值为-Xms128m-Xmx512m即可。尽量不要直接修改mvn.bat或者mvn这两个Maven执行脚本文件。因为如果修改了脚本文件,升级Maven时就不得不再次修改,一来麻烦,二来容易忘记。同理,应该尽可能地不去修改任何Maven安装目录下的文件;
- Maven用户可以选择配置$M2_HOME/conf/settings.xml或者~/.m2/settings.xml。前者是全局范围的,整台机器上的所有用户都会直接受到该配置的影响,而后者是用户范围的,只有当前用户才会受到该配置的影响;
- 推荐使用用户范围的settings.xml,主要是为了避免无意识地影响到系统中的其他用户。如果有切实的需求,需要统一系统中所有用户的settings.xml配置,当然应该使用全局范围的settings.xml;
- 除了影响范围这一因素,配置用户范围settings.xml文件还便于Maven升级。直接修改conf目录下的settings.xml会导致Maven升级不便,每次升级到新版本的Maven,都需要复制settings.xml文件。如果使用~/.m2目录下的settings.xml,就不会影响到Maven安装文件,升级时就不需要触动settings.xml文件;
当集成Maven时,IDE往往会安装一个内嵌的Maven,这个内嵌的Maven通常会比较新,但不一定很稳定,而且往往也会和在命令行使用的Maven不是同一个版本。
编写POM- Maven项目的核心是pom.xml;
- POM(Project Object Model,项目对象模型)定义了项目的基本信息,用于描述项目如何构建,声明项目依赖,等等;
- groupId、artifactId和version这三个元素定义了一个项目基本的坐标,在Maven的世界,任何的jar、pom或者war都是以基于这些基本的坐标进行区分的;
- groupId定义了项目属于哪个组,这个组往往和项目所在的组织或公司存在关联。如果公司是mycom,有一个项目为myapp,那么groupId就应该是com.mycom.myapp;
- artifactId定义了当前Maven项目在组中唯一的ID,你可能会为不同的子项目(模块)分配artifactId,如myapp-util、myapp-domain、myapp-web等;
- version指定了项目当前的版本。SNAPSHOT意为快照,说明该项目还处于开发中,是不稳定的版本。随着项目的发展,version会不断更新,如升级为1.0、1.1-SNAPSHOT、1.1、2.0等;
- name元素声明了一个对于用户更为友好的项目名称,虽然这不是必须的,但还是推荐为每个POM声明name,以方便信息交流;
- 没有任何实际的Java代码,就能够定义一个Maven项目的POM,这体现了Maven的一大优点,它能让项目对象模型最大程度地与实际代码相独立,即解耦。这在很大程度上避免了Java代码和POM代码的相互影响。比如当项目需要升级版本时,只需要修改POM,而不需要更改Java代码;而在POM稳定之后,日常的Java代码开发工作基本不涉及POM的修改。
- 项目主代码和测试代码不同,项目的主代码会被打包到最终的构件中(如jar),而测试代码只在运行测试时用到,不会被打包;主代码目录是src/main/java,测试代码目录是src/test/java;
- 主代码Java类的包名与之前在POM中定义的groupId和artifactId相吻合;
- 使用Maven进行编译:在项目根目录下运行命令mvn clean compile。clean告诉Maven清理输出目录target/,compile告诉Maven编译项目主代码至target/classes目录;
- 测试代码目录是src/test/java;
- 在Java世界中,JUnit是事实上的单元测试标准。要使用JUnit,首先需要为Hello World项目添加一个JUnit依赖;
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.7</version> <scope>test</scope> </dependency>
- scope为依赖范围,若依赖范围为test则表示该依赖只对测试有效。换句话说,测试代码中的import JUnit代码是没有问题的,但是如果在主代码中用import JUnit代码,就会造成编译错误。如果不声明依赖范围,那么默认值就是compile,表示该依赖对主代码和测试代码都有效。
- 需要执行的测试方法都应该以@Test进行标注;
- 运行mvnclean test,但Maven实际执行的可不止这两个任务,还有clean:clean、resources:resources、compiler:compile、resources:testResources以及compiler:testCompile;
由于历史原因,Maven的核心插件之一——compiler插件默认只支持编译Java 1.3,因此需要配置该插件使其支持更高版本的Java。
打包和运行mvn clean package
- Maven会在打包之前执行编译、测试等操作;
- 输出台看到的jar:jar任务负责打包,实际上就是jar插件的jar目标将项目主代码打包成一个名为artifact-version.jar的文件。该文件位于target/输出目录中;
- 至此,我们得到了项目的输出,如果有需要的话,就可以复制这个jar文件到其他项目的Classpath中从而使用它的功能;
如何才能让其他的Maven项目直接引用这个jar呢?还需要一个安装的步骤,执行命令:
mvn clean install
- 从控制台输出可以看到:在打包之后,又执行了安装任务install:install;
- 该任务将项目输出的jar安装到了Maven本地仓库中,可以打开相应的文件夹看到项目的pom和jar;
- 这样其它项目就可以直接引用这个项目的jar包了;
Maven最主要的几个命令:mvn clean compile、mvn clean test、mvn clean package、mvn clean install。执行test之前会先执行compile,执行package之前会先执行test,执行install之前会执行package。
Maven坐标- 任何一个构件都可以使用Maven坐标唯一标识,Maven坐标的元素包括groupId、artifactId、version、packaging、classifier。只要我们提供正确的坐标元素,Maven就能找到对应的构件;
- “Maven是从哪里下载构件的呢?”Maven内置了一个中央仓库的地址(http://repo1.maven.org/maven2),该中央仓库包含了世界上大部分流行的开源项目构件,Maven会在需要的时候去那里下载;
- 在我们开发自己项目的时候,也需要为其定义适当的坐标,这是Maven强制要求的。在这个基础上,其他Maven项目才能引用该项目生成的构件;
- groupId:1 定义当前Maven项目隶属的实际项目;2 Maven项目和实际项目不一定是一对一的关系。比如SpringFramework这一实际项目,其对应的Maven项目会有很多,如spring-core、spring-context等。这是由于Maven中模块的概念,因此,一个实际项目往往会被划分成很多模块;3 groupId不应该对应项目隶属的组织或公司,因为一个组织下会有很多实际项目,如果groupId只定义到组织级别,而后面我们会看到,artifactId只能定义Maven项目(模块),那么实际项目这个层将难以定义;4 groupId的表示方式与Java包名的表示方式类似,通常与域名反向一一对应。上例中,groupId为org.sonatype.nexus,org.sonatype表示Sonatype公司建立的一个非盈利性组织,nexus表示Nexus这一实际项目,该groupId与域名nexus.sonatype.org对应。
- artifactId:1 定义实际项目中的一个Maven项目(模块);2 推荐的做法是使用实际项目名称作为artifactId的前缀。比如上例中的artifactId是nexus-indexer,使用了实际项目名nexus作为前缀,这样做的好处是方便寻找实际构件。在默认情况下,Maven生成的构件,其文件名会以artifactId作为开头,如nexus-indexer-2.0.0.jar,使用实际项目名称作为前缀之后,就能方便从一个lib文件夹中找到某个项目的一组构件。考虑有5个项目,每个项目都有一个core模块,如果没有前缀,我们会看到很多core-1.2.jar这样的文件,加上实际项目名前缀之后,便能很容易区分foo-core-1.2.jar、bar-core-1.2.jar……
- version:定义Maven项目当前所处的版本,如上例中nexus-indexer的版本是2.0.0。
- packaging:1 定义Maven项目的打包方式。2 打包方式通常与所生成构件的文件扩展名对应,如上例中packaging为jar,最终的文件名为nexus-indexer-2.0.0.jar,而使用war打包方式的Maven项目,最终生成的构件会有一个.war文件;2 打包方式会影响到构建的生命周期,比如jar打包和war打包会使用不同的命令;3 当不定义packaging的时候,Maven会使用默认值jar。
- classifier:1 用来帮助定义构建输出的一些附属构件;2 附属构件与主构件对应,如上例中的主构件是nexus-indexer-2.0.0.jar,该项目可能还会通过使用一些插件生成如nexus-indexer-2.0.0-javadoc.jar、nexus-indexer-2.0.0-sources.jar这样一些附属构件,其包含了Java文档和源代码。这时候,javadoc和sources就是这两个附属构件的classifier。这样,附属构件也就拥有了自己唯一的坐标。注意,不能直接定义项目的classifier,因为附属构件不是项目直接默认生成的,而是由附加的插件帮助生成。
- groupId、artifactId、version是必须定义的,packaging是可选的(默认为jar),而classifier是不能直接定义的。
- 项目构件的文件名是与坐标相对应的,一般的规则为artifactId-version[-classifier].packaging,[-classifier]表示可选。另外,packaging并非一定与构件扩展名对应,比如packaging为maven-plugin的构件扩展名为jar。
- groupId、artifactId和version:依赖的基本坐标;
- type:依赖的类型,对应于项目坐标定义的packaging。大部分情况下,该元素不必声明,其默认值为jar;
- scope:依赖范围;
- optional:标记依赖是否可选;
- exclusions:用来排除传递性依赖;
- 大部分依赖声明只包含基本坐标,然而在一些特殊情况下,其他元素至关重要;
- 依赖范围就是用来控制依赖与三种classpath(编译classpath、测试classpath、运行classpath)的关系。
- compile:编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。典型的例子是spring-core,在编译、测试和运行的时候都需要使用该依赖。
- test:测试依赖范围。使用此依赖范围的Maven依赖,只对于测试classpath有效,在编译主代码或者运行项目的使用时将无法使用此类依赖。典型的例子是JUnit,它只有在编译测试代码及运行测试的时候才需要。
- provided:已提供依赖范围。使用此依赖范围的Maven依赖,对于编译和测试classpath有效,但在运行时无效。典型的例子是servlet-api,编译和测试项目的时候需要该依赖,但在运行项目的时候,由于容器已经提供,就不需要Maven重复地引入一遍。
- runtime:运行时依赖范围。使用此依赖范围的Maven依赖,对于测试和运行class-path有效,但在编译主代码时无效。典型的例子是JDBC驱动实现,项目主代码的编译只需要JDK提供的JDBC接口,只有在执行测试或者运行项目的时候才需要实现上述接口的具体JDBC驱动。
- system:系统依赖范围。该依赖与三种classpath的关系,和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显式地指定依赖文件的路径。由于此类依赖不是通过Maven仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用。
- 如果一个依赖又依赖于其他类库,一种做法是项目下载本依赖的jar包以及它依赖的其他jar包,这样就引入了很多不必要的依赖,另一种做法是只下载本依赖的jar包,到实际使用时再根据出错信息加入需要的其他依赖。显然,这些做法不够理想。
- Maven的传递性依赖机制可以很好地解决这一问题。例如account-mail有一个compile范围的spring-core依赖,spring-core有一个compile范围的commons-logging依赖,那么commons-logging就会成为account-email的compile范围依赖,commons-logging是account-email的一个传递性依赖。
- 有了传递性依赖机制,在使用Spring Framework的时候就不用去考虑它依赖了什么,也不用担心引入多余的依赖。Maven会解析各个直接依赖的POM,将那些必要的间接依赖,以传递性依赖的形式引入到当前的项目中。
假设A依赖于B,B依赖于C,我们说A对于B是第一直接依赖,B对于C是第二直接依赖,A对于C是传递性依赖。第一直接依赖的范围和第二直接依赖的范围决定了传递性依赖的范围。
如表所示,最左边一列表示第一直接依赖范围,最上面一行表示第二直接依赖范围,中间的交叉单元格则表示传递性依赖范围。
当第二直接依赖的范围是compile的时候,传递性依赖的范围与第一直接依赖的范围一致;当第二直接依赖的范围是test的时候,依赖不会得以传递;当第二直接依赖的范围是provided的时候,只传递第一直接依赖范围也为provided的依赖,且传递性依赖的范围同样为provided;当第二直接依赖的范围是runtime的时候,传递性依赖的范围与第一直接依赖的范围一致,但compile例外,此时传递性依赖的范围为runtime。
- 假如项目A有这样的依赖关系:A->B->C->X(1.0)、A->D->X(2.0),X是A的传递性依赖,但是两条依赖路径上有两个版本的X,那么哪个X会被Maven解析使用呢?两个版本都被解析显然是不对的,因为那会造成依赖重复,因此必须选择一个。Maven依赖调解的第一原则是:路径最近者优先。该例中X(1.0)的路径长度为3,而X(2.0)的路径长度为2,因此X(2.0)会被解析使用;
- 比如这样的依赖关系:A->B->Y(1.0)、A->C->Y(2.0),Y(1.0)和Y(2.0)的依赖路径长度是一样的,都为2。那么到底谁会被解析使用呢?在Maven 2.0.8及之前的版本中,这是不确定的,但是从Maven 2.0.9开始,为了尽可能避免构建的不确定性,Maven定义了依赖调解的第二原则:第一声明者优先。在依赖路径长度相等的前提下,在POM中依赖声明的顺序决定了谁会被解析使用,顺序最靠前的那个依赖优胜。该例中,如果B的依赖声明在C之前,那么Y(1.0)就会被解析使用;
项目A依赖于项目B,项目B依赖于项目X和Y,B对于X和Y的依赖都是可选依赖:A->B、B->X(可选)、B->Y(可选)。根据传递性依赖的定义,如果所有这三个依赖的范围都是compile,那么X、Y就是A的compile范围传递性依赖。然而,由于这里X、Y是可选依赖,依赖将不会得以传递。换句话说,X、Y将不会对A有任何影响,如图所示:
为什么要使用可选依赖这一特性呢?可能项目B实现了两个特性,其中的特性一依赖于X,特性二依赖于Y,而且这两个特性是互斥的,用户不可能同时使用两个特性。比如B是一个持久层隔离工具包,它支持多种数据库,包括MySQL、PostgreSQL等,在构建这个工具包的时候,需要这两种数据库的驱动程序,但在使用这个工具包的时候,只会依赖一种数据库。
使用<optional>元素表示mysql-connector-java和postgresql这两个依赖为可选依赖,它们只会对当前项目B产生影响,当其他项目依赖于B的时候,这两个依赖不会被传递。因此,当项目A依赖于项目B的时候,如果其实际使用基于MySQL数据库,那么在项目A中就需要显式地声明mysql-connector-java这一依赖。
在理想的情况下不应该使用可选依赖。使用可选依赖的原因是某一个项目实现了多个特性,在面向对象设计中,有个单一职责性原则,意指一个类应该只有一项职责,而不是糅合太多的功能。这个原则在规划Maven项目的时候也同样适用。在上面的例子中,更好的做法是为MySQL和PostgreSQL分别创建一个Maven项目,基于同样的groupId分配不同的artifactId,如com.juvenxu.mvnbook:project-b-mysql和com.juvenxu.mvnbook:project-b-postgresql,在各自的POM中声明对应的JDBC驱动依赖,而且不使用可选依赖,用户则根据需要选择使用project-b-mysql或者project-b-postgresql。由于传递性依赖的作用,就不用再声明JDBC驱动依赖。
最佳实践 排除依赖例如,当前项目有一个第三方依赖,而这个第三方依赖由于某些原因依赖了另外一个类库的SNAPSHOT版本,那么这个SNAPSHOT就会成为当前项目的传递性依赖,而SNAPSHOT的不稳定性会直接影响到当前的项目。这时就需要排除掉该SNAPSHOT,并且在当前项目中声明该类库的某个正式发布的版本。还有一些情况,你可能也想要替换某个传递性依赖,比如Sun JTA API,Hibernate依赖于这个JAR,但是由于版权的因素,该类库不在中央仓库中,而Apache Geronimo项目有一个对应的实现。这时你就可以排除SunJAT API,再声明Geronimo的JTA API实现。
<project> <modelVersion>4.0.0</modelVersion> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>project-a</artifactId> <version>1.0.0</version> <dependencies> <dependency> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>project-b</artifactId> <version>1.0.0</version> <exclusions> <exclusion> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>project-c</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.juvenxu.mvnbook</groupId> <artifactId>project-c</artifactId> <version>1.1.0</version> </dependency> </dependencies> </project>
上述代码中,项目A依赖于项目B,但是由于一些原因,不想引入传递性依赖C,而是自己显式地声明对于项目C 1.1.0版本的依赖。代码中使用exclusions元素声明排除依赖,exclusions可以包含一个或者多个exclusion子元素,因此可以排除一个或者多个传递性依赖。需要注意的是,声明exclusion的时候只需要groupId和artifactId,而不需要version元素,这是因为只需要groupId和artifactId就能唯一定位依赖图中的某个依赖。
有些依赖的版本都是相同的,而且可以预见,如果将来需要升级,这些依赖的版本会一起升级。
使用properties元素定义Maven属性,例如并定义一个version子元素,其值为2.5.6。有了这个属性定义之后,Maven运行的时候会将POM中的所有的${version}替换成实际值2.5.6。
优化依赖1 查看当前项目的已解析依赖
mvn dependency:list
2 查看当前项目的已解析依赖树
mvn dependency:tree
进一步查看已解析依赖的信息。当这些依赖经Maven解析后,就会构成一个依赖树,通过这棵依赖树就能很清楚地看到某个依赖是通过哪条传递路径引入的。
“+-”符号表示该包后面还有其它依赖包, “-”表示该包后面不再依赖其它jar包
3 分析当前项目的已解析依赖
mvn dependency:analyze
- 首先是Used undeclared dependencies,意指项目中使用到的,但是没有显式声明的依赖。这种依赖意味着潜在的风险,当前项目直接在使用它们,例如有很多相关的Java import声明,而这种依赖是通过直接依赖传递进来的,当升级直接依赖的时候,相关传递性依赖的版本也可能发生变化,这种变化不易察觉,但是有可能导致当前项目出错。
- 其次是Unused declared dependencies,意指项目中未使用的,但显式声明的依赖,这里有spring-core和spring-beans。需要注意的是,对于这样一类依赖,我们不应该简单地直接删除其声明,而是应该仔细分析。由于dependency:analyze只会分析编译主代码和测试代码需要用到的依赖,一些执行测试和运行时需要的依赖它就发现不了。对于这样一类依赖,我们不应该简单地直接删除其声明,而是应该仔细分析。有时候确实能通过该信息找到一些没用的依赖,但一定要小心测试。



