maven

Maven

Maven是一个Java项目管理和构建的工具,它可以定义项目结构、项目依赖管理、使用统一的方式进行自动化构建,是Java项目不可或缺的工具。

maven的作用

  1. 提供了一套标准化的项目结构。
    • 以前不用的开发工具项目结构不一样,maven出现后规范了项目结构,开发人员上手项目变得更加简单。

image-20250921132319078

  1. 提供了一套标准化的构建流程(编译,测试,打包,发布...)。

    image-20250921132547791

  2. 提供了一套依赖管理机制(pom.xml文件,管理项目中的所有jar包)

    • 使用传统的项目开发,每次都要复制jar包到lib目录,如果jar包很多,项目大小会很大,且多个项目的话,使用同一个包需要复制多份。
    • maven管理后,所有jar包都存在maven仓库,只需要用pom.xml管理jar包。
    • 解决jar包和jar包的版本冲突问题(利用"依赖传递"特性解决)。

Maven下载和配置

Maven下载和配置 简要步骤及注意事项:

  1. maven是一个java工具,它必须有java环境,配置java环境保留。

  2. 下载版本可参考idea默认绑定的maven版本。

  3. 修改配置:主要修改镜像和仓库位置。

    • Maven 中央仓库:默认运程仓库是 Maven 中央仓库(repo.maven.apache.org),它是官方的、最全的公共仓库,但服务器在国外,国内访问速度可能较慢。

    • 镜像:Maven 中央仓库(Central Repository)在国内的一个“完整拷贝”(或称“镜像”),定期同步,以保证内容一致。

    • 镜像的作用:拦截原本对Maven 中央仓库的请求,并将其重定向到镜像地址。对用户来说,这个过程是透明的,感觉不到区别,由于镜像在国内,下载速度会快很多。

  4. IDEA配置Maven。

创建Maven项目

maven命令创建

  • mvn archetype:generate
    

idea创建

image-20250921140112563

Maven项目结构

image-20250921140601557

另外还有一个target目录专门存放构建操作输出的结果。

约定目录结构的意义

Maven为了让构建过程能够尽可能自动化完成,所以必须约定目录结构的作用。例如: Maven执行编译操作,必须先去Java源程序目录读取Java源代码,然后执行编译,最后把编译结果存放在target目录。

约定大于配置

Maven对于目录结构这个问题,没有采用配置的方式,而是基于约定。这样会让我们在开发过程中非常方便。如果每次创建Maven工程后,还需要针对各个目录的位置进行详细的配置,那肯定非常麻烦。
目前开发领域的技术发展趋势就是:约定大于配置,配置大于编码。

Maven命令

命令行:注意要在pom.xml所在的目录下执行命令

image-20250921144704473

idea自带的界面执行:

image-20250921132547791

生命周期:例如执行compile时,其实会依次执行 clean、valiate、compile

Maven 生命周期命令作用:

clean - 清理项目,删除target目录
validate - 验证项目正确性和所需信息是否可用
compile - 编译项目源代码
test - 运行单元测试
package - 打包编译后的代码(jar、war等格式)
verify - 检查集成测试结果以确保质量标准
install - 将包安装到本地仓库,供其他项目使用
site - 生成项目站点文档
deploy - 将最终包复制到远程仓库

这些命令按顺序执行,构成Maven的标准构建生命周期。

依赖范围

依赖范围:compile(默认)、provided、runtime、system、test
有哪些范围:测试(main.java)、编译(test.java)、运行/打包(包内)

  • test: 编译× 测试✔ 打包×

    • 例如:junit 测试的时候才能用(main.java文件夹下的能用),编译、打包不能用。
  • provided:编译✔ 测试✔ 打包×

    • 例如:javax.servlet(一般用tomcat启动,tomcat里已经有了javax.servlet,项目打包不用带这个包了,带了反而可能导致servlet版本混乱、项目增大)。
  • compile:编译✔ 测试✔ 打包✔

    • 默认的。
  • runtime:编译× 测试✔ 打包✔

    • 反射 数据库驱动class.forName()。
  • system:效果等于provided,但是其不会依赖仓库中jar包,而是引用本地物理路径的jar包 。

    • 基本不会使用,了解即可。

    • 使用时配合一个使用。

