当前位置: 首页 > article >正文

【Java Maven框架】

前言


为什么要学习 Maven ?

没有 Maven 之前的开发环境:
<1> 会出现大量的重复文件,大量的 jar 包,导致项目体积增大,团队协作效率降低。
<2> 环境、版本冲突问题,比如 Windows 环境下能跑的项目在 Linux 就跑不起来。
<3> idea 虽然是自动保存自动编译的。但是打包、部署等操作还是需要我们自己完成的。

自从引入 Maven 后,以上的问题差不多就已经解决了,那么 Maven 的作用是什么呢?

项目构建:提供标准的、跨平台的自动化项目构建方式
依赖管理:方便快捷的管理项目依赖的资源(iar包),避免资源间的版本冲突问题
统一开发结构:提供标准的、统一的项目结构

前期回顾:

【Java 定时任务】

【Java 线程池】

【Java 阻塞队列】

【Java IO】 微信定时消息发送


目录

前言

Maven 目录结构

 Maven 的骨架

 pom.xml 文件

pom.xml 的三个组成部分

Maven 工程构建

Maven 工程测试

Maven 仓库

依赖与依赖程度

依赖程度 

隐藏的 scope 标签

依赖版本统一提取和维护

依赖传递和依赖冲突

依赖传递

依赖冲突

父工程与子工程

maven 工程的继承

父工程统一依赖管理 

maven 工程的聚合

 Maven 目录结构


 Maven 的骨架

我们使用 idea 创建的 Maven项目,它有固定的骨架结构如下:

maven-project
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   └── resources
│   └── test
│       ├── java
│       └── resources
└── target

 maven-project 是项目名称,其他的骨架的作用如下

骨架名称作用
pom.xml项目描述文件
src/main/java存放Java源码的目录
src/main/resources存放资源文件的目录
src/test/resources存放测试资源的目录
target所有编译、打包生成的文件都放在这里

 pom.xml 文件

        建立 Maven 项目的初始 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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.thz</groupId>
    <artifactId>maven-project</artifactId>
    <version>1.0-SNAPSHOT</version>
    ...
    <properties>
        <maven.compiler.source>22</maven.compiler.source>
        <maven.compiler.target>22</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

</project>

pom.xml 的三个组成部分

pom.xml 声明

<?xml version="1.0" encoding="UTF-8"?>

pom.xml 文档类型定义

<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">

pom.xml 正文

除了以上两个部分外剩下的都属于正文部分

坐标三要素:

groupId:组ID,一般使用公司名倒置
artifactID:项目ID,一般就是项目名称
version:版本号

Maven 坐标的作用
        使用唯一标识,唯一性定位资源位置,通过该标识可以将资源的识别与下载工作交由机器完成 。

项目的属性设置: 

maven.compiler.source:源代码的 JDK 版本
maven.compiler.target:测试代码的 JDK 版本
project.build.sourceEncoding:源代码的编码方式
 

Maven 工程构建


        项目构建是指将源代码、依赖库和资源文件等转换成可执行或可部署的应用程序的过程,在这个过程中包括编译源代码、链接依赖库、打包和部署等多个步骤。

        我们可以在 idea 中找到 Maven 的 Lifecycle,这是 Maven 命令的可视化按钮: 

 Maven 命令的作用

命令描述
 compile编译项目,生成 target 文件
package打包项目,生成 jar 文件
clean清理编译或打包后的项目结构
install打包后上传到 maven 本地仓库
deploy只打包,上传到 maven 私服
site生成站点(报告)
test执行测试源(测试)

Maven 工程测试

我们可以写一些代码来测试一下:

 Demo:

public class Demo {
    public String Func(String name){
        System.out.println("Hello "+name);
        return "Hello "+name;
    }
}

 DemoTest:

public class DemoTest {
    @Test
    public void Test(){
        Demo demo = new Demo();
        String ret =  demo.Func("Maven");
        Assert.assertEquals(ret, "Hello Maven");
    }
}

pom.xml:

