maven:《Maven实战》读书笔记

一、前言

 本文章是 《Maven实战》徐晓斌著,机械工业出版社 的读书笔记。读这本书后,你将会掌握maven的功能(构建项目、管理依赖),以及这些功能的机制和实现原理(比如:当敲了命令mvn clean complier之后maven做了什么;dependenciesManagement和dependencies的区别;为什么idea默认的源码目录是src/main/java 等等)。
 另外,Maven教程|菜鸟教程 这个教程也很不错,配合此书一起阅读,能有更好的效果。

在这里插入图片描述

二、初识maven

2.1 什么是pom

POM( Project Object Model,项目对象模型 ) 是 Maven 工程的基本工作单元,是一个XML文件,包含了项目的基本信息,用于描述项目如何构建,声明项目依赖,等等。

2.2.1)pom文件所有标签的含义

参考菜鸟maven教程下的POM 标签大全详解

2.2 maven的主要作用

2.2.1)构建程序

构建程序,即清理,编译,打包。
清理:即删除编译输出目录下的文件。
编译:即将项目的java源码编译成.class,然后和项目的资源一起输出到编译输出目录下,maven默认的项目输出目录是/target/classes
打包:即将项目构建成jar包,或者war包,供其他项目引用或者部署到服务器上。

2.2.2)管理依赖

 一个项目往往要依赖很多其他已经非常成熟的第三方框架、工具。比如spring、springboot、fastjson、jedis等等。然后这些第三方框架往往又依赖了很多其他组织提供的第三方框架,这样一层一层的无限套娃。
 如果没有maven管理依赖,我们就要自己先引入spring的依赖,在引入spring,我们才能正常使用spring;如果有maven管理依赖,我们只要引入spring的坐标,maven自动会帮我们下载spring的依赖,甚至是spring的依赖的依赖!

2.2.2.1)构件

这是maven的一个概念,在maven中所有东西都是构件,插件是构件,第三方jar是构件,自己打包出来的snapshot.jar快照也是构件。而每个构件都会有独一无二的坐标。

2.2.2.2)坐标 (对应pdf 71页)

坐标,这是maven为了能唯一标识一个构件所引入的概念。一个坐标主要包括3个元素。

<groupId>com.baidu.cn</groupId>
<artifactId>studyProject</artifactId>
<version>1.0</version>

可以这么理解
groupId:一个组织、公司的id
artifactId:项目代号
version:项目的版本

然后上面的maven坐标就可以这么理解了:依赖 百度公司下的1.0版本的studyProject项目。

2.2.3)中央仓库(对应pdf 98页)

那么有了坐标,maven根据坐标在哪里下载呢?答案是maven的中央仓库:https://repo.maven.apache.org/maven2。为什么默认会到maven的中央仓库下载?那就要看后面的 maven约定大于配置 这一小节啦。

2.2.3.1)镜像(对应pdf 107页)

镜像的定义:如果仓库X可以提供仓库Y存储的所有内容,那么就可以任务X是Y的一个镜像。
常见的maven中央仓库镜像有阿里云maven镜像,但是既然有中央仓库了,为什么还要弄镜像仓库呢?

镜像的主要作用

  1. maven中央仓库是在国外的,可能访问不稳定、或者网速不够快。所以可以通过在自己公司的局域网里搭建镜像,提高下载的速度。
  2. 如果公司A部门写好了一个工具jar,其他部门也想引入这个jar。这时候就可以在公司的局域网部署仓库镜像,然后把这个工具jar放到仓库镜像上去,然后公司的同事从公司的镜像中下载即可,这样可以使公司的代码不外露。
  3. maven中央仓库并不是所有的jar都有的,由于版权原因,ojdbc不在maven的中央仓库上。所以为了大一统依赖的下载地址,也可以在自己本地或者公司局域网部署镜像仓库。
<!-- maven的语法 -->
<mirror> 
	<id>nexus-aliyun</id> 
	<name>Nexus aliyun</name> 
	<url>http://maven.aliyun.com/nexus/content/groups/public</url> 
	<mirrorOf>*</mirrorOf> 