1.首先﹑依赖范围建议﹑哪怕所有的范围都设置compile,也不会影响功能正常使用。
2.使用依赖提供的scope,直接中央仓库无脑复制就行了,并且idea也会自动补全。
3.90%以上都会使用compile。
4.但是如果做一个好的程序员,应该尽量让程序优雅,保证依赖的最小范围。

依赖传递

image-20250921152154156

<dependencies>
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>5.8.40</version>
    	<! --作用访问是test、provided 就不会传递
			<scope>provided</scope> 
     	-->
    	<!--是否传递 默认是于false 会传递-->
    	<optional>false</optional>
    </dependency>
</dependencies>

依赖的排除和覆盖

 <!-- com.test.project 里面有一个依赖我们不需要的情况,或者版本不需要的情况,用下面的方式解决-->

<dependency>
    <groupId>com.test</groupId>
    <artifactId>bproject</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--  手动排除
    <exclusions>
        <exclusion>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
		</exclusion>
    </exclusions>
	-->
</dependency>

<!--在自己的工程添加一个系统的依赖,不同版本会以我们工程依赖优先进行替换-->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.2.0</version>
</dependency>

依赖图

商业版的idea才有
image-20250921152926852

image-20250921153004550

Maven聚合和继承

聚合:主要关注的是项目的物理结构和构建过程。

  • 将多个独立的 Maven 项目(模块)组合在一起,以便通过一个统一的入口(父 POM)来一次性构建所有这些项目。
  • 子项目直接可以互相直接引用,而不需要install

继承:主要关注的是POM 配置的逻辑关系和复用性。

  • 允许子模块 POM 从一个共同的父 POM 中继承配置信息,从而实现配置的复用和统一管理。

聚合

聚合项目:

  1. 把src删掉,因为聚合项目通常只是管理子工程。

  2. 把packaging修改下默认jar,改成pom,当前不是一个具体的包。

  3. 把子工程管理起来。

<packaging>pom</packaging>
<modules>
    <module>maven_01</module>
    <module>maven_02</module>
</modules>

继承

<parent>
    <groupId>org.example</groupId>
    <artifactId>MavenTest</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent>

Maven命令复制依赖

dependency:copy-dependencies:用于将项目依赖的jar包从仓库拷贝到指定目录。

  • 如果子工程都没引入其他的依赖:

    • 例如父工程引入 5.8.40,子过程都没引入其他版本。

    • 那么复制过来的就都是父工程配置的依赖:5.8.40

  • 如果一个子工程引入其他的依赖,其他用父工程的:

    • 例如父工程引入 5.8.40,子工程1未引入,直接用父工程的,子工程25.8.39
    • 那么复制过来的就都是父工程配置的和子工程2配置的依赖:5.8.405.8.39
  • 如果所有子工程引入新的版本,但是都是同一个:

  • 例如父工程 5.8.40,所有子工程包括子过程1和子工程2都引入5.8.38

  • 那么复制过来的就都是子过程1和子工程2配置的依赖:5.8.38

  • 如果所有子工程引入新的版本,但是不是同一个:

    • 例如父工程 5.8.40,子工程15.8.38,子工程25.8.39
    • 那么复制过来的就是子过程1和子工程2配置的依赖:5.8.385.8.39

总结就是:【父工程】的依赖会传递给【子工程】用,但是【子工程】重新导入的依赖会覆盖【父工程】传递过来的。
另外:如果所有【子工程】都覆盖了【父工程】传递过来的依赖,那么【父工程】配置的这个依赖相当于没有用,dependency:copy-dependencies也就不会复制出来这个【父工程】配置的无用依赖。

mvn clean install dependency:copy-dependencies -DoutputDirectory=D:\file\IDEA_File\mylib -DincludeScope=compile -Dsilent=true -Dmdep.cpPom=false -DskipTests -T 4