导入以下测试需要的依赖

<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
</dependencies>

此时我们只需点击 Maven 自带的按钮程序即可运行:

test:

 此时我们可以看见所有运行编译的文件都放在 target 中

package:

此时我们就可以看到打成 jar 的文件

        但是注意此时这个 jar 包实际只有 3KB,它打包的只是项目的源文件,但是我们的依赖并没有被打包进 jar 包

        通过执行一些指令,我们发现执行后面的指令时,会把前面的指令都执行一边。比如执行编译指令,首先会执行清理将之前的 target 文件给清理掉。


插入仓库的概念:

Maven 仓库

仓库的概念:用于存储资源,包含各种 jar 包

仓库分类作用
远程仓库非本机电脑上的仓库,为本地仓库提供资源
本地仓库自己电脑上存储资源的仓库,连接远程仓库获取资源
中央仓库Maven 团队维护,存储所有资源的仓库
私服部门、公司范围内存储资源的仓库,从中央仓库获取资源 

私服的作用:

保存具有版权的资源,包含购买或自主研发的jar
中央仓库中的iar都是开源的,不能存储具有版权的资源
一定范围内共享资源,仅对内部开放,不对外共享 

        举个例子,当导入一个依赖出现爆红的情况,说明我们本地仓库中没有这个依赖的 jar 包;当我们刷新 Maven 会发现右下角有一个进度条,这说明从 Maven 中央仓库下载依赖。当然你也可以自己从官网中下载需要依赖包:点击进入 Maven 中央仓库


        注意 install 是比较常用的一个指令,当你写完项目代码都应该点一下,这样团队成员访问你的代码都是最新的。例如,你的项目源代码改了,但是你的本地仓库的代码没有改,这个时候团队的其他成员就访问不到你改好后的代码。

工程构建的好处: 

        项目构建是软件开发过程中至关重要的一部分,它能够大大提高软件开发效率,使得开发人员能够更加专注于应用程序的开发和维护,而不必关心应用程序的构建细节。
        同时,项目构建还能够将多个开发人员的代码汇合到一起,并能够自动化项目的构建和部署,大大降低了项目的出错风险和提高开发效率。

依赖与依赖程度


依赖程度 

隐藏的 scope 标签

        我们之前添加依赖,都是直接添加坐标三要素即可,但是它还有一个隐藏的标签 - scope,决定这个依赖的依赖程度。

    <dependencies>
        <dependency>
            <groupId>项目组织</groupId>
            <artifactId>项目名称</artifactId>
            <version>项目版本</version>
            <scope>依赖的程度</scope>
        </dependency>
    </dependencies>

scope 常用的取值有四个:

取值作用
compile如果不设置 scope,默认值就是 compile 表示源代码环境需要,测试环境也需要,并且打包的时候包含
provided源代码环境需要,测试环境也需要,但是打包的时候不包含
test源代码环境不需要,打包的时候不包含,测试环境需要
runtime运行时需要,编译时不需要(源代码环境不需要,测试环境不需要),并且打包的时候包含

compile 就不在过多赘述了,比较好理解。我们来看看其他取值的含义:

provided (源代码环境需要,测试环境也需要,但是打包的时候不包含)

        servlet-api 源代码与测试环境都需要,但是我们的 Tomcat 中已经存在,打包的时候就可以不需要了。

test (源代码环境不需要,打包的时候不包含,测试环境需要)

        这里举个例子,比如,你的项目都已经测试好了,项目准备部署,我还需要测试代码做什么呢?测试只是检验源代码的正确性,而不涉及项目的部署,所以打包的时候不需要。

runtime (运行时需要,编译时不需要,并且打包的时候包含)

        这里举个例子,比如,你可能在编译的时候只需要 API、JAR,而只有在运行的时候才需要 JDBC 驱动实现。

依赖版本统一提取和维护

先来说一下之前写法的弊端:

... 
    <groupId>com.thz</groupId>
    <artifactId>maven-project</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
        ...
    </dependencies>
