简洁 明了 说明 Maven 使用配置、操作详解,一篇就够了 🔥🔥

207 阅读13分钟

Java构建工具对比 🍀

IDE 工具(Eclipse、Net-Beans等编辑器)

  • 依赖大量手工操作,编译、测试、打包都是相互独立,很难一键完成;
  • 也有可能IDE配置不同,在不同机器上运行结果不一样;

Make (Makefile脚本文件驱动构建)

  • 有自定义的语法格式(规则、命令)
  • 可以利用系统的本地命令
  • 很难实现跨平台构建
  • 容易出现语法问题、空格或tab等

Ant (最早用来构建tomcat)

  • xml定义构建脚本
  • 大量内置的任务是Java实现、保证了跨平台性
  • 过程性编译每个项目的执行任务,易造成大量重复(而Maven是声明性、构建过程由插件实现)
  • 没有依赖管理,需要借助lvy管理依赖(而Maven内置依赖管理)

Gradle(构建工具(Android app 主要用gradle构建))

  • 和Maven 一样 约定大于配置
  • 支持多工程构建
  • 构建速度快
  • 以Groovy语言为基础

Maven 安装目录结构 🍎

bin // mvn 运行脚本、执行Java命令
boot //plexus-classworlds-2.6.0类加载框架,使用该jar加载自己的类库;
conf // 配置文件目录setting
lib // maven 运行时需要的类库
NOTICE.txt // 包含的第三方软件
README.txt // 简要介绍和安装说明

使用 archetype 生成模板 🍓

命令使用说明

mvn archetype:generate // 实际是运行 maven-archetype-plugin插件

约定式项目目录结构 🍉

项目
	/src
		/main 主程序目录 (完成项目功能代码和配置文件)
			/java (包相关类定义)
			/resources (配置资源文件)
		/test
			/java
			/resources
	/pom.xml (maven配置文件)

Maven的POM.xml文件 🌞

