Maven入门
有用过Maven,但是一直都不知道是什么~被人一问,发现好像有点东西。随口一说是管理Jar包的,一般都是复制粘贴的。却被人说是啊是啊,但是复制粘贴是别人的又不是你的。说的好像jar包就是自己的一样(白眼.jpg)。——fzj
一.Maven的作用:
是一个项目管理工具。跨平台对外提供了一直的操作接口。统一开发规范与工具,统一管理jar包,还能自动下载构件。
二.安装:
链接放这里:https://www.cnblogs.com/zhaoyang/archive/2012/01/07/2315443.html(Maven的安装与配置)
三.Maven 目录:
bin目录:该目录包含了mvn运行的脚本,这些脚本用来配置Java命令,准备好classpath和相关的Java系统属性,然后执行Java命令。
boot目录:该目录质保函一个文件,名字为plexus-classworlds-2.6.0.jar,是一个类加载器框架,相对于默认的Java类加载器,它提供了更加丰富的语法以及方便配置,Maven使用该框架加载自己的类库。
conf目录:该目录包含了一个非常重要的配置文件叫settings.xml。直接修改该文件,就能在电脑上全局地定制Maven的行为。一般都是复制一份在桌面上,用记事本打开,然后修改,然后替换以前那一份。
lib目录:该目录包含了所有Maven运行时需要的Java类库,需要用到的第三方依赖等。
四.pom.xml
这才是Maven最核心的东西,很有必要去解析一下。
打开pom.xml,你可以看到原始的
1 <project xmlns="http://maven.apache.org/POM/4.0.0" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4 <modelVersion>4.0.0</modelVersion> 5 <groupId>com.baidu</groupId> 6 <artifactId>MavenProject</artifactId> 7 <version>0.0.1-SNAPSHOT</version> 8 <packaging>war</packaging> 9 <dependencies> 10 <dependency> 11 <groupId>ch.inftec.ju</groupId> 12 <artifactId>ju-dbutil-test-mysql</artifactId> 13 <version>4.1</version> 14 </dependency> 15 </dependencies> 16 </project>
第1~3行就是一些简单的声明,xml文件都有的
第4行,modelVersion 是指定了当前的Maven模型的版本号,对于Maven2和Maven3来说,它只能是4.0.0
第5行,groupId 这个应该是公司名或是组织名。一般来说groupId是由三个部分组成,每个部分之间以"."分隔,第一部分是项目用途,比如用于商业的就是"com",用于非营利性组织的就是"org";第二部分是公司名,比如"tengxun"、"baidu"、"alibaba";第三部分是你的项目名
第6行,artifactId 可以认为是Maven构建的项目名,比如你的项目中有子项目,就可以使用"项目名-子项目名"的命名方式
第7行,version 这个是版本号,SNAPSHOT意为快照,说明该项目还在开发中,是不稳定的版本。
注意,在Maven中,groupId、artifactId、version三个元素生成了一个Maven项目的基本坐标。
第8行,packing 项目打包的类型,可以使用jar、war、rar、ear、pom,默认是jar。
dependencies和dependency:
前者包含后者。前面说了,Maven的一个重要作用就是统一管理jar包,为了一个项目可以build或运行,项目中不可避免的,会依赖很多其他的jar包,在Maven中,这些依赖就被称为dependency。
这里引入一个概念,就是本地仓库和远程仓库。
官方下载的本地仓库的配置存放在"%MAVEN_HOME%\conf\settings.xml"里面,找一下"localRepository"就可以了;
MyEclipse默认的本地仓库的地址在"{user.home}/.m2/repository"路径下,同样找一下"localRepository"就可以找到MyEclipse默认的本地仓库了。
本地仓库和远程仓库是这样的,Maven工程首先会从本地仓库中获取jar包,当无法获取指定jar包时,本地仓库会从远程仓库(中央仓库)中下载jar包,并放入本地仓库以备将来使用。
properties
properties是用来定义一些配置属性的,例如project.build.sourceEncoding(项目构建源码编码方式),可以设置为UTF-8,防止中文乱码,也可定义相关构建版本号,便于日后统一升级。
build
build表示与构建相关的配置,比如build下有finalName,表示的就是最终构建之后的名称。
接着解释一下Maven的目录结构:
- main目录下是项目的主要代码,test目录下存放测试相关的代码
- 编译输出后的代码会放在target目录下
- src/main/java下存放Java代码,src/main/resources下存放配置文件
- 这里没有webapp,Web项目会有webapp目录,webapp下存放Web应用相关代码
- pom.xml是Maven项目的配置文件
关于依赖的配置:这里引 嘟嘟独立 的一张图:
我觉得这个图片可真直观~厉害厉害
下面讲讲依赖范围(即<scope>)
是什么?
maven 项目在不同的阶段引入到classpath中的依赖是不同的,例如,编译时,maven 会将与编译相关的依赖引入classpath中,测试时,maven会将测试相关的的依赖引入到classpath中,运行时,maven会将与运行相关的依赖引入classpath中,而依赖范围就是用来控制依赖于这三种classpath的关系。总结起来就是三种:编译classpath,测试classpath、运行classpath。
有什么?有好几种呢。
compile(编译依赖范围)、test(测试依赖范围)、provided(已提供依赖范围)、
runtime(运行时依赖范围)、system(系统依赖范围)、import(引入依赖范围)
怎么用?
1)编译依赖范围(compile),该范围就是默认依赖范围,此依赖范围对 于编译、测试、运行三种classpath都有效,举个简单的例子,假如项目中有spring-core的依赖,那么spring-core不管是在编译,测试,还是运行都会被用到,因此spring-core必须是编译范围(构件默认的是编译范围,所以依赖范围是编译范围的无须显示指定)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>2.5</version>
<scope>compile</scope> <!--默认为该依赖范围,无须显示指定--〉
</dependency>
2)测试依赖范围(test),顾名思义就是针对于测试的,使用此依赖范围的依赖,只对测试classpath有效,在编译主代码和项目运行时,都将无法使用该依赖,最典型的例子就是 Junit, 构件在测试时才需要,所以它的依赖范围是测试,因此它的依赖范围需要显示指定为<scope>test</scope> ,当然不显示指定依赖范围也不会报错,但是该依赖会被加入到编译和运行的classpath中,造成不必要的浪费 。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
3)已提供依赖范围(provided),使用该依赖范围的maven依赖,只对编译和测试的classpath有效,对运行的classpath无效,典型的例子就是servlet-api, 编译和测试该项目的时候需要该依赖,但是在运行时,web容器已经提供的该依赖,所以运行时就不再需要此依赖,如果不显示指定该依赖范围,并且容器依赖的版本和maven依赖的版本不一致的话,可能会引起版本冲突,造成不良影响。
<dependency>
<groupId>javax-servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
4)运行时依赖范围(runtime),使用该依赖范围的maven依赖,只对测试和运行的classpath有效,对编译的classpath无效,典型例子就是JDBC的驱动实现,项目主代码编译的时候只需要JDK提供的JDBC接口,只有在测试和运行的时候才需要实现上述接口的具体JDBC驱动。
5)系统依赖范围(system),该依赖与classpath的关系与 provided依赖范围完全一致,但是系统依赖范围必须通过配置systemPath元素来显示指定依赖文件的路径,此类依赖不是由maven仓库解析的,而且往往与本机系统绑定,可能造成构件的不可移植,因此谨慎使用,systemPath元素可以引用环境变量:
<dependency>
<groupId>javax.sql</groupId>
<artifactId>jdbc-stext</artifactId>
<version>2.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/rt.jar</systemPath>
</dependency>
6)导入依赖范围(import),该依赖范围不会对三种classpath产生影响,该依赖范围只能与dependencyManagement元素配合使用,其功能为将目标pom文件中dependencyManagement的配置导入合并到当前pom的dependencyManagement中。
这里又引 嘟嘟独立 的一张图:(除import以外的各种依赖范围与三种classpath的关系如下:)
Maven传递性依赖(Optional Dependencies):
是什么?
当一个项目A依赖另一个项目B时,项目A可能很少一部分功能用到了项目B,此时就可以在A中配置对B的可选依赖。举例来说,一个类似hibernate的项目,它支持对mysql、oracle等各种数据库的支持,但是在引用这个项目时,我们可能只用到其对mysql的支持,此时就可以在这个项目中配置可选依赖。
简单来说就是:A-->B,B-->C == A-->C。配置可选依赖的原因:1、节约磁盘、内存等空间;2、避免license许可问题;3、避免类路径问题,等等。
有了传递性依赖机制,在使用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,所以根据第一原则,A->D->X(2.0)路径短,所以X(2.0)会被解析使用 - 第一声明者优先
如果路径都一样长的话,第一原则就不行了,比如 A->B->Y(1.0)、A->C->Y(2.0),Y(1.0)和Y(2.0)的路径一样,所以这时候根据第二原则,先声明的被解析。
可选依赖:
如图,项目中A依赖B,B依赖于X和Y,如果所有这三个的范围都是compile的话,那么X和Y就是A的compile范围的传递性依赖,但是如果我想X,Y不作为A的传递性依赖,不给他用的话。就需要下面提到的配置可选依赖。
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>com.juvenxu.mvnbook</groupId> 4 <artifactId>project-b</artifactId> 5 <version>1.0.0</version> 6 <dependencies> 7 <dependency> 8 <groupId>mysql</groupId> 9 <artifactId>mysql-connector-java</artifactId> 10 <version>5.1.10</version> 11 <optional>true</optional> 12 </dependency> 13 <dependency> 14 <groupId>postgresql</groupId> 15 <artifactId>postgresql</groupId> 16 <version>8.4-701.jdbc3</version> 17 <optional>true</optional> 18 </dependency> 19 </dependencies> 20 </project>
配置也简单,在依赖里面添加
<optional>true</optional>
就表示可选依赖了,这样A如果想用X,Y就要直接显示的添加依赖了。
排除依赖:
有时候你引入的依赖中包含你不想要的依赖包,你想引入自己想要的,这时候就要用到排除依赖了,比如下图中spring-boot-starter-web自带了logback这个日志包,我想引入log4j2的,所以我先排除掉logback的依赖包,再引入想要的包就行了
排除依赖代码结构:
1 <exclusions> 2 <exclusion> 3 <groupId>org.springframework.boot</groupId> 4 <artifactId>spring-boot-starter-logging</artifactId> 5 </exclusion> 6 </exclusions>
这里注意:声明exclustion的时候只需要groupId和artifactId,而不需要version元素,这是因为只需要groupId和artifactId就能唯一定位依赖图中的某个依赖。
归类依赖:
有时候我们引入的很多依赖包,他们都来自同一个项目的不同模块,所以他们的版本号都一样,这时候我们可以用属性来统一管理版本号
1 <project> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>com.juven.mvnbook.account</groupId> 4 <artifactId>accout-email</artifactId> 5 <version>1.0.0-SNAPSHOT</version> 6 <properties> 7 <springframework.version>1.5.6</springframework.version> 8 </properties> 9 <dependencies> 10 <dependency> 11 <groupId>org.springframework</groupId> 12 <artifactId>spring-core</artifactId> 13 <version>${springframework.version}</version> 14 </dependency> 15 <dependency> 16 <groupId>org.springframework</groupId> 17 <artifactId>spring-beans</artifactId> 18 <version>${springframework.version}</version> 19 </dependency> 20 </dependencies> 21 </project>
如图所示,先通过
</properties> 这里定义你先要的版本 </properties>
来定义,然后在下面依赖使用${}来引入你的属性。
本文有参考以下文章:http://tengj.top/2018/01/01/maven/ (嘟嘟MD)