</mirror>

<mirrorOf>*</mirrorOf> *是通配符,表示所有走远程仓库的请求都会走阿里云镜像。这里的语法详细阅读《Maven实战》一书
2.2.3.2)使用Nexus创建私服

TODO

2.3 maven的约定大于配置(对应pdf 157页)

不得不佩服,2001诞生的maven已经有了“约定大于配置”这个思想,而到现在springboot也一直沿用!约定大于配置,可以将常见的问题规范化,大家在解决这些常见问题的时候都约定俗成的遵守某一规范,可以减少很多沟通成本。

比如maven有以下约定
源代码目录默认为: src/main/java
测试目录默认为: src/test/java

 上面这两个只是maven其中的一些约定,那怎么看maven有哪些约定呢?这就要看超级pom.xml文件了,因为所有的pom文件都会继承超级pom,类似于java的所有类都会继承Object。
 那么超级pom文件在哪里呢?在$MAVEN_HOME/lib/maven-model-builder-x.x.x.jar中的org/apache/maven/model/pom-4.0.0.xml。只要你打开了超级pom,你就明白了maven有哪些约定了。包括但不限于:源码路径、编译目标文件夹路径、中央仓库等等。
 另外要说明的是,虽然pom是可以继承的,但是不是pom中的所有元素都可以被继承,以下是pom中常见的可以被继承的元素:

  • grouId、version:项目坐标的核心元素
  • description:项目描述
  • properties:自定义属性
  • dependencies:依赖
  • dependencyManagement:依赖版本管理信息
  • repositoies:项目自定义仓库配置
  • build:插件配置

三、动动手,来使用maven吧

3.1 maven的依赖机制

3.1.1)依赖范围(对应pdf 81页)

编译:就是在IDE里运行src/java/main的代码,就是使用编译classpath
测试:就是在IDE里运行src/java/test的代码,就是使用测试classpath
运行:就是代码写好了,用IDE打包后的jar或者war中的classpath

compile:默认的依赖范围。对编译、测试、运行三种classpath都有效。
test:只对测试的classpath有效。
provided:只对测试、编译的classpath有效。
runtime:只对测试、运行的classpath有效。
system:这种基本不用,忽略。

3.1.2)依赖传递(对应pdf 82页)

依赖传递说的是:jar包之间的依赖传递。比如说我项目要依赖spring,spring又依赖了log4j,这时候我在项目中的pom.xml中只要引入了spring的依赖,maven就会自动帮我引入log4j的依赖。

但是依赖传递会有那么两个问题(对应pdf 84页)
最短路径优先:项目的依赖关系(A -> B -> C -> X(1.0版本)、A -> D -> X(2.0版本)),这时候maven会根据依赖路径最短优先,只会依赖X2.0版本。
先声明优先:项目的依赖关系(A -> B -> Y(1.0版本)、A -> C -> Y(2.0版本)),依赖路径的长度都是2,这时候B和C的依赖,哪个依赖先在pom.xml中声明,便依赖哪个Y。

解决方法:

  • 提供依赖方在 项目的依赖设置为可选依赖
<!-- 当有使用方引用这个项目的依赖时,使用方就不会自动引入mysql这个依赖,因为它是可选依赖。 -->
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.10</version>
	<optional>true<optional>
</dependency>
  • 使用依赖放在 项目中排除依赖,切断依赖传递
<!-- 在使用方项目中依赖某个依赖,但是又不想引入这个依赖的依赖,可以用exclusion来切断依赖链路 -->
<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.10</version>
	<exclusions>
		<exclusion>
			<!-- 注意exclusion只需要groupId+artifactId,因为只要这2个就可以确定依赖了 -->
			<groupId>xxx</groupId>
			<artifactId>xxx</artifactId>
		<exclusion>
	</exclusions>
</dependency>

总结:optional只是切断依赖传递;但是不会影响依赖继承,optional的依赖还是可以被子项目继承!