Maven是基于项目对象模型(Project Object Model,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">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.test</groupId>
    <artifactId>MavenTest</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>MavenTest</name>
</project>

  1. 第一行是XML头,指定了该xml文档的版本和编码方式;
  2. project 是根元素,声明了一些POM相关的命名空间及xsd元素;
  3. modelVersion指定了当前POM的版本,在 maven2和maven3 都是 4.0.0;
  4. groupId定义了项目属于哪个组织,通常是组织域名的倒序,比如说我的域名是 test.com,所以groupId就是 com.test;
  5. artifactId定义了项目在组织中的唯一ID;
  6. version指定了项目当前的版本,SNAPSHOT意为快照,说明该项目还处于开发中;
  7. name 声明项目名称;

注:groupId、artifactId和version这三个元素定义了一个项目的基本坐标,在Maven里任何的jar和pom都是以基于这些坐标进行区分;

Maven命令说明 🌴

  1. 命令:mvn clean 清理clean:将以前编译得到的旧文件class字节码文件删除(会默认把target文件夹中的数据清理);

  2. 命令: mvn compile 或 mvn clean compile 编译compile:将java源程序编译成class字节码文件(写入target目录中)

  3. 命令:mvn test 或 mvn clean test 测试test:自动测试,自动调用junit程序

  4. 命令: mvn report 报告report:测试程序执行的结果

  5. 命令: mvn package 或 mvn clean package 打包package:动态Web工程打War包,java工程打jar包

  6. 命令:mvn verify verify检查包是否有效

  7. 命令: mvn install 或 mvn clean install 安装install:将包文件安装到本地 Maven 存储库,把被依赖的maven工程的jar包导入到本地仓库中;

  • 例如 项目A 依赖 项目B ,那么项目B就必须先构建编译安装到本地仓库;
  • 通过install插件将项目B构建文件安装复制到本地仓库;
  1. 命令: mvn deplop 部署deploy:将动态Web工程生成的war包复制到Servlet容器下,使其可以运行,将包文件部署到远程服务器或存储库;
  • 查看当前项目已解析依赖
mvn dependency:list
  • 查看当前项目依赖树
mvn dependency:tree
  • 分析当前项目
// 只会分析编译主代码和测试代码需要用到的依赖,运行时需要的依赖发现不了
mvn dependency:analyze 

Maven 坐标描述 🌲

<groudId>com.test</groudId>
<artifactId>test</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
  • groudId : 组织唯一标识,一般使用域名反向写法;
  • artifactId: 项目的唯一标识;
  • version: 版本号
  • packaging : maven打包方式(pom/jar/war),默认使用jar
  1. pom 父类型一般为pom类型,pom是指项目里没有java代码,也不执行任何代码,只是为了聚合工程或传递依赖用;在执行maven命令的时候将首先对父项目执行,然后当 父项目 的packing 类型为 pom 时,将对其所有的子模块执行同样的命令,否则无法执行同样命令,以及依赖的传递将无法由maven 编译或打包;
  2. jar 内部调用或作为服务包;
  3. war 打包项目用于Web容器上部署(tomcat/Jetty等)
  • classifier: 用来帮助定义构建生成一些附属构件、需要附加插件帮组生成;
  • type:指定了依赖的类型,默认为 jar;
  • scope:指定了依赖的范围;
  • optional:标记了依赖是否是可选的(针对 传递性依赖设置是否可选);
  • exclusions:用来排除传递性依赖; 比如:A 依赖=>B 依赖=> C ,不要引入传递性依赖C,可以使用 exclusions中含exclusion加入排除的groupdId和artifactId不需要version;
<dependency>
	<groupId>com.test</groupId>
	<artifactId>project-b</artifactId>
	<version>1.0.0</version>
	<exclusions>
		<exclusion>
			<groupId>com.test</groupId>
			<artifactId>project-c</artifactId>
		</exclusion>
	</exclusions>
</dependency>

提示: <dependency></dependency>内部通过依赖的基本坐标groupId、artifactId、version确定唯一的依赖,可以称这3个为坐标;

依赖范围scope详解 🌳

  • 1、compile: 默认值,适用于所有阶段(开发、测试、部署、运行),本jar会一直存在所有阶段;

  • 2、provided:只在开发、测试阶段使用,目的是不让Servlet容器和你本地仓库的jar包冲突;

  • 3、runtime: 只在运行时使用,如JDBC驱动,适用运行和测试阶段。

  • 4、test: 只在测试时使用,用于编译和运行测试代码。不会随项目发布。

  • 5、system: 类似provided,需要显式提供包含依赖的jar,Maven不会在Repository中查找它。

  • compile,默认的依赖范围,表示依赖需要参与当前项目的编译,测试、运行周期;

  • test,表示依赖仅参与测试,包括测试代码的编译和运行。比如: junit;

  • runntime,表示依赖无需参与到项目的编译,不过后期的测试和运行需要,比如: jdbc驱动;

  • provided,表示不打包进去,别的容器会提供或编译与测试环境要求需要 比如: servlet-api;

  • system,从参与程度上来说,和 provided 类似编译与测试 ,但不通过 Maven 仓库解析,可能会造成构建的不可移植,要谨慎使用;

  • import,导入依赖范围;

依赖传递性 🔥

Maven 会解析直接依赖的POM,将那些必要的间接依赖,以传递性依赖形式引入到当前项目中;

test-demo(一级) 依赖=> spring-boot-starter-web(二级) 依赖=> spring-webmvc (三级)
  • test-demo 有一个compile依赖范围的spring-boot-starter-web依赖;
  • spring-boot-starter-web有一个compile依赖范围的spring-webmvc;
  • spring-webmvc 是test-demo 的传递性依赖;
  • 传递性依赖的依赖范围由 一级和二级依赖的依赖范围决定(都是compile,则传递性依赖范围为compile);
  • 三级如果是可选依赖,就不会作为一级的传递性依赖并不造成影响;只会对二级造成影响;

依赖调解(针对多个传递性依赖Maven解析使用哪个,不造成依赖重复) 🌹

  1. 路径最近优先
  2. POM中依赖声明顺序最靠前的依赖优先;

统一依赖版本声明(properties元素) 🍏

为了统一管理版本号,可以使用properties标签,里面可以自定义版本的标签名;在使用的地方使用${自定义标签名};

  • 可以定义变量在dependency中引用,代码如下所示:
<properties>
    <java.version>1.8</java.version>
    <spring-framework.version>4.3.18.RELEASE</spring-framework.version>
</properties>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring-framework.version}</version>
</dependency>

仓库描述 💐

本地仓库

  1. 默认路径: .m2/repository/
  2. 本地项目只有在本地仓库内,才能给其他maven项目使用;

私服(远程仓库)

  1. 拉取的依赖坐标当私服没有时,就会去中央仓库去拉取;
  2. 私服认证
  • settings配置
<servers>
	<server>
		<id>jboss</id>
		<username>user</username>
		<password>pwd</password>
	</server>
</servers>
  • 部署构件到私服 (1) POM配置distributionManagement
