1、什么是Maven
Maven是跨平台的项目管理工具。主要服务于基于Java平台的项目构建,项目管理和项目信息管理。那么怎么样才算是理想的项目构建呢?理想的项目构建就是必须具备高度自动化,跨平台,可重用的组件,标准化这些特性。
所谓的项目构建就是对项目进行清理、编译、测试、报告、打包、部署等一系列的过程称为项目的构建如下图所示:
如今我们构建一个项目需要用到很多第三方的类库,如写一个使用Spring的Web项目就需要引入大量的jar包。一个项目Jar包的数量之多往往让我们瞠目结舌,并且Jar包之间的关系错综复杂,一个Jar包往往又会引用其他Jar包,缺少任何一个Jar包都会导致项目编译失败。以往开发项目时,程序员往往需要花较多的精力在引用Jar包搭建项目环境上,而这一项工作尤为艰难,少一个Jar包、多一个Jar包往往会报一些让人摸不着头脑的异常。而Maven就是一款帮助程序员构建项目的工具,我们只需要告诉Maven需要哪些Jar包,它会帮助我们下载所有的Jar,极大地提升了开发效率。
Maven模型结构如下图所示:
它将每一个项目都当成了一个对象POM,通过依赖管理Dependency从仓库中进行存取。通过插件对Maven的生命周期进行管理,生命周期的每一个阶段都由相应的插件进行支持。由不同的命令激活不同的插件进行工作。
2、Maven规定的目录结构
Maven这个项目管理和构建自动化工具,越来越多的开发人员使用它来管理项目中的jar包。但是对于我们程序员来说,我们最关心的是它的项目构建功能。所以这里我们介绍的就是怎样用 maven 来满足我们项目的日常需要。Maven 使用惯例优于配置的原则 。它要求在没有定制之前,所有的项目都有如下的结构:
一个 maven 项目在默认情况下会产生 JAR 文件,另外 ,编译后 的 classes 会放在 basedir/target/classes 下面, JAR 文件会放在 ${basedir}/target 下面。这时有人会说了 , Ant 就没有那么多要求 ,它允许你可以自由的定义项目的结构。在这里不想引起口水战哈, 我个人觉得 maven 的这些默认定义很方便使用。好了 ,接下来我们来安装 maven 。
3、Maven安装与配置
1、到Apache官网下载maven安装包:apache-maven-3.5.0 http://maven.apache.org/download.cgi,解压即可使用。
2、maven环境变量配置,配置方式跟jdk有些类似。新建环境变量MAVEN_HOME(值为maven的根目录)、然后在PATH环境变量里加入%MAVEN_HOME%\bin;即可。
3、设置setting.xml文件,配置本地仓库。apache-maven-3.5.0\conf下有个setting.xml文件,打开这个xml文件,将localRepository标签的值设置成你本地仓库的路径即可。如下图所示:
4、在eclipse上安装maven插件。在线安装(不用下载安装包)是用的最多的安装方式,就是:Help –> Install New Software,然后输入 HTTP 地址来安装,把选项勾上,然后等待它完成,完成之后重启 eclipse 即可。但有一个很明显的缺点,就是慢,即使网络好也不见得很快安装好!
重启eclipse后,当你打开Window→ Preferences的时候,你应该能够在一个选项列表中看到一个Maven选项。
然后,Windows–>Prefrences–>Installations–>Add。installation name选maven的根目录,然后一直按确定就OK啦。如下图所示(本人早期安装的版本是3.2.1,因此图中显示的是3.2.1版本):
至此 maven的安装与Eclipse配置完成!
4、什么是Maven仓库和Maven坐标
Maven仓库用来存放Maven管理的所有Jar包。分为:本地仓库和中央仓库。
- 本地仓库:Maven本地的Jar包仓库。
- 中央仓库:Maven官方提供的远程仓库。
当项目编译时,Maven首先从本地仓库中寻找项目所需的Jar包,若本地仓库没有,再到Maven的中央仓库下载所需Jar包。
在Maven中,坐标是Jar包的唯一标识,Maven通过坐标在仓库中找到项目所需的Jar包。如下代码中,groupId和artifactId构成了一个Jar包的坐标。
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.39</version>
</dependency>
- groupId:所需Jar包的项目名
- artifactId:所需Jar包的模块名
- version:所需Jar包的版本号
5、项目依赖
- 传递依赖:如果我们的项目引用了一个Jar包,而该Jar包又引用了其他Jar包,那么在默认情况下项目编译时,Maven会把直接引用和简洁引用的Jar包都下载到本地。
- 排除依赖:如果我们只想下载直接引用的Jar包,那么需要在pom.xml中做如下配置:(将需要排除的Jar包的坐标写到下面)
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
若项目中多个Jar同时引用了相同的Jar时,会产生依赖冲突,但Maven采用了两种避免冲突的策略,因此在Maven中是不存在依赖冲突的。
- 短路优先
本项目——>A.jar——>B.jar——>X.jar
本项目——>C.jar——>X.jar
若本项目引用了A.jar,A.jar又引用了B.jar,B.jar又引用了X.jar,并且C.jar也引用了X.jar。在此时,Maven只会引用引用路径最短的Jar。
- 声明优先:若引用路径长度相同时,在pom.xml中谁先被声明,就使用谁。
Maven会自动根据dependency中的依赖配置,直接通过互联网在Maven中心库下载相关依赖包到本地Maven库中。
什么是依赖范围?
依赖声明主要包含如下元素:
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
其中依赖范围scope用来控制依赖和编译,测试,运行的classpath的关系,主要的是四种依赖范围如下:
1、compile:默认编译依赖范围。对于编译,测试,运行三种classpath都有效
2、test:测试依赖范围。只对测试的classpath有效
3、provided:已提供的依赖范围。对于编译,测试的classpath都有效,但对于运行无效。因为已经由容器提供。例如:servlet-api
4、runtime:运行时提供。例如:jdbc驱动
Maven项目的pom.xml文件结构解释:
<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">
<!-- 版本:4.0.0 -->
<modelVersion>4.0.0</modelVersion>
<!-- 组织名称:暂时使用 组织名称+项目名称 作为组织名称 -->
<!-- 组织名称:实际名称 按照访问路径规范设置,通常以功能作为名称:eg: junit spring -->
<groupId>my.test.maven</groupId>
<!-- 项目名称 -->
<artifactId>HelloWorld</artifactId>
<!-- 当前项目版本号:同一个项目开发过程中可以发布多个版本,此处标示0.0.1版 -->
<!-- 当前项目版本号:每个工程发布后可以发布多个版本,依赖时调取不同的版本,使用不同的版本号 -->
<version>0.0.1</version>
<!-- 名称:可省略 -->
<name>Hello</name>
<!-- 依赖关系 -->
<dependencies>
<!-- 依赖设置 -->
<dependency>
<!-- 依赖组织名称 -->
<groupId>junit</groupId>
<!-- 依赖项目名称 -->
<artifactId>junit</artifactId>
<!-- 依赖版本名称 -->
<version>4.9</version>
<!-- 依赖范围:test包下依赖该设置 -->
<scope>test</scope>
</dependency>
</dependencies>
</project>
6、Maven项目约定
约定大于配置:项目约定主要是规范开发人员编程,统一项目风格,简化操作和代码量。
maven也是这样的,约定如下图:
项目的目录结构如下图所示,其中pom.xml在其项目的根目录:
7、Maven编译插件
maven-compile-plugin,这个插件就如同名字所显示的这样,是用来编译源代码的。使用这个插件是在于有时候我们需要编译工程的时候,比如输入命令:mvn install ,但是系统编译的时候报错了,错误的信息如下:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.0.2:compile (default-compile) on project springJMS: Compilation failure: Compilation failure:
[ERROR] /home/frank/programcode/SpringJMSSample/src/main/java/huangbowen/net/jms/MessageSender.java:[6,1] error: annotations are not supported in -source 1.3
[ERROR]
[ERROR] (use -source 5 or higher to enable annotations)
[ERROR] /home/frank/programcode/SpringJMSSample/src/main/java/net/EmbedBrokerApp.java:[5,7] error: static import declarations are not supported in -source 1.3
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
从错误显示的信息我们就可以看出,这是因为编译的时候是默认用的javac 1.3版本的,太老了不支持代码里的特性。为了修改这个问题,我们需要设置编译器的版本。解决这个问题的办法也比较简单,将编译器的版本设定为1.7,在pom.xml文件中添加如下代码:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
8、Maven常用命令
1. 创建Maven的普通java项目:
mvn archetype:create -DgroupId=packageName -DartifactId=projectName
2. 创建Maven的Web项目:
mvn archetype:create -DgroupId=packageName -DartifactId=webappName-DarchetypeArtifactId=maven-archetype-webapp
3. 编译源代码: mvn compile
4. 编译测试代码:mvn test-compile
5. 运行测试:mvn test
6. 产生site:mvn site
7. 项目打包:mvn package
8. 在本地Repository中安装jar,即发布项目并提交到本地仓库:mvn install
9. 清除产生的项目(清理target目录下的文件):mvn clean
10. 查看项目的依赖树:mvn dependency:tree ,将项目的依赖树打印到文件中:mvn dependency:tree >> tree.txt
11. 生成eclipse项目:mvn eclipse:eclipse
12. 生成idea项目:mvn idea:idea
13. 组合使用goal命令,如只打包不测试:mvn -Dtest package
14. 编译测试的内容:mvn test-compile
15. 只打jar包: mvn jar:jar
16. 只测试而不编译,也不测试编译:mvn test -skipping compile -skipping test-compile
( -skipping 的灵活运用,当然也可以用于其他组合命令)
16. 清除eclipse的一些系统设置:mvn eclipse:clean
17. 用了maven后,你再也不需要用eclipse里的tomcat来运行web项目:mvn tomcat:run
18. 不想到处找源码了,直接获取项目里所依赖的jar包的源码:mvn dependency:sources
9、Maven项目构建实例
1、Maven工程创建
2、选择快速框架
3、输出项目名,包(Packaging,如果只是普通的项目,选jar就好了,如果是web项目就选war,这里我们选择jar)
groupid和artifactId被统称为“坐标”是为了保证项目唯一性而提出的,如果你要把你的项目弄到maven本地仓库去,你想要找到你的项目就必须根据这两个id去查找。
GroupID是项目组织唯一的标识符,实际对应JAVA的包的结构,是main目录里java的目录结构。ArtifactID就是项目的唯一的标识符,实际对应项目的名称,就是项目根目录的名称。
一般GroupID就填com.leafive.test这样子。
groupId一般分为多个段,这里只说两段,第一段为域,第二段为公司名称。域又分为org、com、cn等等许多,其中org为非营利组织,com为商业组织。举个apache公司的tomcat项目例子:这个项目的groupId是org.apache,它的域是org(因为tomcat是非营利项目),公司名称是apache,artigactId是tomcat。
groupId一般是域名的反写,也作为项目中类的包名,artifactId是工程名,也就是根文件夹的名字。
4、创建好的目录如下:
5、刚开始的pom.xml是这样的:
pom.xml文件是Maven的核心,你的项目需要什么Jar包就在pom.xml里面配置。当编译项目时Maven读取该文件,并从仓库中下载相应的Jar包。
6、现在我们添加mysql驱动包的依赖,在pom.xml中添加如下代码:
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.29</version>
</dependency>
</dependencies>
7、如上编辑pom.xml,然后保存(只需保存对pom.xml文件的修改,就能看到效果),那么Maven就会自动从中央仓库下载包,会发现这里多了一个包:
然后就可以正常使用这个包啦
8、其他想要找其他依赖包对应的dependency标签语句,可以在Maven中央仓库的官方网站找: http://mvnrepository.com/
查询最新的mysql-connector-java包的dependency的方法如下图:
10、依赖包的Snapshot版本与Release版本
Maven的Snapshot版本与Release版本:1. Snapshot版本代表不稳定、尚处于开发中的版本 ;2. Release版本则代表稳定的版本 。
什么情况下该用SNAPSHOT?
协同开发时,如果A依赖构件B,由于B会更新,B应该使用SNAPSHOT来标识自己。这种做法的必要性可以反证如下:
a.如果B不用SNAPSHOT,而是每次更新后都使用一个稳定的版本,那版本号就会升得太快,每天一升甚至每个小时一升,这就是对版本号的滥用。
b.如果B不用SNAPSHOT, 但一直使用一个单一的Release版本号,那当B更新后,A可能并不会接收到更新。因为A所使用的repository一般不会频繁更新release版本的缓存(即本地repository),所以B以不换版本号的方式更新后,A在拿B时发现本地已有这个版本,就不会去远程Repository下载最新的B。
不用Release版本,在所有地方都用SNAPSHOT版本行不行?
不行,正式环境中不得使用snapshot版本的库。 比如说,今天你依赖某个snapshot版本的第三方库成功构建了自己的应用,明天再构建时可能就会失败,因为今晚第三方可能已经更新了它的snapshot库。你再次构建时,Maven会去远程repository下载snapshot的最新版本,你构建时用的库就是新的jar文件了,这时正确性就很难保证了。稳定版应该依赖Release版本的jar包,这样更新时需要更新版本号,版本号没变时,依赖的jar包也不会变。
maven中的仓库分为两种,snapshot快照仓库和release发布仓库。snapshot快照仓库用于保存开发过程中的不稳定版本,release正式仓库则是用来保存稳定的发行版本。定义一个组件/模块为快照版本,只需要在pom文件中在该模块的版本号后加上-SNAPSHOT即可(注意这里必须是大写)。
maven会根据模块的版本号(pom文件中的version)中是否带有-SNAPSHOT来判断是快照版本还是正式版本。如果是快照版本,那么在mvn deploy时会自动发布到快照版本库中,而使用快照版本的模块,在不更改版本号的情况下,直接编译打包时,maven会自动从镜像服务器上下载最新的快照版本。如果是正式发布版本,那么在mvn deploy时会自动发布到正式版本库中,而使用正式版本的模块,在不更改版本号的情况下,编译打包时如果本地已经存在该版本的模块则不会主动去镜像服务器上下载。