...

        例如:项目的依赖一多,假设某天需要修改一个依赖的版本号,我是不是还需要去那么多依赖中去找,这就显得很麻烦。有没有更直接的方法呢?可以将 version 这行提取出来,统一维护。

... 
<groupId>com.thz</groupId>
    <artifactId>maven-project01</artifactId>
    <version>${project01.version}</version>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        ...
    </dependencies>

    <!--声明版本-->
    <properties>
        <maven.compiler.source>22</maven.compiler.source>
        <maven.compiler.target>22</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!--命名随便,内部制定版本号即可-->
        <project01.version>1.0</project01.version>
        <junit.version>4.12</junit.version>
        <mysql.version>8.0.33</mysql.version>
        ...
    </properties>

例如:<project01.version>1.0</project01.version> 相当与取别名,project01.version 对应的就是 1.0 这个版本号。而 ${project01.version} 的 ${} 操作就是提取这个别名的内容。

        我们将依赖的版本号统一放在 properties 中维护,这样就可以很明确的知道什么依赖用了什么版本;当需要修改依赖的版本号时就可以在此修改,相对应的 ${...} 的值也会修改。

依赖传递和依赖冲突


依赖传递

        像以上这种情况,B依赖于A,C依赖于B;那么C是否依赖于A呢?答案是肯定的。之前我们提到有个隐藏标签 scope ,没有明确的写,那么就是默认值 compile。三个环境都需要依赖A,那么C在打包的时候还是需要将A一起打包的。所以说,C依赖于A。

将 B 的 scope 标签换成 provided (源代码环境需要,测试环境也需要,但是打包的时候不包含)。

        这个时候C是否依赖于A呢?因为B是provided标签修饰的,打包时不包含,也就是说C是看不见 A 的。所以此时 C 并不依赖于 A。

依赖冲突

举个例子:

项目B依赖于mysql5.7,项目A依赖于mysql8.0;此时C同时依赖于A、B。那么项目 A、B 必然会因为 mysql 的版本号冲突。那么怎么解决冲突呢?其实很简单就是排除掉一个。

       <!--使用exclusions标签配置依赖的排除-->
       <exclusions>
       <!--在exclusion标签中配置一个具体的排除-->
                <exclusion>
                    <!--指定要排除的依赖的坐标(不需要写version)-->
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                </exclusion>
       </exclusions>

为了更好的展示,这里新建了两个子模块

untitled1:

    ...
    <artifactId>untitled1</artifactId>

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

untitled2:

    ...
    <artifactId>untitled2</artifactId>

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

untitled:

    ...
    <modules>
        <module>untitled1</module>
        <module>untitled2</module>
    </modules>

    <dependencies>
        <dependency>
            <groupId>com.thz</groupId>
            <artifactId>untitled2</artifactId>
            <version>1.0</version>
        </dependency>

        <dependency>
            <groupId>com.thz</groupId>
            <artifactId>untitled1</artifactId>
            <version>1.0</version>
        </dependency>
    </dependencies>
    ...

此时,我在 untitled 中导入了 untitled1 和 untitled2 依赖;但是会因为 untitled1 和 untitled2 中的数据库版本问题而冲突。

        可以观察到 idea 帮我们检查到了冲突并且还做了版本屏蔽;但是 idea 不是万能的有些冲突问题还需要自己手动解决。以上屏蔽了 mysql8.0 的版本,那么我想要屏蔽 mysql5.0 的版本该怎么办呢?- 可以使用 exclusion 排除标签。

    ...
<dependencies>
        <dependency>
            <groupId>com.thz</groupId>
            <artifactId>untitled2</artifactId>
            <version>1.0</version>
            <exclusions>
                <exclusion>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
</dependencies>
    ...

         此时使用的就是 mysql8.0 的版本。依赖冲突问题最好在导入依赖的时候先想会发生什么冲突,如果等到发生了冲突再来排查,这样排查的时间成本会很高,因为一个项目导入的依赖是非常多的。

父工程与子工程