<project>
	<distributionManagement>
		<repository><!--发布版本构件仓库-->
			<id>maven-releases</id> <!--与Settings中server配置ID匹配 -->
			<name>maven Release Repository</name>
			<url>http://192.168.0.12/repository/maven-releases/</url>
		</repository>
		<snapshotRepository><!--开发版本构件仓库-->
            <id>maven-snapshots</id>
			<name>maventSnapshots Repository</name>
            <url>http://192.168.0.12/repository/maven-snapshot/</url>
        </snapshotRepository>
	</distributionManagement>
</project>

(2) 配置完成,运行 mvn clean deploy项目构建的构件部署到私服

中央仓库 (central)

  1. 可在maven-core-3.8.4.jar\org\apache\maven\repository\RepositorySystem.class 中查看仓库地址;
  2. 配置POM中使用远程仓库
<project>
	<repositories>
		<id>jboss</id><!--仓库ID必须唯一-->
		<name>jboss Name</name>
		<url>http://repository.jboss.com/maven2/</url>
		<release>
			<enabled>true</enabled> <!--支持下载发布版-->
			<checksumPolicy>fail</checksumPolicy><!--当Maven验证构件校验文件失败时处理:-ignore(忽略),fail(失败),或者warn(警告)-->
			<updatePolicy>daily</updatePolicy> <!--从远程仓库检查更新频率,daily表示每天检查一次、never从不检查更新、always每次构建都检查更新、interval 间隔检查更新-->
			<!--如果坐标一致,maven不会更新-->
			
		</release>
		<snapshots>
			<enabled>false</enabled><!--不支持下载开发版-->
		</snapshots>
		<layout>default</layout>
	</repositories>
</project>
  • 为什么区分发布版和开发版? (1)因为发布版固定版本号不是用于开发频繁更新,来频繁修改POM; (2)使用开发版如1.0.SNAPSHOT ,发布的时候Maven 会自动打上时间戳; (3)开发版每次检查更新时就会安装最新时间的开发包,可以通过配置updatePolicy和使用命令 mvn clean isntall -U,-U 强行检查更新;
  1. 配置远程仓库镜像地址(阿里云) 修改在conf/settings.xml下添加镜像仓库地址
<mirror>
		<id>nexus-aliyun</id>
		<mirrorOf>central</mirrorOf>
		<name>Nexus aliyun</name>
		<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>

mirrorOf 使用介绍说明(Maven自带的中央仓库使用的仓库id为central)

  • <mirrorOf>*<mirrorOf> 表示匹配所有远程仓库;

  • <mirrorOf>external:*<mirrorOf> 表示匹配所有远程仓库,使用localhost、file://协议的除外,既匹配不在本机上的远程仓库;

  • <mirrorOf>central,central1<mirrorOf> 表示匹配仓库central和central1,使用逗号分隔多个远程仓库;

  • <mirrorOf>*,!central<mirrorOf> 表示匹配所有远程仓库,central除外,使用感叹号将指定仓库从匹配中排除;

注: 镜像仓库会完全遮蔽被镜像的仓库,当镜像仓库不稳定,则无法下载构件;

生命周期和插件 🍄

初始化、编译、测试、打包、集成测试、部署;

三种独立生命周期(互不影响)

  1. clean 清理项目 (如下阶段)
  • pre-clean 清理前工作
  • clean 清理上一次构建的文件
  • post-clean 清理后工作
  1. default 构建的核心部分,编译,测试,打包,部署等等(如下阶段)
  • validate
  • initialize
  • generate-sources
  • process-sources 处理项目主资源文件(src/main/resource)
  • generate-resource
  • process-resource
  • compile 编译项目的主源码(src/main/java)
  • process-classes
  • generate-test-sources
  • process-test-sources 处理项目测试源码文件
  • generate-test-resource
  • process-test-resource
  • test-compile 编译项目测试代码(src/test/java)
  • process-test-classes
  • test 使用单元测试框架
  • prepare-package
  • package 接受编译好的文件,打包jar等
  • pre-integration-test
  • integration-test
  • post-integration-test
  • verify
  • install 包安装到本地仓库
  • deploy 包复制到远程仓库 文档详解
  1. Site Lifecycle 生成项目报告,站点,发布站点。
  • pre-site 执行一些需要在生成站点文档之前完成的工作
  • site 生产项目站点文档
  • post-site 执行一些需要在生成站点文档之后完成的工作,并且为部署做准备
  • site-deploy 将生成的站点文档部署到特定的服务器上

注:例如 命令mvn clean install实际执行是pre-clean和clean阶段,而default生命周期是validate至install阶段,两个生命周期阶段结合;

maven插件 官网插件

  • 编译插件 maven-compiler-plugin