3.1.3)依赖继承

 聚合项目下依赖继承的问题:比如 A项目下有两个子项目B、C、D。然后B依赖了spring,C依赖了mysql,D依赖了spring、mysql。然后为了整合子项目的依赖,并且避免子项目引入的依赖之间版本不一致导致的版本冲突问题,所以统一将spring、mysql依赖提升到A项目的pom.xml下。但是这时候因为依赖继承的机制,所有子项目都继承了spring和mysql,但是B项目明明只要依赖Spring,不需要依赖mysql,这时候就会使某些项目引入多余的依赖了。
 maven提供了dependencyManagent这个机制来解决这个问题,上述问题可以通过以下来解决

<!-- A项目的pom.xml -->
<!-- 使用dependencyManagent管理的只是依赖的版本,子项目继承这个项目的时候,可以省略依赖的版本信息 -->
<dependencyManagent>
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-core</artifactId>
	    <version>5.3.7</version>	
	</dependency>
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.10</version>
	</dependency>
</dependencyManagent>
<!-- B项目的pom.xml -->
<dependencys>
	<!-- 子项目在按需引入依赖,并且不需要注明版本,解决了不同子项目可能会引入同一个依赖的不同版本的问题 -->
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-core</artifactId>
	</dependency>
</dependencyManagent>

与dependencyManagent类似,pluginManagement是管理插件的版本

3.2 maven的三大生命周期(对应pdf 115页)

maven有三大生命周期:clean、default、site。 每个生命周期包含很多个阶段,然后每个阶段的实际行为由绑定了该阶段的插件来完成,如果该阶段没有绑定任何插件则不会有任何行为。一句话简述就是:maven的生命周期是抽象的,其实际行为由插件来完成

3.2.1)clean生命周期

clean是为了清理项目,clean生命周期包括以下阶段
1)pre-clean
2)clean
3)post-clean
比如执行命令mvn clean。那么只会执行pre-clean、clean,而不会执行post-clean

3.2.2)default生命周期

default是为了构建项目,default生命周期包括以下阶段
1)vaildate
2)initialize
3)generate-sources
4)process-sources
5)generate-resources
6)process-resources
7)compile
8)process-classes
9)generate-test-sources
10)process-test-sources
11)generate-test-resouces
12)process-test-resouces
13)test-compiler
14)process-test-classes
15)test
16)prepare-package
17)package
18)pre-integration-test
19)integration
20)post-integration-test
21)verify
22)install
23)deploy
如果执行mvn compiler,就会执行1~7;如果执行mvn test,就会执行1~15

上面的阶段可以精简为下面
阶段 处理 描述
验证 validate 验证项目 验证项目是否正确且所有必须信息是可用的
编译 compile 执行编译 源代码编译在此阶段完成
测试 Test 测试 使用适当的单元测试框架(例如JUnit)运行测试。
包装 package 打包 创建JAR/WAR包如在 pom.xml 中定义提及的包
检查 verify 检查 对集成测试的结果进行检查,以保证质量达标
安装 install 安装 安装打包的项目到本地仓库,以供其他项目使用
部署 deploy 部署 拷贝最终的工程包到远程仓库中,以共享给其他开发人员和工程

3.2.3)site生命周期

site母的是为了建立和发布项目站点,这个很少用,可以忽略。

总结:这个小节只要知道maven有3大生命周期,然后每个生命周期下有很多阶段,然后用maven构建程序的时候,这些生命周期和阶段会按以上顺序依次执行即可。

3.3)命令行与生命周期、插件目标与插件绑定

3.3.1)命令行与生命周期

maven的3个生命周期是相互独立的,所以可以使用mvn clean compile命令。该命令的意思就是调用mvn的clean生命周期,执行到clean阶段;调用clean生命周期后在调用default生命周期,执行到compile阶段。
生命周期的运行顺序:clean -> default -> site
生命周期的阶段的运行顺序:按照上述default生命周期中的顺序
命令行的语法:

mvn [clean生命周期的某个阶段] [clean生命周期的某个阶段] [clean生命周期的某个阶段]

