类似 iOS 的 cocoaPod,是一个跨平台的项目管理工具,作用域 Java平台项目,使用命令行完成重复,繁琐的构建。从中央仓库帮我们下载并管理项目依赖的 jar 包,自动管理这些 jar 包依赖的其他 jar 包。
boot 中放着 maven 的类加载器
conf 中
setting.xml 配置 全局的文件
额外的配置:Maven 在构建大型业务项目的时候,十分耗内存,可以为 Maven 的运行配置 JVM 内存:设置 Maven_OPTS 环境变量(中间是空格):-Xms128m Xmx512m
第一个 marven 程序
- Maven 规定了一套默认的项目格式:
src/main/java
– 存放项目的 .java 文件src/main/resources
– 存放项目资源文件,如 spring、structs2 配置文件,db.propertiessrc/main/webapp
– 存放 jsp,css,image 等文件src/test/java
– 存放所有测试 .java 文件,如 JUnit 测试类src/test/resources
– 测试资源文件(如果不写,默认和 src/main/resources 一样)pom.xml
– 主要要写的 maven 配置文件target
– 项目由 maven 自动输出位置
- 按照下面的目录结构创建一个目录结构
节点 | 描述 |
---|---|
project | 工程的根标签 |
modelVersion | 模型版本需要设置为 4.0 |
groupId | 工程组的唯一标识,在组织或者项目中是唯一的,例如一个银行组织 com.companyname.project-group 拥有所有和银行相关的项目 |
artifactId | 工程的标识,通常是工程的名称,groupId和artifactId 一起定义了 artifact 在仓库中的位置 |
version | 工程的版本号,在 artifact 仓库中,用来区分不同的版本 com.company.bank:consumer-banking:1.0 com.company.bank:consumer-banking:1.1 |
切换到项目根目录下(在可以看到pom.xml的文件夹下),分别执行下列命令并观察:
- 执行 mvn compile 命令
- 编译源代码
- 行mvn clean命令
- 清除产生的项目
- 执行mvn compile命令
- 执行mvn test命令
- 运行测试
- 执行mvn package命令
- 打包
- 执行mvn site命令
- 产生 site
- 执行mvn install命令
- 在本地 Repository 中安装 jar
- 目录:
/Users/Ammar/.m2
通过 maven 命令生成 maven 项目骨架
执行命令:mvn archetype:generate
可以看到有很多的archetype,每个前面有一个序号,输入序号(或者直接回车,选的就是quick-start):
- groupId:
cn.lizhaoloveit.maven
- artifactId:
HelloMaven
- Define value for property ‘version’ 1.0-SNAPSHOT :
1.0
- package:
/n
自动创建所有目录
mvn archetype:create -DgroupId=[your group id] -DartifactId=[your archetype id] -DarchetypeArtifactId=maven-archetype-webapp
如果mvn archetype:generate列出内容过多,或者下载过慢,运行:
mvn archetype:generate -DarchetypeCatalog=internal
只列出系统内部有的archetype;
Maven 安装 jar 包
mvn install:install-file -Dfile=jar包的位置 -DgroupId=上面的groupId -DartifactId=上面的artifactId -Dversion=上面的version -Dpackaging=jar
eclipse 关联 Maven
构建 web 项目
web.xml is missing and <failOnMissingWebXml> is set to true
报错,缺少 web.xml,在 src/main/webapp/ 下创建 WEB-INFO 文件夹和 web.xml 文件。
导入需要依赖的 jar
1 | <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"> |
安装 tomcat7 插件,
1 | <plugin> |
pom-war-jar
pom 工程:用在父级工程或聚合工程中,用来做 jar 包的版本控制
war 工程:将会打包成 war,发布在服务器上的工程,如网站或服务
jar 工程:将会打包成 jar,用作 jar 包使用
jdk9 后还可以打包 jmod(Java 模块化),厉害的是这个通过 jlink命令,可以直接将jmode打包为对应环境的可执行程序,告别了让用户安装 jdk 的步骤,现在也有对应的 maven 插件了
以淘淘商城为例:
pom 工程
- taotao-parent :
- 是整个项目的父工程,它是一个 pom 工程,用来做整个项目的版本控制,也就是项目中所有要使用到的 jar 包的版本都集中由父工程管理。这样你在写其他工程 pom 文件中 maven 依赖时就不需要写版本号了,当然所有的项目都要先继承它才行
- taotao-manager
- 创建 taotao-manager 用来做聚合工程,它也是一个 pom 工程,创建四个 model 分别是 taotao-manager-pojo, taotao-manager-dao, taotao-manager-service, taotao-manager-web,同时会自动生成 4 个独立的 maven 工程。聚合工程知识用来帮助其他模块构建的工具,本身并没有实质的内容,具体每个工程代码的编写还是在生成的工程中去写。
- 使用聚合工程 taotao-manager 的意义就是:原本这些模块也是一个个独立的工程,现在将它们聚合到 taotao-manager 中,这样我们构建项目的时候就只要构建 taotao-manager一个就行了,我们只要使用 maven 构建这个聚合工程 taotao-manager 就行了不用去操心模块的构建,比如 install 时,只要 install taotao-manager 就行。总之就是简化操作,正常的编码工作还是在对应的 taotao-manager-pojo, taotao-manager-dao, taotao-manager-service,taotao-manager-web 工程中进行的。
war 工程
taotao-rest, taotao-portal 这些
这些功臣个都是要部署在服务器上的,所以要打包成 war 形式,这些工程有的是用户通过浏览器直接访问,有的是通过发布服务被别的工程调用。
jar 工程
taotao-common
这个就是打包成 jar 的工程,它就是存放一些其他工程都会使用的类,工具类。我们可以在其他工程的 pom 文件中去引用它,和引用别的 jar 包没什么区别
1 | <depandency> |
坐标和依赖
坐标
Maven 中坐标就是用来规范 maven 唯一标示一个构建的方法
- groupId:项目名称
- artifactId:项目中的 项目/模块名称
- version:项目版本
- packing:打包的方式,比如 jar、war、pom 等(项目名-版本号.打包方式)
- classifier:附属构件,比如source,javadoc等,一般不会直接定义 classifier(分类器,分级器),附加构件不是直接定义的,是插件帮助构件的。
Maven 管理依赖
- 如何引入 jar 包
在代码开发时,如果需要使用第三方 jar 包提供的类库,那么需要在 pom.xml 加入该 jar 包依赖。
1 | <dependencies> |
- 依赖传递
pom.xml 引入 zookeeper jar 包依赖,当 Maven 解析该依赖时,需要引入的 jar 包不仅仅只有 zookeeper,还会有 zookeeper 内部依赖的 jar 包,还会有 zookeeper 内部依赖的 jar 包依赖的 jar 包…,依赖关系不断传递,直至没有依赖。
- 包冲突的产生
假设 A->B->C->D1, E->F->D2。D1, D2 分别为 D 的不同版本。如果 pom.xml 文件中引入了 A 和 E 后,按照 Maven 依赖传递原则,工程内需要引入的实际 jar 包,A,B,C,D1和E,F,D2,D1和D2产生包冲突
- 解决包冲突
Maven 解析 pom.xml 文件时,同一个 jar 包只会保留一个,这样会避免引入两个 jar 包导致的工程运行不稳定性。
- 最短路径优先
- Maven 面对 D1 和 D2 时,会默认选择最短路径的那个 jar 包,即D2,E->F->D2比 A->B->C->D1 路径短1.
- 最先声明优先
- 如果路径一样,A->B->C1, E->F->C2 那么久选择最先声明
移除依赖,如果不想通过 A->B->C->D1 引入 D1 的话,那么再声明引入 A 的时候,将 D1 排除。
将 zookeeper 的 jline 依赖排除
1 | <dependency> |
- 检测包冲突工具
1 | mvn dependency:help |
依赖管理解决问题:
当同一个工程内有多个模块时,并且要求多个模块使用某个 jar 包的相同版本,为了方便统一版本号,升级版本号,需要提取出一个父亲模块来管理子模块共同依赖的 jar 包版本。
有两个模块 projectA, projectB,它们依赖分别如下所示:
projectA:
1 | <project> |
project B
1 | <project> |
projectA 和 projectB 共同依赖了 group-a/artifact-b/1.0,提取公共依赖,生成 parent,parent 依赖如下:
1 | <project> |
则 projectA 和 projectB 均不需要指定 group-a/artifact-b 的 version 信息,未来升级 version 信息时,只需要在 parent 内部指定。
project A
1 | <project> |
projectB
1 | <project> |
依赖范围 scope
scope 的 分类
compile
- 默认就是 compile,compile意味着被依赖的项目需要参与当前项目的编译,后续的测试,运行周期也参与其中,是一个比较强的依赖,打包的时候通常需要包含进去。
test
- 依赖项目仅仅参与测试相关的工作,包括测试代码的编译,执行,比如典型的如 junit。
runtime
- runtime 表示被依赖项目无需参与项目的编译,不过后期测试和运行周期需要参与,与 compile 相比,跳过编译而已,正在终端项目(非开源,企业内部系统)中,和 comiple 区别不大,比较常见的对应的 API jar 是 comiple 的,具体是先是 runtime 的,oracle jdbc 驱动jar 就是个很好的例子,一般 scope 为 runtime ,runtime 依赖通常和 optional 搭配使用,optional 为 true,可以用 A 实现,也可以用 B 实现。
provided
- 意味着打包的时候可以不用包进去,别的设施会提供,该依赖可以参与编译,测试,运行等周期,相当于 compile,但是在打包阶段做了 exclude 的动作。
system
- 被依赖的项目不会从 maven 仓库抓,而是从本地文件系统拿,一定需要配合systemPath属性使用,与 provided相同。
scope 的依赖传递:
A->B->C 当前项目为 A, A依赖于B,B依赖于C,知道B在A项目中的 scope,那么 当 C 是test或者provided时,A 不依赖 C,否则 A依赖C,C的scope继承于B的scope(对于A来说)
optional
依赖的可选:可选项目不会被传递;
如果A项目依赖B项目;B项目依赖C或者D(比如一个基础项目,兼容了MYSQL或者ORACLE,那么他可能依赖mysql-connector或者classes12.jar)
但是在A项目运行的时候,只需要C或者D其中一个就可以了。那么对于B项目来说,C和D项目就可以设置optional为true;在A项目中单独设置C或者D的依赖;
举例:
比如 mybatis 和 log4j 或dbcp
exclusion 排除
移除依赖,如果不想通过 A->B->C->D1 引入 D1 的话,那么再声明引入 A 的时候,将 D1 排除。解决依赖冲突问题
最佳实践
- 项目中源代码使用的 jar 包一定在 pom.xml 中显示引用
- 经常 check 一下包冲突,检查是否需要处理
- 当使用多个模块时, parent 一定要使用包管理模块来规范 jar 包版本,而不是包依赖模块直接引入依赖。
Maven 生命周期
在Maven出现之前,项目构建的生命周期就已经存在,软件开发人员每天都在对项目进行清理、编译、测试及部署。虽然大家都在不停地做构建工作,但公司和公司间、项目和项目间,往往使用不同的方式做类似的工作。有的项目以手工的方式在执行编译测试,有的项目写了自动化脚本执行编译测试。可以想象的是,虽然各种手工方式十分类似,但不可能完全一样;同样地,对于自动化脚本,大家也是各写各的,能满足自身需求即可,换个项目就需要重头再来。
Maven的生命周期就是为了对所有的构建过程进行抽象和统一。
Maven 从大量项目和构建工具中学习和反思,总结了一套完善、易扩展的生命周期,这个生命周期包含了项目的清理、初始化、编译、测试、打包、集成测试、验证、部署和站点生成等几乎所有构建步骤。几乎所有的项目的构建,都能映射到这样一个生命周期上。
Maven 的生命周期是抽象的,意味着生命周期本身不做任何实际的工作,Maven 的设计中,实际的任务,都交由插件来完成。
每个构建步骤,都可以绑定一个或者多个插件行为,Maven 为大多数构建步骤编写并绑定了默认插件,编译是由 maven-compiler-plugin 完成的,测试是由 maven-surefire-plugin 完成的,当用户有特殊需要的时候,也可以配置插件定制构建行为,甚至自己编写插件。
三套互相独立的生命周期
clean、default、site。
- clean 生命周期的目的是清理项目,
- default 生命周期的目的是构建项目,
- site 生命周期的目的是建立项目站点。
clean
目的是清理项目,包含3个阶段:
- pre-clean 执行一些清理前需要完成的工作
- clean 清理上一次构建生成的文件
- post-clean 执行一些清理后需要完成的工作
default 生命周期
default 生命周期定义了 真正构建时所需要执行的所有步骤,阶段如下:
- validate
- initialize
- generate-sources
- process-sources 处理项目主资源文件,一般来说,是对
src/main/resources
目录的内容进行变量替换等工作后,复制到项目输出的主classpath
目录中 - generate-resources
- process-resources
- compile 编译项目的源码,编译
src/mian/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 最终将包复制到远程仓库。
site 生命周期
目的是建立和发布项目站点,Maven能够基于 pom 所包含的信息,自动生成一个站点,方便团队交流和发布项目信息。生命周期如下:
- pre-site 执行一些在生成项目站点之前需要完成的工作
- site 生成项目站点文档
- post-site 执行一些生成项目站点之后需要完成的工作
- site-deploy 将生成的项目站点发布到服务器上
package 和 install 的区别
mvn clean package
依次执行了clean、resources、compile、testResources、testCompile、test、jar(打包)等7个阶段。mvn clean install
依次执行了clean、resources、compile、testResources、testCompile、test、jar(打包)、install等8个阶段。mvn clean deploy
依次执行了clean、resources、compile、testResources、testCompile、test、jar(打包)、install、deploy等9个阶段。package命令完成了项目编译、单元测试、打包功能,但没有把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库和远程maven私服仓库
install命令完成了项目编译、单元测试、打包功能,同时把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库,但没有布署到远程maven私服仓库
deploy命令完成了项目编译、单元测试、打包功能,同时把打好的可执行jar包(war包或其它形式的包)布署到本地maven仓库和远程maven私服仓库