Maven ~ 从入门到入坑。
文章目录
目前的技术在开发中存在的问题。(why)。
一个项目就是一个工程。
问题:如果项目非常庞大,就不适合继续使用 package 来划分模块。最好是第一个模块对应一个工程,利于分工协作。
Maven:借助 Maven 就可以将一个项目拆分成多个工程。项目中需要的 jar 包必须手动“复制”、“粘贴”到 WEB\INF/lib 目录下。同样的 jar 包文件重复出现在不同的项目工程中,一方面浪费存储空间,另外也让工程比较臃肿。
Maven:借助 Maven,可以将 jar 包仅仅保存在“仓库”中,有需要使用的工程“引用”这个文件,并不需要重复复制。(对比对象的引用理解)。jar 包需要别人替我们准备好,或到官网下载。
不同技术的官网提供 jar 包下载的形式是五花八门的。
借助 Maven 可以以一种规范的方式下载 jar 包,因为所有知名框架或第三方工具的 jar 包已经按照统一的规范放在了 Maven 的中央仓库中。
以规范方式下载的 jar 包,内容也是可靠的。Tips:“统一的规范”不仅是对 IT 开发领域非常重要,对于整个人类社会都是非常重要的。eg. USB。—> USB 电灯,风扇。
- 一个 jar 包依赖的其他 jar 包需要自己看手动加入到项目中。
Maven 会自动将被依赖的 jar 包导入进来。
如果所有 jar 包之间的依赖关系都需要程序员自己非常清楚的了解,那么会极大增加学习成本。
Maven ~ what。
Maven是一个项目管理工具,它包含了一个项目对象模型 (Project Object Model),一组标准集合,一个项目生命周期(Project Lifecycle),一个依赖管理系统(Dependency Management System),和用来运行定义在生命周期阶段(phase)中插件(plugin)目标(goal)的逻辑。当你使用Maven的时候,你用一个明确定义的项目对象模型来描述你的项目,然后Maven可以应用横切的逻辑,这些逻辑来自一组共享的(或者自定义的)插件。
~
服务于 Java 平台的自动化构建一具。
Make —> Ant —> Maven —> Gradle。
~
- 构建。
以“Java 源文件”、”框架配置文件“、”JSP“、”HTML“、”图片“等资源为”原材料“,去”生产“一个可以运行的项目的过程。- 编译
- 部署
- 搭建
编译:Java 源文件(User.java)—> 编译 —> Class 字节码文件(User.class)—> 交给 JVM 去执行。
部署:一个 B / S 项目最终运行折并不是动态 Web 工程本身,而是这个动态 Web 工程”编译“的结果。
eg. 生的鸡 —> 处理 —> 熟的鸡。
动态 Web 工程 —> 编译、部署 —> 编译结果。
Maven 运行时环境。
一只煮熟的鸡。
Java 源代码经过编译后会生成 .class 文件,这些文件会以和源码相同的目录结构存在与 target/ 目录下。
自动化构建和构建环节。
构建就是以我们写的 Java 代码、框架配置文件、国际化等其他资源文件、jsp 页面和图片等静态资源文件作为”源材料“,去”生产“一个可以运行的项目的过程。
- 构建包含的环节。
清理:将以前编译得到的旧的 class 字节码文件删除,为下一次编译做准备。
编译:将 Java 源程序编译成 class 字节码文件。
测试:自动测试,自动调用 junit 程序。
报告:测试程序执行的结果。
打包:动态 Web 工程打 war 包,Java 工程打 jar 包。
安装:Maven 特定的概念——将打包得到的文件复制到”仓库“中的指定位置。
部署:将动态 Web 工程生成的 war 包复制到 Servlet 容器的指定目录下,使其可以运行。
- 自动化构建。
Maven 使用。
- JAVA_HOME 环境变量。
- 解压 Maven。(
放在无中文无空格路径下
)。 - 配置 Maven 环境变量。
MAVEN_HOME or M2_HOME(M2_HOME
是 2 版本的命名,3 版本也可以使用,向下兼容)。
geek@geek-PC:~$ vim ~/.bashrc
# maven.
export PATH=/home/geek/geek/tools_my/apache-maven-3.5.4/bin:$PATH
geek@geek-PC:~$ mvn -v
Apache Maven 3.5.4 (1edded0938998edf8bf061f1ceb3cfdeccf443fe; 2018-06-18T02:33:14+08:00)
Maven home: /home/geek/geek/tools_my/apache-maven-3.5.4
Java version: 1.8.0_241, vendor: Oracle Corporation, runtime: /home/geek/geek/tools_my/jdk1.8.0_241/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "4.15.0-30deepin-generic", arch: "amd64", family: "unix"
Maven 核心概念。
- 约定的目录结构。
- POM。
- 坐标。
- 依赖。
- 仓库。
- 生命周期 / 插件 / 目标。
- 继承。
- 聚合。
Maven 约定目录结构。
根目录:工程名。
src 目录:源码。
pom.xml 文件:Maven 工程的核心配置文件。
main 目录:存放主程序。
test 目录:存放测试程序。
java 目录:存放 Java 源码文件。
resources 目录:存放框架或其他工具的配置文件。
为什么要遵循约定的目录结构。?
- Maven 要负责我们这个项目的自动化构建,以编译为例,Maven 想要自动进行编译,那么 ta 必须知道 Java 源文件保存在哪里。
- 如果我们自己自定义的东西想要让工具或框架知道在哪里,有两种方法:
- 以配置文件的方式告诉框架。
<param-value>classpath:spring-context.xml</param-value>
- 遵守框架内部已经存在的约定。
log4j.properties
log4j.xml约定 > 配置 > 编码。
Maven 常用命令。
mvn clean —> 清理。
mvn validate —> 证实。
mvn compile —> 编译主程序。
mvn test —> 编译测试程序。
mvn package —> 打包。
mvn verify —> 证实。
mvn install —> 安装。
mvn site —> 生成站点。
mvn deploy —> 部署。
打开 /apache-maven-3.5.4/conf
,修改 settings.xml
配置文件。
在 <!-- -->
注释区域下添加 <localRepository> </localRepository>
标签对,在其中写入刚才创建的 maven_repository
完整路径。
<!-- localRepository
| The path to the local repository maven will use to store artifacts.
|
| Default: ${user.home}/.m2/repository
<localRepository>/path/to/local/repo</localRepository>
-->
<localRepository>/home/geek/geek/tools_my/maven_repository</localRepository>
- 更改 maven 仓库源。
打开 maven
的配置文件(一般在 maven 安装目录的 conf/settings.xml),在 <mirrors> </mirrors>
标签中添加 mirror 子节点。
<mirror>
<id>aliyunmaven</id>
<mirrorOf>*</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
POM。
POM。——Project Object Model。项目对象模型。
pom.xml 对象 Maven 工程是核心配置文件,与构建过程相关的一切设置都在这个文件中去设置。
重要程度相当于 web.xml 对象动态 Web 工程。
坐标。
- 数学中的坐标。
在平面上,使用 X、Y 两个向量可以唯一定位平面中任何一个点。
在空间上,使用 X、Y、Z 三个向量可以唯一定位空间中任何一个点。
- Maven 中的坐标。
使用以下三个向量在仓库中唯一定位一个 Maven 工程。
groupId:公司或组织域名的倒序 + 项目名。
artifactId:模块名。
version:版本。
Maven 工程的坐标与仓库中路径的对应关系。
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
org/framework/spring-context/5.0.2.RELEASE/spring-context-5.0.2.RELEASE.jar
仓库。
仓库的分类。
本地仓库:当前电脑上部署的仓库目录,为当前电脑上所有 Maven 工程服务。
远程仓库:私服(Nexus):架设在当前局域网环境下,为当前局域网内所有 Maven 工程服务。
中央仓库:架设在 Internet 上,为全世界所有 Maven 工程服务。
中央仓库的镜像:架设在各大洲,为中央仓库分担流量,减轻中央仓库的压力,同时更快的响应用户请求。
如果工作中电脑不能联网,就要用私服(Nexus)。
仓库中保存的内容:Maven 工程。
Maven 自身所需要的插件。
第三方框架或工具的 jar 包。(第一方:jdk。第二方:“我”)。
我们自己开发的 Maven 工程。
依赖。
Maven 解析依赖信息时会到本地仓库中去查找被依赖的 jar 包。
// 对于我们自己开发的 Maven 工程,使用 install 命令安装后就可以进入仓库。
// > mvn install
依赖的范围。
compiled
对主程序是否有效:√
对测试程序是否有效:√
是否参与打包:√
是否参与部署:√test
对主程序是否有效:×
对测试程序是否有效:√
是否参与打包:×
是否参与部署:×
典型 eg. junitprovided
对主程序是否有效:√
对测试程序是否有效:√
是否参与打包:×
是否参与部署:×
典型 eg. servlet-api.jar
如果不加 provided,运行后就会报java.lang.NullPointerException
。provided 理解。
当项目运行在服务器时,服务器已经提供了 Servlet(Tomcat),如果还用 Maven 工程中的 servlet-api.jar ,就会产生冲突。provided 仅表示在maven 工程中提供此 jar 包,在部署到服务器后就不再提供。
生命周期。
各个构建环节执行的顺序。
Maven 的核心程序中定义了抽象的生命周期,生命周期中各个阶段的具体任务由插件来完成。
Maven 核心程序是为了更好的实现自动化构建,按照这一特点执行生命周期中的各个阶段。(不论现在要执行生命周期中的哪一个阶段,都是从该生命周期最初的位置开始执行)。
Maven 有三套相互独立的生命周期。
Clean Lifecycle —> 在执行真正的构建之前进行一些清理工作。
Default Lifecycle —> 构建的核心部分。编译,测试,打包,安装,部署等。
Site Lifecycle —> 生成项目报告、站点,发布站点。
ta 们是相互独立的,你可以仅仅调用 clean 来清理工作目录,仅仅调用 site 来生成站点。当然也可以直接运行 mvn clean install site
运行所有这三套生命周期。
clean 生命周期。
pre-clean —> 执行一些需要在 clean 之前完成的工作。
clean —> 移除所有上一次构建生成的文件,
post-clean —> 执行一些需要在 clean 之后立即完成的工作。
site 生命周期。
pre-site —> 执行一些需要在生成站点文档之前完成的工作。
site —> 生成项目的站点文档。
post-site —> 执行一些需要在生成站点之后完成的工作,并且位部署做准备。
site-deploy —> 将生成的站点文档部署到特定的服务器上。
这里经常使用的是 site 阶段和 site-deploy 阶段,用以生成和发布 Maven 站点,这可是 Maven 相当强大的功能,Manager 比较喜欢,文档及统计数据自动生成。
Default 生命周期。
Default 生命周期是 Maven 生命周期中最重要的一个,绝大部分工作都发生在这个生命周期中。这里,只介绍一些比较重要和常用的阶段。
~
Validate
generate-sources
process-sources
generate-resources
process-resources —> 复制并处理资源文件,至目标目录,准备打包。
compile —> 编译项目的源代码。
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources —> 复制并处理资源文件,至目标测试目录。
test-compile —> 编译测试源代码。
process-test-classes
test —> 使用合适的单元测试框架进行测试,这些测试代码不会被打包或部署。
插件和目标。
生命周期的各个阶段仅仅定义了要执行的任务是什么。
各个阶段和插件的目标是对应的。
相似的目标由特定的插件来完成。
生命周期阶段 | 插件目标 | 插件 |
---|---|---|
compile | compile | maven-compiler-plugin:3.8.1 |
test-compile | testCompile | maven-compiler-plugin:3.8.1 |
可以将目标看作“调用插件功能的命令”。
使用 Maven 项目运行程序时,报出警告
Warning:java: source value 1.5 is obsolete and will be removed in a future release
Warning:java: target value 1.5 is obsolete and will be removed in a future release
Warning:java: To suppress warnings about obsolete options, use -Xlint:-options.
这是因为 Maven 内置 jdk 编译环境是 1.5 版本。
使用 maven 开发项目时,不会使用 IDEA 设置的 jdk,而是使用 Maven 内置的 jdk 编译器。
如需使用较新的版本,需要我们自己指定版本号。
只要在项目的 pom 文件中加入
(<project> </project>
标签内)。
<project>
<build>
<!--[...]-->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
<!--[...]-->
</build>
</project>
这样在下一次运行程序时就不会报出警告。
ps:jdk 1.5 和 jdk 1.8 主要差异。
List<String> list = new Array<>();
List<String> list = new Array<String>();// jdk 1.5 必须在 <> 中加入泛形。
依赖。
依赖的传递。
子工程可以继承父工程的依赖。
注意:非 compile 范围的依赖不能传递。(test, provided)。
-
依赖的排除。
- 也有传递性。
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
<exclusions>
<exclusion>
<groupId>...</groupId>
<artifactId>...</artifactId>
</exclusion>
</exclusions>
</dependency>
依赖的原则。
作用:解决模块工程之间的 jar 包冲突问题。
- 就近原则 / 路径最短者优先。
- 路径相同时标签先声明者优先。
统一管理依赖的版本。
在
<properties></properties>
标签内使用标签统一声明版本号。
在需要统一版本的位置,使用${自定义标签名}
引用。
<properties>
<geek.spring.version>5.0.2.RELEASE</geek.spring.version>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${geek.spring.version}</version>
<!--<version>5.0.2.RELEASE</version>-->
</dependency>
</dependencies>
继承。
eg. junit 是 test 范围的依赖不能传递,所以必然会分散在各个模块中,很容易造成版本不一致。
=》 解决:将 junit 依赖版本统一提取到“父”工程中,在子工程中声明依赖时不指定版本,以父工程中统一设定的版本为准,同时便于修改。
注意:父工程打包方式:pom。
在子工程的<parent></parent>
标签中要添加<relativePath>../Parent/pom.xml</relativePath>
,// 以当前工程的 pom.xml 文件为基准,写入父工程 pom.xml 文件的相对路径。
父工程中使用 <depenndencyManagement></depenndencyManagement>
。
<dependencyManagement>
<dependencies>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
子工程中使用。就不需要 <version></version>
标签了。
<dependencies>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
聚合。
如果没有聚合,例如 a <— b <— c。 b 依赖 a,c 依赖 b。
要运行 c,必须先 install a,再 install b。一个一个安装太麻烦。
聚合 —> 一键安装。
配置方式:。
<modules>
<module>../spring_coupling</module>
<module>../spring_factory</module>
</modules>
在此工程中运行 maven install。
所聚合的两个模块会一起安装。
Maven Web 工程自动部署。
将写好的工程打 war 包,放入 Tomcat 的 webapps 目录下,启动 Tomcat,Tomcat 会自动将 .war 包解压至当前目录下。
自动 ==》 cargo 插件。