我们最能感受到的Maven的好处应该是它的“自动化构建”与“管理依赖关系”两大功能,下面就看围绕这两大功能的Maven中的核心概念。
1.约定的目录结构
1.1 观点:约定>配置>编码
1.2 自动化构建的必然要求
• Maven要进行自动的编译,就必须知道源程序在哪里
• Maven要进行自动的测试,就必须知道测试程序在哪里
• Maven要自动的处理配置文件或资源文件,也必须知道它们的位置
1.3 Maven 使用约定优于配置的原则 。它要求在没有定制之前,所有的项目都有如下的主要目录结构:
一个 maven 项目在默认情况下会产生 JAR 文件,另外 ,编译后 的 .classe文件 会放在 ${basedir}/target/classes 下面;JAR文件会放在${basedir}/target 下面。如下图所示:
2.POM
2.1 Project Object Model:项目对象模型。Maven 程序的配置全部都体现在pom.xml配置文件中,所以pom.xml是一个Maven工程的核心配置。
<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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.guigu.maven</groupId>
<artifactId>MavenTest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MavenTest</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
在 POM 中,groupId,artifactId, packaging, version 叫作 maven 坐标,它能唯一的确定一个构件。有了 maven 坐标,我们就可以用它来指定我们的项目所依赖的其他项目,插件,或者父项目。
3.坐标
3.1 几何中的坐标
• 平面:使用x和y两个向量就可以唯一的定位平面中的任何一个点
• 空间:使用x和y还有z三个向量就可以唯一的定位空间中的任何一个点
3.2 使用 三个向量结合起来唯一的定位每一个Maven工程
• groupid:公司或组织的域名的倒序+项目名称
• artifactId:当前模块的名称
• version:当前模块的版本
<groupId>com.guigu.maven</groupId>
<artifactId>Hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
3.3 根据坐标到仓库中查找jar包
目录规范:groupId/artifactId/version/artifactId-version.jar,例如:
<groupId>com.guigu.maven</groupId>
<artifactId>Hello</artifactId>
<version>0.0.1-SNAPSHOT</version>
com/guigu/maven/Hello/0.0.1-SNAPSHOT/Hello-0.0.1-SNAPSHOT.jar
3.4 一个Maven工程必须执行“安装”操作后才能够进入本地仓库:mvn install
3.5 关于SNAPSHOT和RELEASE
SNAPSHOT:快照,开发过程中的不稳定版本
RELEASE:正式版
4.依赖管理
4.1 基本概念
当A jar包需要用到B jar包中的类时,我们就说A对B有依赖。例如:commons-fileupload-1.3.jar依赖于commons-io-2.0.1.jar。
4.2 直接依赖和间接依赖
如果A依赖B,B依赖C,那么A→B和B→C都是直接依赖,而A→C是间接依赖。
4.3 依赖的范围
当一个Maven工程添加了对某个jar包的依赖后,这个被依赖的jar包可以对应下面几个可选的范围:
test: 指测试范围有效,编译和打包时都不使用该依赖。
compile:(为默认值) 编译范围有效,编译和运行(打包)时都会将依赖存进去
provided: 测试、编译范围都有效,最后生成war包时不会加入,例如:servlet-api,编译的时候需要该文件,但是在打包的时候不需要把这个 jar 文件打在 WAR 中,因为servlet容器或者应用服务器会提供的。打进去会出现冲突。
runtime: 编译时不依赖,运行(打包)时依赖
其他:import、system等。
各个依赖范围的作用可以概括为下图:
4.4 依赖的传递性
当存在间接依赖的情况时,主工程对间接依赖的jar可以访问吗?这要看间接依赖的jar包引入时的依赖范围,只有依赖范围为compile时可以访问。例如:
4.5 依赖的原则
• 路径最短者优先
• 路径相同时,先声明者优先:声明先后指的是dependency元素声明的顺序
-
4.6 依赖的排除
• 在依赖Hello的时候,会有log4j的间接依赖版本加入到当前工程的运行时环境中
• 使用exclusions和exclusion元素将间接依赖中的不需要的jar包排除
<dependency>
<groupId >com.guigu.maven </groupId>
<artifactId >Hello </artifactId>
<version >0.0.1-SNAPSHOT </version>
<scope >compile </scope>
<exclusions >
<exclusion>
<groupId>log4j</ groupId>
<artifactId>log4j</ artifactId>
</exclusion>
</exclusions >
</dependency>
4.7 统一管理版本号:在pom.xml中定义常量
<!-- 定义一个常量值,便于在当前配置文件中重复使用 -->
<properties>
<!-- 定义常量值的标签名可以自行制定,并根据这个标签名来引用常量 -->
<guigu.spring.version >4.1.1.RELEASE </guigu.spring.version>
</properties>
<dependency>
<groupId >org.springframework </groupId>
<artifactId >spring-core </artifactId>
<version >${guigu.spring.version} </version>
</dependency>
说明:这样定制的好处:我们修改依赖版本修改的时候,只需要修改</properties>里面的内容就可以了,使用起来十分方便。
5.仓库管理
5.1 分类
• 本地仓库:conf\settings.xml配置文件中localrepository标签内配置的目录,为当前电脑上的所有Maven工程服务。
• 远程仓库
a.私服:架设在当前局域网环境下的一个Maven仓库服务器,为当前局域网内的所有Maven工程服务。
b.中央仓库:为了能够让全世界所有的Maven程序可以使用第三方框架或工具类的jar包等资源而架设的服务器。
c.中央仓库的镜像:为了分担中央仓库的负载,同时提升用户访问的速度,架设的镜像服务器。
5.2 仓库中都保存了些什么?
① Maven自身的插件
② 第三方jar包
③ 程序员自己开发的项目模块
6.生命周期
6.1 Maven怎么知道执行compile、test、package、install、deploy等等这些构建环节执行的顺序?
因为Maven已经将这个顺序在内部声明好了,执行构建操作时就按这个既定的顺序执行即可。
处理主程序中的资源文件:maven-resources-plugin:2.6:resources
编译主程序:maven-compiler-plugin:2.5.1:compile
处理测试程序的资源文件:maven-resources-plugin:2.6:testResources
编译测试程序:maven-compiler-plugin:2.5.1:testCompile
执行测试:maven-surefire-plugin:2.12.4:test
打印测试报告……
生成jar包:maven-jar-plugin:2.4:jar
执行安装:maven-install-plugin:2.4:install
6.2 生命周期
① 概念:Maven在内部定义的各个构建环节执行的顺序
② 目的:进一步确保自动化构建的实现
③ Maven中定义了三个生命周期:clean、default、site
a. clean: 主要目的是清理项目
pre-clean: 执行一些清理前需要完成的工作
clean: 清理上一次构建生成的文件
post-clean: 执行一些清理后需要完成的工作
b. default:定义了真正构建时所需要执行的所有步骤,它是生命周期中最核心的部分
validate
initialize
generate-sources
process-sources: 处理项目主资源文件。一般来说,是对src/main/resources目录的内容进行变量替换等工作后,复制到项目输出的主classpath目录中
generate-resources
process-resources
compile: 编译项目的主源码。一般来说,是编译src/main/Java目录下的Java文件至项目输出的主classpath
录中
process-classes
generate-test-sources
process-test-sources: 处理项目测试资源文件。一般来说,是对src/test/resources目录的内容进行变量替换等工作后,复制到项目输出的测试classpath目录中
generate-test-resources
process-test-resources
test-compile: 编译项目的测试代码,一般来说,是编译src/test/java目录下的Java文件至项目输出的测试classpath目录中
process-test-classes
test: 使用单元测试框架运行测试,测试代码不会打包或部署
prepare-package
package: 接受编译好的代码,打包成可发布的格式,如JAR
pre-integration-test
integration-test
post-integration-test
verify
install: 将包安装到Maven本地仓库,供本地其他Maven项目使用
deploy: 将最终的包复制到远程仓库,供其他开发人员和Maven项目使用
site生命周期: 建立和发布项目站点,Maven能够基于POM所包含的信息,自动生成站点
pre-site: 执行一些在生成项目站点之前需要完成的工作
c. site: 生成项目站点文档
post-site: 执行一些在生成项目站点之后需要完成的工作
site-deploy: 将生成的项目站点发布到服务器上
7.继承
由于非 compile范围的依赖信息是不能在 “依赖链”中传递的,所以有需要的工程只能单独配置。此时如果项目需要将各个模块的junit版本统一为4.9,那么到各个工程中手动修改无疑是非常不可取的。使用继承机制就可以将这样的依赖信息统一提取到父工程模块中进行统一管理。
7.1 创建父工程:packaging元素内设置为pom
7.2 继承父工程(在子工程当中 )
<!-- 继承父工程 -->
<parent>
<groupId >com.guigu.maven </groupId>
<artifactId >Parent </artifactId>
<version >0.0.1-SNAPSHOT </version>
<!-- 从当前子工程出发读取父工程pom.xml文件的相对路径 -->
<relativePath >../Parent/pom.xml </relativePath>
</parent>
7.3 删除子工程中重复的配置
7.4 在父工程中配置需要统一管理的依赖
<!-- 管理依赖的配置方式 -->
<dependencyManagement>
<dependencies >
<dependency>
<groupId> junit</groupId >
<artifactId> junit</artifactId >
<version> 3.8.1</ version>
<scope> test</ scope>
</dependency>
</dependencies >
</dependencyManagement>
7.5 删除子工程中被管理的依赖的版本号
<dependency>
<groupId >junit</groupId>
<artifactId >junit</artifactId>
<scope >test </scope>
</dependency>
注意:当存在继承的时候,安装工程需要先安装父工程。
8.聚合
8.1 要解决的问题:当一个项目是由多个工程组成的,那么安装到仓库时,需要手动逐一安装比较麻烦。
8.2 配置了聚合之后只需要安装总的聚合工程就能够自动安装各个子工程。
8.3 配置方式(在父工程中配置 )
<!-- 在总的聚合工程中配置聚合 -->
<modules>
<module >../Hello </module>
<module >../HelloFriend </module>
<module >../MakeFriends </module>
<module >../MavenWeb </module>
</modules>
9.总结
Maven提倡“约定优于配置”,它的项目的目录结构,测试方法的命名等都有一定的要求。
Maven是基于Pom的,一个Maven项目所有的配置都放置在 POM 文件中:定义项目的类型、名字,管理依赖关系,定制插件的行为等等。Pom文件之间还可以继承、聚合等。
Maven很强大,很大一方面是它的插件服务非常强大,Maven本身基本不怎么做事,它基本是调用一些插件来做事。Maven有3套生命周期,clean、compile、site,而每个生命周期中的每个步骤都有一个目标插件来支持。配置 每个插件时也都会指明插件的运行时机(即Maven生命周期中的某个步骤)。
Maven自带强大的依赖管理系统,配置了某个依赖,确定了某个构件的坐标,Maven就能帮我们自动下载构件。
Maven本身自带一个本地仓库;然后它又为全世界的Java开发者提供了一个免费的“中央仓库”,为了解决本地仓库频繁与中央仓库(存在于外网中)交互,导致效率低的问题,又衍生出了私有仓库,即私服(Nexus)。