Maven提供了编译插件,可在编译插件中设置Java的编译级别,代码如下:
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.0</version>
            <configuration>
                <source>${java.version}</source>
                <target>${java.version}</target>
            </configuration>
        </plugin>
    </plugins>
</build>
  • 测试插件 maven-surefire-plugin

  • 描述插件 maven-help-plugin

// 插件信息
mvn help:describe -Dplugin=org.springframework.boot:spring-boot-maven-plugin

// 插件目标信息
mvn help:describe -Dplugin=org.springframework.boot:spring-boot-maven-plugin -Dgoal=run
  • maven-shade-plugin生产可执行jar包

  • maven-surefire-plugin 介绍

  1. Maven本身并不是一个单元测试框架,在构建执行到特定生命周期阶段的时候,通过插件来执行JUnit或者TestNG的测试用例;
  2. 测试运行器(Test Runner),它能兼容JUnit 3、JUnit 4以及TestNG,自动执行测试源码路径(默认为src/test/java/;
Test*.java:任何子目录下所有命名以Test开关的Java类;
Test.java:任何子目录下所有命名以Test结尾的Java类;
  1. 要想跳过测试,在命令行加入参数skipTests如:
mvn package -DskipTests  
  1. 需要跳过测试运行,还要跳过测试代码的编译
mvn package -Dmaven.test.skip=true  
  1. 动态指定要运行的测试用例
// 可以使用逗号定多个测试类:
mvn test -Dtest=Random*Test,AccountCaptchaServiceTest
  1. 可以添加-DfailIfNoTests=false参数告诉maven-surefire-plugin即使没有任何测试也不要报错
mvn test -Dtest -DfailIfNoTests=false  
  • maven-compiler-plugin
  1. maven-compiler-plugin 插件来对 Java 代码编译的,如果不指定 JDK 版本,maven-compiler-plugin 会自动使用一个默认的版本;
  2. maven-compiler-plugin 默认的 JDK 版本为 1.7,此时 JDK 1.7 是不可能将带有 JDK 1.8 特性的代码编译通过的

指定jdk 版本

<?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">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>cn.test</groupId>
    <artifactId>testDemo</artifactId>
    <version>1.0.0-SNAPSHOT</version>
 
    <properties>
        <!-- maven-compiler-plugin 将会使用指定的 JDK 版本对源代码进行编译(针对编译运行环境) -->
        <maven.compiler.source>1.8</maven.compiler.source>
        <!-- maven-compiler-plugin 将会使用指定的 JDK 版本将 java 文件编译为 class 文件(针对编译运行环境)-->
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
 
</project>

聚合 🍉

(父)聚合模板,打包方式<packaging>pom</packaging>必须为pom,否则无法构建

<project>
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.test</groupId>
	<artifactId>test-demo</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<packaging>pom</packaging>
	<name>Test Demo</name>
	<modules> <!-- 子模块-->
		<module>test-name1</module>
		<module>test-name2</module>
	</modules>
</project>

继承 🍎

POM元素

  • groupId
  • version
  • description
  • organization
  • inceptionYear
  • url
  • developers
  • contributors
  • distributionManagement 项目部署配置
  • properties 自定义属性
  • dependencys
  • dependencyManagement 项目依赖管理配置
  • repositories 仓库配置
  • build
  • reporting

关于dependencyManagement说明

  • 因dependencies默认会被子项目继承;
  • dependencyManagement 只是声明依赖,并不自动实现引入,子项目需要显示声明需要的依赖;
  • 项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom;
  • 如果子项目中指定了版本号,那么会使用子项目中指定的jar版本
  • 使用import依赖范围(type:pom)可以将目标pom中dependencyManagement导入合并到当前依赖管理配置dependencyManagement;

关于pluginManagement配置管理插件

子模块声明使用的插件,会继承父模块pluginManagement配置,统一管理配置和版本;

<!-- 父POM-->
<build>
	<pluginManagement>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
				  <!-- 源代码编译版本 -->
				  <source>1.8</source>
				  <!-- 目标平台编译版本 -->
				  <target>1.8</target>
				</configuration>
			</plugin>
		</plugins>
	</pluginManagement>
</build>

<!-- 子POM -->
<build>
	<plugins>
		<plugin>
			<groupId>org.apache.maven.plugins</groupId>
			<artifactId>maven-compiler-plugin</artifactId>
		</plugin>
	</plugins>
</build>

build配置说明 🍓

配置好build后,执行mvn package之后,会在maven工程指定的target目录里生成配置要求的war包;

<build>
  <!-- 项目的名字 -->
  <finalName>testDemo</finalName>
  <!-- 描述项目中资源的位置 -->
  <resources>
    <!-- 自定义资源1 -->
    <resource>
      <!-- 资源目录 -->
      <directory>src/main/java</directory>
      <!-- 包括哪些文件参与打包 -->
      <includes>
        <include>**/*.xml</include>
      </includes>
      <!-- 排除哪些文件不参与打包 -->
      <excludes>
        <exclude>**/*.txt</exclude>
          <exclude>**/*.doc</exclude>
      </excludes>
    </resource>
  </resources>
  <!-- 设置构建时候的插件 -->
  <plugins>
    <!-- 资源插件(资源的插件) -->
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-resources-plugin</artifactId>
      <version>2.1</version>
      <executions>
        <execution>
          <phase>compile</phase>
        </execution>
      </executions>
      <configuration>
        <encoding>UTF-8</encoding>
      </configuration>
    </plugin>
    <!-- war插件(将项目打成war包) -->
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-war-plugin</artifactId>
      <version>2.1</version>
      <configuration>
        <!-- war包名字 -->
        <warName>testDemo</warName>
      </configuration>
    </plugin>
  </plugins>
</build>

parent 标签的使用

  • parent标签,复用依赖,减少冗余配置
  • 在多模块(module)的项目中,如此时声明一个父pom文件,将公用的依赖提取到父pom文件中(即使用标签),将大大减少其他子pom文件中的依赖的配置;
<parent>
	<groupId>com.test</groupId>
	<artifactId>test-parent</artifactId>
	<version>1.0.0</version>
</parent>

Maven 属性说明 🍏

内置属性

  1. ${basedir} 项目根目录包含(pom文件目录)
  2. ${version} 项目版本

POM 属性

  1. ${project.build.sourceDirectory} 项目主源码目录(src/main/java)
  2. ${project.build.testSourceDirectory} 项目测试源码目录(src/test/java/)
  3. ${project.version} 项目版本
  4. ${project.outputDirectory} 编译输出目录(target/classes)

自定义属性 properties

<project>
	<properties>
		<my.name>test</my.name>
	</properties>
</project>

Settings属性 用settings开头设置属性

  1. ${settings.localRepository} 用户本地仓库

java 系统属性 使用 mvn help:system 查看所有Java系统属性

环境变量属性 用env开头引用,${env.JAVA_HOME} 指定 JAVA_HOME环境变量值; 使用 mvn help:system 查看所有Java系统属性;

Profiles 详解 🔥

  • 可以编写于settings和pom上;
  • 配置 profile 还可以根据不同的环境(开发环境、测试环境,生产环境)读取不同的配置文件
  • 运行指定profile中ID对应的内容,使用mvn 命令后加-P 指定id(多个id用逗号隔开) 如: mvn clean install -pdev
  • 资源插件需要配置filtering为true;
<profiles>
	<profile>
		<div>dev</div>
		<properties>
			<db.driver>com.mysql.jdbc.Driver</db.driver>
			<db.url>jdbc:mysql://localhost:3306/testmysql</db.url>
			<db.username>dev</db.username>
			<db.password>dev-pwd</db.password>
		</properties>
	</profile>
	<profile>
		<div>test</div>
		<properties>
			<db.driver>com.mysql.jdbc.Driver</db.driver>
			<db.url>jdbc:mysql://localhost:3306/testmysql</db.url>
			<db.username>test</db.username>
			<db.password>test-pwd</db.password>
		</properties>
	</profile>
</profiles>
  • setting 文件中配置profile默认激活状态
<settting>
	<activeProfiles>
		<activeProfile>dev</activeProfile>
	</activeProfiles>
</settting>
  • 根据系统属性和设置属性值激活
  1. 使用mvn clean install -Dtest=start激活
<profies>
	<profile>
		<activation>
			<property>
				<name>test</name>
				<value>start</value>
			</property>
		</activation>
	</profile>
</profies>
  1. 使用根据系统环境激活
<profies>
	<profile>
		<activation>
			<os>
				<name>Windows 11</name>
				<family>Windows</family> <!--UNIX/Mac-->
				<arch>64</arch>
				<version></version>
			</os>
		</activation>
	</profile>
</profies>
  • 文件存在与否 是否激活
<profies>
	<profile>
		<activation>
			<file>
				<exists>test.properties</exists>
			</file>
		</activation>
	</profile>
</profies>
  • 默认激活
<profies>
	<profile>
		<activation>
			<activeByDefault>true</activeByDefault>
		</activation>
	</profile>
</profies>

结束

欢迎大家评论,点赞,讨论;