【重构谷粒商城】06:Maven快速入门教程

发布于:2025-02-14 ⋅ 阅读:(30) ⋅ 点赞:(0)

重构谷粒商城06——Maven快速入门教程

前言:这个系列将使用最前沿的cursor作为辅助编程工具,来快速开发一些基础的编程项目。目的是为了在真实项目中,帮助初级程序员快速进阶,以最快的速度,效率,快速进阶到中高阶程序员。

本项目将基于谷粒商城项目,并且对谷粒商城项目进行二次重构,使其满足最新的主流技术栈要求。

上一篇文章我们对docker容器化技术进行了讲解,这一篇文章我们将快速、精准、全面的学习maven。准备好项目所需要的maven环境。

1.Maven简介

Maven 是一个用于 Java 项目的构建和管理工具,旨在简化项目的构建过程,方便地管理项目所需的依赖库。

其主要作用包括:

依赖管理:通过配置 pom.xml 文件,Maven 可以自动下载并管理项目所需的所有依赖库,避免手动下载和导入的繁琐过程。

image-20250122155631537标准化项目结构:Maven 提供了一套标准的项目目录结构,使得不同开发者之间的协作更加顺畅,项目的可维护性更高。

构建自动化:Maven 定义了一系列标准的构建生命周期阶段,如编译、测试、打包、部署等,开发者只需执行相应的命令即可完成这些操作,提高了开发效率。

在java项目中需要打包很多源代码文件。如果没有maven,就会很麻烦。

image-20250122155813163

Maven 的构建过程遵循一系列预定义的生命周期,每个生命周期包含多个阶段(phase),每个阶段执行特定的任务。在执行 mvn package 命令时,Maven 会按照以下主要阶段顺序进行操作:

  1. validate:验证项目是否正确,所有必要的信息是否可用。比如检查项目的目录结构:确保项目的目录布局符合标准规范,例如源代码应位于 src/main/java,资源文件应位于 src/main/resources 等。
  2. compile:编译项目的源代码。 Maven 的 compile 阶段,Maven 会使用 Java 编译器将项目的源代码(即 .java 文件)编译成字节码文件(即 .class 文件)。这些 .class 文件通常位于项目的 target/classes 目录下。
  3. test:使用适当的单元测试框架测试编译的源代码。
  4. package:将编译后的代码打包成可分发的格式(如 JAR、WAR)。打包会将应用程序及其所有依赖项打包成一个文件,简化了分发和部署过程,确保在不同环境中都能一致运行。还对代码进行混淆和压缩,增加反编译的难度,保护知识产权。

此外,Maven 还有其他生命周期和阶段,例如 clean 生命周期用于清理项目,site 生命周期用于生成项目站点文档等。

按照流程走。so easy。

image-20250122160304606

通过使用 Maven,开发者可以更高效地管理项目的构建过程和依赖关系,提升开发效率,减少重复劳动。

2、基本概念

2.1 pom.xml文件

pom.xml 是 Maven 项目的核心配置文件,用于定义项目的基本信息、依赖关系、构建配置等。通过配置 pom.xml,Maven 可以自动管理项目的构建过程和所需的依赖库,提高开发效率,减少手动配置的繁琐。

image-20250122160827395

maven通过很多工具,安装pom文件对项目进行编译、打包等。

image-20250122160730963

2.2 Maven仓库

Maven 仓库是用于存储项目所需的依赖库(如 JAR 包)的地方,方便 Maven 在构建项目时获取和管理这些依赖。

Maven 仓库主要分为三种类型:

  1. 本地仓库:存储在开发者本地计算机上的仓库,默认路径为用户主目录下的 .m2/repository。当构建项目时,Maven 会首先从本地仓库中查找所需的依赖。
  2. 中央仓库:由 Maven 官方提供的公共仓库,包含了大量常用的开源库。当本地仓库中没有找到所需依赖时,Maven 会从中央仓库下载。
  3. 远程仓库:由第三方提供的仓库,如公司内部的私有仓库或其他公共仓库。开发者可以在项目的 pom.xml 中配置这些仓库的地址,以获取特定的依赖。

image-20250122161118014

3、Maven安装

先需要安装jdk(1.7+)。这里不再赘述。

maven官网:https://maven.apache.org/

傻瓜式安装。

image-20250122161634824

配置下环境变量。这么基础的内容,我就偷个懒,跳过了。(下图为mac中环境变量配置示意,具体自行查下)

image-20250122162245524