maven 工程的继承

        利用 Maven 可以对项目进行分模块开发。那么怎样把各个模块整合到一起呢?

        这就利用了 Maven 继承的特性。一般是每个模块都继承一个父工程。

子类:声明父项目的地址

    <!--定位父项目位置-->
    <parent>
        <groupId>com.thz</groupId>
        <artifactId>maven-project01</artifactId>
        <version>1.0</version>
    </parent>

    <!--子项目的名称-->
    <artifactId>untitled1</artifactId>

        我们在子工程中通过 parent 标签定位父工程的位置,这样的好处就是可以把一些共性放在父类里面。然后我们有多个子工程,这些子工程就没有必要去设置这个共性。因为它默认情况会继承。 

父工程统一依赖管理 

 父项目声明依赖:

  • 使用 dependencyManagement 标签配置对依赖的管理,被管理的依赖并没有真正被引入到工程,只有在子项目调用了才会被引入工程。
<!-- 使用 dependencyManagement 标签配置对依赖的管理 -->
<!-- 被管理的依赖并没有真正被引入到工程 -->
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>6.0.10</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>6.0.10</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>6.0.10</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-expression</artifactId>
      <version>6.0.10</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>6.0.10</version>
    </dependency>
  </dependencies>
</dependencyManagement>

子项目继承依赖:  

  • 子工程引用父工程中的依赖信息时,可以把版本号去掉,因为使用的是父工程默认的版本号。
<!-- 子工程引用父工程中的依赖信息时,可以把版本号去掉。  -->
<!-- 把版本号去掉就表示子工程中这个依赖的版本由父工程决定。 -->
<!-- 具体来说是由父工程的 dependencyManagement 来决定。 -->
<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
  </dependency>
</dependencies>

 

maven 工程的聚合


 父类:声明子项目的模块

    <modules>
        <module>untitled1</module>
        <module>untitled2</module>
    </modules>

         Maven 项目中的<modules>元素,它指定了当前项目的子模块列表。在这个例子中,项目有两个子模块,分别是untitled1和untitled2。这意味着这个项目是一个聚合项目,它管理着多个子模块的构建和依赖关系。当你运行 Maven 命令时,Maven会自动构建所有的子模块。


http://www.kler.cn/a/371811.html

相关文章:

  • 为什么使用Node.js爬虫更优
  • RagFlow本地部署使用
  • No.42 接雨水
  • 小柴冲刺软考中级嵌入式系统设计师系列二、嵌入式系统硬件基础知识(6)嵌入式系统总线及通信接口
  • lab1测试脚本注解
  • 记录一个容器间访问不通问题
  • 五个我经常使用的前端开发的库
  • 【机器学习】任务九:卷积神经网络(基于 Cifar-10 数据集的彩色图像识别分类、基于 CNN 的手写数字识别的实验)
  • 基于java的山区环境监督管理系统(源码+定制+开发)环境数据可视化、环境数据监测、 环境保护管理 、污染防治监测系统 大数据分析
  • 【C++】string 类深度解析:探秘字符串操作的核心
  • python如何完成金融领域的数据分析,思路以及常见的做法是什么?
  • 【Django】创建项目、启动及app过程及遇到的问题和解决方案
  • Firefox和Chrome谁的插件生态系统更完善
  • 8年经验之谈 —— 如何使用自动化工具编写测试用例?
  • Java基础(4)——构建字符串(干货)
  • 结合Intel RealSense深度相机和OpenCV来实现语义SLAM系统
  • 开源AI助力医疗革新:OCR系统与知识图谱构建
  • 大厂物联网(IoT)高频面试题及参考答案
  • HTML入门教程7:HTML样式
  • Go 读取xls文件 (shakinm/xlsReader/xls)
  • 消息队列-Rabbitmq(消息发送,消息接收)
  • 爬虫设计思路
  • ‘cmd‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。
  • 【SQLite】改善默认输出格式不直观难以阅读问题:通过修改输出设置提升数据可读性
  • 【http作业】
  • Chrome异步编程