mvn clean install dependency:copy-dependencies -DoutputDirectory=D:\file\IDEA_File\mylib -DincludeScope=compile -Dsilent=true "-Dmdep.cpPom=false" -DskipTests -T 4

// 会把父工程和子过程里面所有导入的依赖里的jar包复制到D:\ToolOfProductionData\IDEAFile\MavenTest\mylib中
// 如果子工程都没引入其他的依赖:那么复制过来的就都是父工程配置的依赖
// 如果子工程引入的版本都是同一个:例如父工程
mvn clean install dependency:copy-dependencies -DoutputDirectory=D:\ToolOfProductionData\IDEAFile\MavenTest\mylib -DincludeScope=compile -Dsilent=true "-Dmdep.cpPom=false" -DskipTests -T 4

pom.xml文件解析

父工程

<?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">
    <!-- 无视 maven版本相关  -->
    <modelVersion>4.0.0</modelVersion>

    <!-- 坐标信息 本工程的坐标信息 每个项目都有一个唯一的坐标信息 -->
    <!--
        怎么配置本工程的坐标信息:
        项目:
            groupId: 域名反过来 com.baidu
            artifactId: 项目名字
            version: 版本 1.0-SNAPSHOT SNAPSHOT:还没上线的最初的快照版本
        模块:前台、后台、公共模块
            groupId: 域名反过来+项目名字  com.baidu.项目名字
            artifactId: 模块名字
            version: 版本 1.0-SNAPSHOT SNAPSHOT:还没上线的最初的快照版本
    -->
    <groupId>org.example</groupId>
    <artifactId>MavenTest</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- 设置打包方式 默认是jar 常用的:jar war pom -->
    <packaging>pom</packaging>

    <modules>
        <module>maven_01</module>
        <module>maven_02</module>
    </modules>

    <!-- 属性 变量
            通常设置依赖的版本:统一管理版本
     -->
    <properties>
        <!--  当前jdk版本  其实一般不会在这里设置,idea启动项目里面已经设置了,其实这里可以删除     -->
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <!--  编码设置      -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!--  设置依赖的版本    -->
        <javax.servlet.version>3.1.0</javax.servlet.version>
    </properties>

    <!-- 通过坐标信息引用jar包,准确说是引入依赖,因为一个依赖会有多个jar包   -->
    <!-- 找依赖的地址: https://mvnrepository.com/  -->
    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.4</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>${javax.servlet.version}</version>
            <!--
            依赖范围:compile(默认)、provided、runtime、system、test
            有哪些范围:测试(main.java)、编译(test.java)、运行/打包(包内)

            test: 编译× 测试✔ 打包×      例如:junit  测试的时候才能用(main.java文件夹下的能用),编译、打包不能用
            provided:编译✔ 测试✔ 打包×  例如:javax.servlet(一般用tomcat启动,tomcat里已经有了javax.servlet,项目打包不用带这个包了,带了反而可能导致servlet版本混乱、项目增大)
            compile:编译✔ 测试✔ 打包✔   默认的
            runtime:编译× 测试✔ 打包✔   反射 数据库驱动class.forName()
            system:基本不会使用,了解即可 效果等于provided,但是其不会依赖仓库中jar包,而是引用本地物理路径的jar包  使用时配合一个<systemPath></systemPath>使用

            1.首先﹑依赖范围建议﹑哪怕所有的范围都设置compile,也不会影响功能正常使用。
            2.使用依赖提供的scope,直接中央仓库无脑复制就行了,并且idea也会自动补全。
            3.90%以上都会使用compile。
            4.但是如果做一个好的程序员,应该尽量让程序优雅,保证依赖的最小范围。
            -->
            <scope>compile</scope>
        </dependency>
    </dependencies>
    <!-- 对于子工程必须要的依赖,可以放在dependencies这里,直接传递给子项目-->

    <!-- 对于子工程不是必须要的依赖,可以放在dependencyManagement这里,子工程引入时不需要版本,因为这里控制了-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.8.40</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

子工程1