4、maven的配置

在安装目录下,找到配置文件。

image-20250122163455884

配置下本地仓库的位置。

  <!-- 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>C:\Users\banjiu\workspace\mvn-repository</localRepository>

配置镜像。

</mirrors>
 <mirror>  
    <id>alimaven</id>  
    <name>aliyun maven</name>  
    <url>http://maven.aliyun.com/nexus/content/groups/public/</url>  
    <mirrorOf>central</mirrorOf>          
 </mirror>
</mirrors>

配置jdk版本。可以修改为自己本地安装的jdk版本。

<profiles>
  <profile>
    <id>jdk-22</id>
    <activation>
      <jdk>22</jdk>
    </activation>
    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.10.1</version>
          <configuration>
            <source>22</source>
            <target>22</target>
          </configuration>
        </plugin>
      </plugins>
    </build>
  </profile>
</profiles>

执行下面命令,确认下配置是否不存在问题。

mvn help:system

看到build success,说明ok。

image-20250122173145053

补充:win11控制台中文乱码,可以输入下面命令临时解决。

chcp 65001

5、mvn命令的使用

可以参考官网文档:https://maven.apache.org/guides/getting-started/index.html

先快速创建一个maven项目。

mvn archetype:generate -DgroupId=com.mycompany.app -DartifactId=my-app -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.5 -DinteractiveMode=false

mvn archetype:generate:调用 Maven 的 Archetype 插件,生成项目骨架。

-DgroupId=com.mycompany.app:指定项目的 groupId,通常是组织或公司域名的反向表示。

-DartifactId=my-app:指定项目的 artifactId,即项目的名称。是项目的唯一标识。

-DarchetypeArtifactId=maven-archetype-quickstart:指定使用的原型模板,这里选择了 maven-archetype-quickstart,这是一个用于快速启动 Java 项目的模板。

在下面输出信息中,可以看到项目生成位置。

image-20250123154927764

使用vscode打开项目。

code C:\Users\半旧\my-app

image-20250123155533794

下面我们可以来看看下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">

接下来是groupId、artifactId、version,是我们命令行输入的参数。

<groupId>com.mycompany.app</groupId>
 <artifactId>my-app</artifactId>
 <version>1.0-SNAPSHOT</version>

我们编译下项目。

image-20250123160150355

可以看到多了一个target目录,存放的是编译生成的字节码文件。

接下来打包项目。

mvn package

可以看到生成了jar包。

image-20250123160659638

运行下这个jar包。

PS C:\Users\半旧\my-app> java -cp .\target\my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
Hello World!

-cp 指定类路径(Classpath),即 Java 程序运行时查找类文件的位置。

6、在IDEA中使用maven

创建一个maven项目。

image-20250123161453835

如果您出现了使用maven archetype创建项目的没有生成src目录,而且pom文件为空的问题:

image-20250124104731909

请将创建项目时,选择的jdk版本降级。你的版本太高了。实测选择20是没有问题的。

image-20250124104849580

好了,现在看看生成的项目,和我们之前用命令生成的一模一样。

可以将maven换成自己下载部署的maven。

image-20250210171604437

还有配置文件记得替换。

image-20250210172249164

7、Maven的生命周期和插件

打开maven工具栏,即可看到maven的生命周期

image-20250211100359653

上面生命周期,其实是依靠插件来实现的,点开插件,可看到,与生命周期可以对应上。

image-20250211100618807

插件,本质上就是java的类,它们实现了maven的接口,在不同的阶段被调用。

maven提供了三种主要的生命周期,分别是cleandefaultsite

clean用来清理项目,把之前生成的字节码文件、jar包都清理掉。可以双击执行下,看看效果(也可以执行mvn clean命令)

先编译下。看到生成了target目录,里面包含了class文件。

image-20250211101943727

再clean,执行后target目录被清除

image-20250211102137988

在编译时,可能出现如下错误。

