一、Maven基础
1.1 Maven功能
Maven能帮助我们干什么?它主要有两个功能:
- 依赖管理(jar包管理):在开发过程中需要大量jar包,我们只需要在Maven的主配置文件中添加相应jar包的标识,他就会自动下载相应jar包,不用我们自己去到处搜索jar包了
- 构建项目:我们可以通过Maven构建项目,它定义了一套生命周期,规范了构建流程,可以一键构建,提高了大型项目的开发效率
1.2 POM文件
每个maven项目都会有一个pom.xml文件, 在这个文件里面是通过坐标来唯一标识Maven依赖,坐标元素包括groupId、artifactId、version、packaging、classifier(前三个为必选),这些元素的含义:
- groupId:定义当前Maven项目所属的实际项目
- artifactId:定义实际项目中的一个Maven模块
- version:定义Maven项目当前所处的版本
- packaging:定义Maven项目打包方式, 通常打包方式与所生成构件扩展名对应,包括:jar(默认)、war、pom、maven-plugin等
- classifier:用来帮助定义构建输出的一些附属构件(如javadoc、sources),不能直接定义项目的classifier,须有插件的帮助
这里提一下version版本的两种类型:
- SNAPSHOT:泛指以-SNAPSHOT为结尾的版本号,用于保存开发过程中的不稳定版本号,在mvn deploy时会主动发布到快照版本号库中;而使用快照版本号的模块,在不更改版本号的情况下直接编译打包时,maven会自己主动从镜像server上下载最新的快照版本号
- RELEASE:所有非-SNAPSHOT结尾的版本号则都被认定为RELEASE版本,即正式版,在mvn deploy时会自己主动发布到正式版本号库中;而使用正式版本号的模块在不更改版本号的情况下,编译打包时假设本地已经存在该版本号的模块则不会主动去镜像server下载
1.3 Maven依赖
Maven最著名的就是Maven的依赖管理,它使得我们不必再到开源项目的官网一个个下载开源组件,然后再放入classpath。一个依赖声明可以包含如下元素:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.2.7.RELEASE</version>
<type>jar</type>
<scope>compile</scope>
<optional>false</optional>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
依赖范围Scope
Scope 是用来限制Dependency的作用范围的, 他会影响maven项目在各个生命周期时导入的package的状态。常用scope见下图:
依赖范围Scope依赖传递性
比如下图有Maven项目junit,项目commons-logging依赖junit,项目spring-core依赖commons-logging,项目my-app依赖spring-core;那么我们可以说my-app依赖junit;我们执行项目my-app时,会自动把spring-core、commons-logging、junit都下载导入到my-app项目的jar包文件夹中,这就是依赖的传递性。
依赖传递示例假如现在不想执行my-app时把junit下载进来,那么我们可以用<exclusions>
标签。
依赖调节的原则:
- 如果依赖路径的长度不同,则短路优先
- 依赖路径长度相同情况下,则先声明优先
依赖分析
mvn dependency:list
查看当前项目的已解析依赖
mvn dependency:tree
查看当前项目的依赖树
mvn dependency:analyze
自动化分析当前项目的依赖
IDEA可以安装一个很方便的插件Maven Helper来帮助我们进行依赖管理、排包等操作。
1.4 Maven仓库
Maven仓库只有两大类:
- 本地仓库:Maven在本地存储构件的地方;
- 远程仓库:在远程仓库中又分成了2种:
a. 中央仓库:中央仓库是默认的远程仓库,maven在安装的时候,自带的就是中央仓库的配置;
b. 私服:私服是一种特殊的远程仓库,它是架设在局域网内的仓库服务,私服代理广域网上的远程仓库,供局域网内的Maven用户使用;私服的特性有:
- 节省外网带宽
- 加速Maven构建
- 部署第三方构件
- 提高稳定性,增强控制
- 降低中央仓库负荷
1.5 常用Maven指令
mvn clean compile
清理+编译
mvn clean test
清理+编译+执行测试
mvn clean package
清理+编译+打包
mvn clean install
清理+编译+打包+放置本地仓库
mvn archetype:generate
创建项目骨架
二、Maven生命周期和插件
Maven生命周期
Maven有三个内置的生命周期:default、clean和site。在default的生命周期处理你的项目部署,在clean的生命周期处理项目的清理,在site的生命周期处理你的项目站点文档的创建。
Maven生命周期Maven插件
Maven本身是一个框架,实际的任务都由插件完成。插件与生命周期阶段绑定,用户通过指定生命周期阶段就能够隐式的通过插件执行任务,如:$mvn compiler:compile
,冒号前是插件前缀,后面是该插件目标(即: maven-compiler-plugin的compile目标),而该目标绑定了default生命周期的compile阶段。
Maven 默认插件目标绑定源码:
<component>
<role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>
<role-hint>jar</role-hint>
<implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation>
<configuration>
<lifecycles>
<lifecycle>
<id>default</id>
<!-- START SNIPPET: jar-lifecycle -->
<phases>
<process-resources>org.apache.maven.plugins:maven-resources-plugin:2.6:resources</process-resources>
<compile>org.apache.maven.plugins:maven-compiler-plugin:3.1:compile</compile>
<process-test-resources>org.apache.maven.plugins:maven-resources-plugin:2.6:testResources</process-test-resources>
<test-compile>org.apache.maven.plugins:maven-compiler-plugin:3.1:testCompile</test-compile>
<test>org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test</test>
</phases>
<!-- END SNIPPET: jar-lifecycle -->
</lifecycle>
</lifecycles>
</configuration>
</component>
自定义绑定
除了内置绑定以外,用户还能够自定义将某个插件目标绑定到生命周期的某个阶段上。如创建项目的源码包,maven-source-plugin插件的jar-no-fork目标能够将项目的主代码打包成jar文件,可以将其绑定到verify阶段上:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
其中executions下每个execution子元素可以用来配置执行一个任务。
自定义插件开发
参考:https://blog.csdn.net/zjf280441589/article/details/53044308/
三、聚合与继承
Maven的聚合特性(aggregation)能够使项目的多个模块聚合在一起构建,而继承特性(inheritance)能够帮助抽取各模块相同的依赖、插件等配置,在简化模块配置的同时,保持各模块一致。
例如,父POM:
<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.my.app</groupId>
<artifactId>appName</artifactId>
<packaging>pom</packaging>
<version>1.0.0.SNAPSHOT</version>
<modules>
<module>appName-client</module>
<module>appName-core</module>
<module>appName-web</module>
</modules>
<properties>
<finalName>appName</finalName>
<warName>${finalName}.war</warName>
<spring.version>4.0.6.RELEASE</spring.version>
<junit.version>4.12</junit.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<warExplodedDirectory>exploded/${warName}</warExplodedDirectory>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>attach-sources</id>
<phase>verify</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
- packaging: pom,否则无法聚合构建
- modules: 实现聚合的核心,module值为被聚合模块相对于聚合POM的相对路径,离开聚合POM也能够独立构建(注: 模块所处目录最好与其artifactId一致)
- dependencyManagement: 能让子POM继承父POM的配置的同时,又能够保证子模块的灵活性:在父POMdependencyManagement元素配置的依赖声明不会实际引入子模块中,但能够约束子模块dependencies下的依赖的使用(子模块只需配置groupId与artifactId)
- pluginManagement: 与dependencyManagement类似,配置的插件不会造成实际插件的调用行为,只有当子POM中配置了相关plugin元素,才会影响实际的插件行为
子POM:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<groupId>com.my.app</groupId>
<artifactId>appName</artifactId>
<version>1.0.0.SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>appName-client</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
可以看到,子POM中并未定义模块groupId与version,这是因为子POM默认会从父POM继承了如下元素:
- groupId、version
- dependencies
- developers and contributors
- plugin lists (including reports)
- plugin executions with matching ids
- plugin configuration
- resources
参考:
- 彦薇:Maven原理及使用深度解析
- Maven 核心原理
- maven snapshot快照仓库和release公布仓库区别
网友评论