下面列举了一些命令和这些命令的解读
mvn clean complier:运行maven的clean生命周期的clean阶段,在运行default生命周期的complier阶段。这个命令的作用是:先删除编译输出目录下的文件,然后在将当前项目编译,编译后的文件输出到编译目录下。简单来讲就是:重新编译。
mvn clean test:测试
mvn clean package:打包
mvn clean install:将项目打包安装到本地maven仓库

3.3.2)插件目标与插件绑定(对应pdf 120页)

插件目标:一个插件有很多个功能,这些功能是这些插件的目标。在maven中,这个概念叫『插件目标』。
插件绑定:比如default生命周期的compile阶段需要maven-compiler-plugin:compiler这个目标去实现,这个对照关系又称为『插件绑定』。

那么常见的maven命令mvn compile究竟会执行什么呢?这个就得看maven的default生命周期compile阶段绑定了哪个插件的哪个目标,下面是超级pom.xml的部分配置。

<plugin>
   <inherited>true</inherited>
   <artifactId>maven-source-plugin</artifactId>
   <!-- executions执行的意思,即插件目标 -->
   <executions>
     <execution>
       <id>attach-sources</id>
       <!-- phase为绑定的阶段,attach-sources插件应该默认绑定到compile阶段 -->
       <!-- <phase>compile</phase> -->
       <!-- goals为插件目标 -->
       <goals>
         <goal>jar-no-fork</goal>
       </goals>
     </execution>
   </executions>
 </plugin>
 
上面的意思:引入maven-source-plugin插件,然后将这个插件的jar-no-fork目标绑定到default生命周期的compile阶段上。即mvn complier命令会执行maven-source-plugin插件的jar-no-fork功能。

从超级pom.xml可以看出,maven为了开箱即可用,所以默认将某些阶段绑定了某些插件的目标。
一个生命周期的某个阶段具体执行什么功能,要看这个阶段绑定了什么插件的目标,一个阶段可以绑定一个或多个插件目标。

四、maven的多模块配置

4.1 父子模块配置

父模块:
<modules>
     <module>子模块的artifactId</module>
     <module>子模块的artifactId</module>
     <module>子模块的artifactId</module>
 </modules>
子模块:
1)其打包方式packaging的值必须为pom
2)<modules>中引入其子模块
3)聚合模块一般只有pom.xml文件,没有代码实现。因为聚合模块的作用就是聚合其他子模块。
4)子模块加入<parent>标签

<parent>
	<groupId></groupId>
	<artifactId></artifactId>
	<version></version>
	 <!-- 元素relativePath表示父模块pom的相对路径。当项目构建时,maven会首先根据relativePath检查父pom,如果找不到,在从本地仓库查找。 -->
	<relativePath><relativePath>
</parent>

4.2 可以被继承的pom元素(对应pdf 149页)

常见的有:groupId、version、properties(变量)、dependencies(依赖)、dependenciesManagement(依赖管理,这个不会真正引入依赖,只是声明了依赖的版本)、build(插件管理配置)

五、零散的知识点

5.1 compiler插件

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.1</version>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
    </configuration>
</plugin>

maven的compiler插件默认只支持编译jdk1.3,所以要编译更高版本的jdk,要进行手工指定jdk版本。

5.2 idea的iml文件

.iml文件是什么?如下图所示,.iml文件就是idea存储项目结构的文件格式,类比eclipse的存储结构文件格式就是.classpath。存储结构文件下包含了当前项目依赖的jar。
在这里插入图片描述
跟.iml文件相关的一个问题:idea没有编译错误,但出现红色波浪线怎么去掉?问题如下图所示
在这里插入图片描述

问题原因:很可能是刚修改过pom.xml,然后maven自定引入了依赖,idea编译成功。但是.iml文件下的依赖信息跟项目的pom.xml的依赖信息不一致,导致报错。
解决办法:重新生成iml文件,在缺少.iml文件项目下运行mvn idea:module,完成后将自动生成.iml文件。
参考博客:https://blog.csdn.net/u012627861/article/details/83028437

5.3 maven项目原型

在这里插入图片描述

5.4 快照版本(对应pdf 103页)

TODO
快照版本就是“小步快跑”,每天都会检查一次是否要更新?

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫985

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值