[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to C:\Users\半旧\Desktop\wz\javawrokspace\test\target\classes
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR : 
[INFO] -------------------------------------------------------------
[ERROR] 不再支持源选项 7。请使用 8 或更高版本。
[ERROR] 不再支持目标选项 7。请使用 8 或更高版本。
[INFO] 2 errors 
[INFO] -------------------------------------------------------------

解决方法:在pom.xml中限定maven使用的jdk版本。

<build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version> <!-- 确保使用的是最新版本 -->
        <configuration>
          <source>1.8</source> <!-- 设置为 Java 8 -->
          <target>1.8</target> <!-- 设置为 Java 8 -->
        </configuration>
      </plugin>
    </plugins>
  </build>

reload maven project。

image-20250211102524140

第二个生命周期,default。包含了我们从头开始构建一个项目的主要步骤,是 Maven 的主要生命周期,负责处理项目的编译、测试、打包等工作。

验证(validate):验证配置项文件是否正确。比如pom文件有没有格式错误。

编译(compile): 编译源代码(src/main/java)。如果有编译错误,这个阶段会失败

测试(test): 运行项目中的单元测试(src/test/java)。测试代码会被编译,然后执行。通常使用 JUnit 运行单元测试。

打包(package): 将编译后的代码打包成可发布的格式(如 .jar.war 文件)。这一步会把项目构建成可以部署的形式。执行package,会自动先执行编译、测试。maven生命周期,每一个阶段都是依赖上一个阶段的。如果上一周期执行失败,下一个周期就不会再执行了。

verify: 运行任何验证任务,比如集成测试,确保项目打包后的内容是可用的。

install: 将打包好的文件安装到本地 Maven 仓库,供本地的其他项目使用。这样其他项目就可以通过 groupId, artifactId, version 来依赖这个构建产物。

deploy: 将构建好的文件上传到远程 Maven 仓库(一般是项目的私服仓库,需要提前部署配置),供其他开发者或者项目使用。通常用于生产环境。

第三个生命周期,site。站点生命周期负责生成项目的站点文档(如 Javadoc、项目报告等)。站点生命周期一般用于生成和发布项目相关的静态文档。

image-20250211104521106

浏览器打开,可以看到项目基本的一些信息。

image-20250211104632033

我们前面说过,maven生命周期,每一个阶段都是依赖上一个阶段的,会自动执行前面的生命周期。

在实际项目中,我们经常使用的是mvn package和mvn install。

8、Maven的坐标和存储位置

maven标识存储jar包,依靠坐标定位。pom文件中的groupId(公司/组织标识)、artifactId(项目标识)、version(版本号)可以唯一标识一个jar包。

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

image-20250211105852432

在maven repository网站,可以找到几乎所有开源jar包的坐标。https://mvnrepository.com/

image-20250211110056275

找个jdbc测试玩一下。

image-20250211110404466

粘贴。

image-20250211110453908

reload maven project。

image-20250211110632254

在本地仓库中就可以找到了。

image-20250211110828793

9、依赖管理

Maven 的依赖管理是其最强大的功能之一,它帮助开发者管理项目中的外部库、框架和其他组件的依赖关系。通过依赖管理,Maven 自动处理各种版本和依赖冲突,简化了开发人员的工作流程。

9.1 指定依赖范围

scope可以指定依赖的范围。

  <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>

常见的依赖范围包括:

  • compile:默认的范围,表示该依赖对所有阶段都是可见的(编译、测试、运行)。可省略不写。
  • provided:表示该依赖在编译时需要,在运行时不需要。比如Lombok,主要功能是简化编写代码时Getter、Setter的编写。运行时不需要。比如在 Web 应用中,运行时Servlet API 可以被 Web 容器提供。运行时也不需要。
  • runtime:表示在编译时不需要,但在运行时需要。最典型的就是jdbc。
  • test:表示该依赖只在测试时需要。无法在非测试代码中导入,不会被打包到最终的jar包中。比如Junit框架。
  • system:不推荐。表示依赖于本地系统中某个特定的文件。配合systempath使用。这样maven就不会去私服仓库或者中央仓库下载,而是直接使用本地的jar包。
  • import:用于导入依赖的 BOM 文件(Bill of Materials)。不会实际引入依赖,只是用来管理版本号,后面讲解父子工程时,再详细解释。

9.2 添加依赖

在maven repository网站,找到jar包的坐标,添加到pom文件中,刷新下maven项目就可以了。

9.3 依赖传递

依赖是具有传递性的。直接写再pom.xml中的依赖是直接依赖。

依赖传递(Transitive Dependency)是指当你的项目依赖于某个库时,该库可能也依赖于其他的库。Maven 会自动将这些 传递性依赖 添加到你的项目中,而无需你显式地声明它们。

举个例子

假设有三个项目:项目 A项目 B项目 C。它们的依赖关系如下:

  • 项目 A 依赖于 项目 B
  • 项目 B 依赖于 项目 C

在这种情况下,项目 C项目 A 的传递性依赖,Maven 会自动将 项目 C 添加到 项目 A 的依赖中。

注意:只有依赖范围是compile的依赖才会被传递,其它依赖不会被传递。

可以通过exclusion标签,人为的排除一个依赖,也可以通过optional标签,标记一个依赖是可选的,这样就不会被继承。在父子工程中,我们将详细讲解这一点。

9.4 依赖冲突

如果一个项目中依赖了两个不同的依赖A、B,而这两个依赖A、B又同时依赖另外一个依赖C的不同版本,那么就会出现依赖冲突。

Maven会根据一定规则解决依赖冲突。

首先是最短路径优先原则。即 Maven 会选择在项目依赖树中离当前依赖最近的版本。

第二个是先声明优先。路径长度相同情况下,在pom.xml文件中先声明的依赖,会被优先使用。

image-20250211135758670

如果需要,你也可以通过 <dependencyManagement> 来显式声明一个特定版本,避免自动选择。

9.5 父子工程

在 Maven 中,父子工程(Parent-Child Projects)是通过继承来管理多个模块的构建过程的一种机制。父子工程结构通常用于多模块项目(Multi-Module Projects),使得多个子项目可以共享构建配置、依赖管理和插件配置,从而提高项目的可维护性和一致性。

父工程(Parent Project) 是一个包含共同配置、依赖管理、插件管理等的项目。它通常不会被独立构建,而是作为一个父级项目存在。

子工程(Child Project) 是继承自父工程的项目,通常是具体的模块或子项目。子工程可以继承父工程的配置,同时还可以进行额外的自定义和覆盖。

新建一个项目。

image-20250211140416688

改下打包方式。改为pom,这样不会父模块在打包时生成任何的jar包或者war包,而是用于管理其它子工程。

image-20250211140747088

父工程的src一般也不需要,删除。

image-20250211141027348

建个子模块。

image-20250211141314677

看下子工程的pom文件。可以看到,子工程pom文件中,指定了父工程目标,子工程自己的groupid省略了,因为会继承父工程的groupid。

image-20250211141450230

再建立子工程child-b,child-c。

再回过头看父工程的pom文件。

image-20250211142050705

执行父工程的maven命令,子工程都会被执行一次。

image-20250211142340528

9.6 手动解决依赖冲突

在模块a中引入模块b,c的依赖。

image-20250211143620632

在模块b,c中分别引入不同版本的spring。

image-20250211144307741

maven会根据先生们原则,自动引用模块b中的spring依赖。

image-20250211144705259

如果我们不希望模块b的spring模块被模块a优先使用(该依赖非必要)。我们可以在模块a中pom文件,增加exclusion标签,

image-20250211150856192

reload maven project,关闭idea重新打开。

image-20250211150945162

在模块b的pom文件中,添加optional标签,可以实现同样效果。

image-20250211160015262

使用optional标签和exclusion标签解决依赖冲突,有什么不同?

在 Maven 中,optional 标签exclusion 标签 都是用来解决依赖冲突和管理依赖关系的工具,但它们的使用方式和目的有所不同。我们来分别看看这两个标签的区别:

1. optional 标签

  • 作用optional 标签用于标识某个依赖是可选的。也就是说,该依赖不是项目必需的,并且不会传递给依赖该项目的其他模块。它只在当前模块需要时才会被包含。
  • 使用场景:你可以使用 optional 来告诉 Maven,某个依赖是当前模块使用的,但不希望这个依赖在子项目中被传递下去。
  • 默认行为:如果没有使用 optional 标签,Maven 会将所有的直接依赖传递给依赖这个模块的其他模块。
  • 典型用途:当你希望某个库在你的模块中可用,但不希望它在依赖你的模块的其他模块中自动生效时,使用 optional
示例:

假设你依赖于某个库,但它是可选的,不希望传递给子模块。

<dependency>
    <groupId>com.example</groupId>
    <artifactId>some-library</artifactId>
    <version>1.0.0</version>
    <optional>true</optional>
</dependency>

在这个例子中,some-library 被标记为可选依赖,它仅在当前模块中使用,而不会传递给依赖当前模块的其他模块。

2. exclusion 标签

  • 作用exclusion 标签用于排除传递性依赖,通常是你不希望引入到项目中的传递性依赖。如果某个库是你依赖的其他库的传递性依赖,而你不希望它出现在项目中,你可以使用 exclusion 来显式地排除它。
  • 使用场景:当你发现某个传递性依赖可能会引起冲突或者不需要时,可以使用 exclusion 标签排除它。
  • 典型用途:用于排除冲突的依赖项、过时的版本或不需要的传递性依赖。
示例:

假设你依赖的库 A 引入了 spring-context,但是你不希望它出现在你的项目中:

<dependency>
    <groupId>com.example</groupId>
    <artifactId>library-a</artifactId>
    <version>1.0.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
        </exclusion>
    </exclusions>
</dependency>

在这个例子中,spring-context 是一个传递性依赖,通过 exclusion 排除了它。

9.7 依赖继承

下面我们讲下如何使用父子依赖,来管理一些公共的依赖。

先将子模块的依赖,全部删除。

在父模块中定义依赖,子模块也会继承。

image-20250211162804293

不过,这样无论子模块是否需要这个依赖,都会继承。因此,我们一般会使用depencymanagement标签。父工程定义版本。子模块按需继承,不关心版本。

image-20250211163227294

image-20250211163430893

标签properties,可以定义一些公共的属性。

image-20250211163801805

10、私服仓库

最常用的是Nexus。这里了解下基本的作用和用途,需要时再学习下就行。

私服仓库(私有 Maven 仓库)是一个组织或团队内部使用的专用仓库,用来存储和管理公司或项目特定的依赖、构件(如 JAR、WAR 文件)以及其他 Maven 构建成果。私服仓库通常是托管在公司内部的一个服务器上,或者使用第三方服务(如 Nexus、Artifactory、GitHub Packages 等)来搭建。私服仓库的作用和优势主要有以下几个方面:

  1. 管理和存储内部依赖
  • 许多企业和组织会开发自己的内部库、工具或者服务,这些库通常是私有的、公司专有的,不会公开发布到公共 Maven 中央仓库。私服仓库可以用来存储这些内部的构件,方便团队和项目使用。
  • 例如,一个公司内部的共享库,其他项目或团队可以通过 Maven 直接引用这个库。
  1. 解决外部依赖的缓存和优化构建速度
  • 当项目依赖于外部的公共库时,每次构建时 Maven 会去公共仓库下载这些依赖文件。如果网络不稳定或外部仓库的访问速度较慢,可能会影响构建速度。
  • 私服仓库充当了一个 代理 的角色,缓存来自公共 Maven 仓库的依赖,所有开发者都可以从私服仓库获取这些依赖,这样就避免了每次构建时都去公共仓库下载依赖,提高了构建的速度和稳定性。
  1. 版本控制和依赖管理
  • 私服仓库可以帮助组织控制和管理所有使用的库和版本。你可以在私服仓库中上传指定版本的构件,这样不同的团队可以确保使用的是相同版本的依赖,从而避免了版本冲突。
  • 通过私服仓库,可以方便地管理内部构件的版本,进行统一的发布和管理。
  1. 保证安全性和合规性
  • 有些库和工具是内部开发的,需要保护源代码或确保外部无法访问。私服仓库能够提供访问权限控制,确保只有授权的用户或团队能够上传或下载特定的依赖,保护公司的知识产权和敏感信息。
  • 如果你依赖的库包含了有法律或合规要求的组件,可以通过私服仓库来确保所有使用的构件都是经过合规检查的。
  1. 定制化构件发布流程
  • 私服仓库允许组织实现自定义的构件发布和分发流程。开发人员可以将构件上传到私服仓库,并按照企业内部的要求发布构件。
  • 如果某个构件有安全问题或需要更新版本,私服仓库可以帮助管理这些更新,并确保所有团队都能快速获取到新的版本。
  1. 减少依赖的外部风险
  • 公共仓库(如 Maven Central)可能会遇到不可用或故障的情况,这可能会导致构建失败或依赖无法下载。私服仓库提供了更高的可靠性,因为它可以由公司自己托管,并且不依赖外部服务。
  • 如果使用私服仓库,即使公共仓库出现问题,构建依赖仍然可以从本地或内部的私服仓库中获取。
  1. 自定义构建工具和插件的管理
  • 除了常见的依赖管理,私服仓库还可以用来存储公司内部开发的插件、工具和构建脚本,供所有开发人员和项目共享。这样,团队可以更好地控制和管理工具链。
  1. 版本兼容性和逐步迁移
  • 如果你的团队正在从某个版本迁移到新版本,私服仓库可以作为迁移工具,管理不同版本的构件,并帮助团队在旧版本和新版本之间平滑过渡。

网站公告

今日签到

点亮在社区的每一天
去签到