<?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>
    <parent>
        <groupId>org.example</groupId>
        <artifactId>MavenTest</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>maven_01</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.43</version>
        </dependency>
    </dependencies>

</project>

子工程2

<?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>
    <parent>
        <groupId>org.example</groupId>
        <artifactId>MavenTest</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>maven_02</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>
    </dependencies>
</project>

下载依赖流程

image-20250921143017835

# 没有镜像时的完整流程
开始下载依赖
    ↓
1. 检查本地仓库 (~/.m2/repository)
   ├── 存在 → 直接使用,结束
   └── 不存在 → 继续
    ↓
2. 按顺序访问 pom.xml 中配置的 <repositories>
   ├── 第1个repository → 直接访问其URL
   ├── 第2个repository → 直接访问其URL
   ├── ...
   └── 最后一个repository → 直接访问其URL
    ↓
3. 中央仓库(默认存在)
   └── 直接访问 https://repo.maven.apache.org/maven2/
    ↓
4. 找到依赖 → 下载到本地
   所有仓库都没找到 → 报错

# 配置镜像时的完整流程 
开始下载依赖
    ↓
1. 检查本地仓库 (~/.m2/repository)
   ├── 存在 → 直接使用,结束
   └── 不存在 → 继续
    ↓
2. 按顺序访问 pom.xml 中配置的 <repositories>
   ├── 第1个repository → 是否配置镜像 → 否就直接访问其URL,是就镜像拦截: 实际访问镜像配置的URL
   ├── 第2个repository → 是否配置镜像 → 否就直接访问其URL,是就镜像拦截: 实际访问镜像配置的URL
   ├── ...
   └── 最后一个repository → 是否配置镜像 → 否就直接访问其URL,是就镜像拦截: 实际访问镜像配置的URL
    ↓
3. 中央仓库(默认存在)
   └── → 是否配置镜像 → 否直接访问 https://repo.maven.apache.org/maven2/,是就镜像拦截: 实际访问镜像配置的URL
    ↓
4. 找到依赖 → 下载到本地
   所有仓库都没找到 → 报错

参考

参考视频:7、依赖引用_哔哩哔哩_bilibili

镜像和repositories

<!-- 项目的pom.xml -->
<repositories>
    <repository>
        <id>private</id>
        <name>自研私有库</name>
        <url>https://xxx/repositories/releases/</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>

    <repository>
        <id>central</id>
        <name>阿里云公共库</name>
        <url>https://xxx/repository/public</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>

    <repository>
        <id>spring</id>
        <name>阿里云spring仓库</name>
        <layout>default</layout>
        <url>https://xxx/repository/spring</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
    
    <!-- 最后会有一个隐含的中央仓库 id叫central -->
    <!--
    <repository>
        <id>central</id>
        <name>Central Repository</name>
        <url>https://repo.maven.apache.org/maven2</url>
        <layout>default</layout>
        <snapshots>
          <enabled>false</enabled>
        </snapshots>
    </repository>
	-->
</repositories>
<!-- Maven的settings.xml -->
<mirrors>
    <mirror>
    <id>mirrorId</id>
    <!-- mirrorOf配置*表示所有仓库 -->
    <!-- <mirrorOf>*,!repo1</mirrorOf> 这样配置表示,除了repo1,都重定向到<url>http://my.repository.com/repo/path</url>-->
    <mirrorOf>repositoryId</mirrorOf> 
    <name>Human Readable Name for this Mirror.</name>
    <url>http://my.repository.com/repo/path</url>
    </mirror>
</mirrors>

<mirrorOf>repositoryId</mirrorOf>里面repositoryId配置*:

  • 项目的pom.xml 里面配置的所有的,都重定向到<url>http://my.repository.com/repo/path</url>

<mirrorOf>repositoryId</mirrorOf>里面repositoryId配置对应的id:

  • 项目的pom.xml 里面配置的id,重定向到<url>http://my.repository.com/repo/path</url>
posted @ 2025-09-21 16:07  deyang  阅读(19)  评论(0)    收藏  举报