Maven高级

Maven 是一款构建和管理 Java 项目的工具

Maven高级内容包括:

  • 分模块设计与开发

  • 继承与聚合

  • 私服

1.分模块设计与开发

1.1介绍

所谓分模块设计,顾名思义指的就是我们在设计一个 Java 项目的时候,将一个 Java 项目拆分成多个模块进行开发。

1). 未分模块设计的问题

如果项目不分模块,也就意味着所有的业务代码是不是都写在这一个 Java 项目当中。随着这个项目的业务扩张,项目当中的业务功能可能会越来越多。

假如我们开发的是一个大型的电商网站,这个项目组至少几十号甚至几百号开发人员,这些开发人员全部操作这一个 Java 项目。此时大家就会发现项目管理和维护起来将会非常的困难。假如在我们的项目当中,我们自己定义了一些通用的工具类以及通用的组件,而公司还有其他的项目组,其他项目组也想使用我们所封装的这些组件和工具类,其实是非常不方便的。因为 Java 项目当中包含了当前项目的所有业务代码,所以就造成了这里面所封装的一些组件会难以复用。

总结起来,主要两点问题:不方便项目的维护和管理、项目中的通用组件难以复用。

2). 分模块设计

分模块设计我们在进行项目设计阶段,就可以将一个大的项目拆分成若干个模块,每一个模块都是独立的。

比如我们可以将商品的相关功能放在商品模块当中,搜索的相关业务功能我都封装在搜索模块当中,还有像购物车模块、订单模块。而为了组件的复用,我们也可以将项目当中的实体类、工具类以及我们定义的通用的组件都单独的抽取到一个模块当中。

如果当前这个模块,比如订单模块需要用到这些实体类以及工具类或者这些通用组件,此时直接在订单模块当中引入工具类的坐标就可以了。这样我们就将一个项目拆分成了若干个模块儿,这就是分模块设计。

分模块设计就是将项目按照功能/结构拆分成若干个子模块,方便项目的管理维护、拓展,也方便模块键的相互调用、资源共享。

1.2 实践

1.2.1 分析

在练习tlias案例时,我们除了开发部门管理、员工管理、登录认证等相关业务功能外,也定义了一些实体类,也即pojo包下存放的一些类,像分页结果的封装类PageBean、统一响应结果Result,我们还定义了一些通用的工具类,像Jwts、阿里云oss操作的工具类等等。

如果在当前公司的其他项目组当中,也想使用我们所封装的这些公共的组件,该怎么办?

  • 方案一:直接依赖我们当前项目 tlias-web-management ,但是存在两大缺点:

    • 这个项目当中包含所有的业务功能代码,而想共享的资源,仅仅是pojo下的实体类,以及 utils 下的工具类。如果全部都依赖进来,项目在启动时将会把所有的类都加载进来,会影响性能

    • 如果直接把这个项目都依赖进来了,那也就意味着我们所有的业务代码都对外公开了,这个是非常不安全的。

方案二:分模块设计

  • 将pojo包下的实体类,抽取到一个maven模块中 tlias-pojo

  • 将utils包下的工具类,抽取到一个maven模块中 tlias-utils

  • 其他的业务代码,放在tlias-web-management这个模块中,在该模块中需要用到实体类pojo、工具类utils,直接引入对应的依赖即可。

注意:分模块开发需要先针对模块功能进行设计,再进行编码。不会先将工程开发完毕,然后进行拆分。

PS:当前是为了演示分模块开发,所以是基于我们前面开发的案例项目进行拆分的,实际中都是分模块设计,然后再开发的。

1.2.2 实现
  1. 创建maven模块 tlias-pojo,存放实体类,A. 创建一个正常的Maven模块,模块名tlias-pojo

B. 然后在tlias-pojo中创建一个包 com.itheima.pojo (和原来案例项目中的pojo包名一致)

C. 将原来案例项目 tlias-web-management 中的pojo包下的实体类,复制到tlias-pojo模块中

D. 在 tlias-pojo 模块的pom.xml文件中引入依赖

XML 复制代码
<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
    </dependency>
</dependencies>

E. 删除原有案例项目tlias-web-management的pojo包【直接删除不要犹豫,我们已经将该模块拆分出去了】,然后在pom.xml中引入 tlias-pojo的依赖

XML 复制代码
<dependency>
    <groupId>com.itheima</groupId>
    <artifactId>tlias-pojo</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

2. 创建Maven模块 tlias-utils,存放相关工具类

A. 创建一个正常的Maven模块,模块名tlias-utils

B. 然后在 tlias-utils 中创建一个包 com.itheima.utils (和原来案例项目中的utils包名一致)

C. 将原来案例项目 tlias-web-management 中的utils包下的实体类,复制到tlias-utils模块中

D. 在 tlias-utils 模块的pom.xml文件中引入依赖

XML 复制代码
<dependencies>
    <!--JWT令牌-->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>

    <!--阿里云OSS-->
    <dependency>
        <groupId>com.aliyun.oss</groupId>
        <artifactId>aliyun-sdk-oss</artifactId>
        <version>3.15.1</version>
    </dependency>
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.3.1</version>
    </dependency>
    <dependency>
        <groupId>javax.activation</groupId>
        <artifactId>activation</artifactId>
        <version>1.1.1</version>
    </dependency>
    <!-- no more than 2.3.3-->
    <dependency>
        <groupId>org.glassfish.jaxb</groupId>
        <artifactId>jaxb-runtime</artifactId>
        <version>2.3.3</version>
    </dependency>

    <!--WEB开发-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.7.5</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
    </dependency>
</dependencies>

E. 删除原有案例项目tlias-web-management的utils包【直接删除不要犹豫,我们已经将该模块拆分出去了】,然后在pom.xml中引入 tlias-utils的依赖

XML 复制代码
<dependency>
    <groupId>com.itheima</groupId>
    <artifactId>tlias-utils</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

到此呢,就已经完成了模块的拆分,拆分出了 tlias-pojo、tlias-utils、tlias-web-management ,如果其他项目中需要用到 pojo,或者 utils工具类,就可以直接引入依赖。

1.3 总结

1). 什么是分模块设计:将项目按照功能拆分成若干个子模块
2). 为什么要分模块设计:方便项目的管理维护、扩展,也方便模块间的相互调用,资源共享
3). 注意事项:分模块设计需要先针对模块功能进行设计,再进行编码。不会先将工程开发完毕,然后进行拆分

2. 继承与聚合

在案例项目分模块开发之后啊,我们会看到tlias-pojo、tlias-utils、tlias-web-management中都引入了一个依赖 lombok 的依赖。我们在三个模块中分别配置了一次。

如果是做一个大型的项目,这三个模块当中重复的依赖可能会很多很多。如果每一个 Maven 模块里面,我们都来单独的配置一次,功能虽然能实现,但是配置是比较繁琐的。

而接下来Maven 的继承用来解决这问题的。

2.1 继承

我们可以再创建一个父工程 tlias-parent ,然后让上述的三个模块 tlias-pojo、tlias-utils、tlias-web-management 都来继承这个父工程 。 然后再将各个模块中都共有的依赖,都提取到父工程 tlias-parent中进行配置,只要子工程继承了父工程,依赖它也会继承下来,这样就无需在各个子工程中进行配置了。

  • 概念:继承描述的是两个工程间的关系,与java中的继承相似,子工程可以继承父工程中的配置信息,常见于依赖关系的继承。

  • 作用:简化依赖配置、统一管理依赖

  • 实现:

XML 复制代码
<parent>
    <groupId>...</groupId>
    <artifactId>...</artifactId>
    <version>...</version>
    <relativePath>....</relativePath>
</parent>

2.1.1 继承关系

2.1.1.1 思路分析

当前的项目 tlias-web-management,还稍微有一点特殊,因为是一个springboot项目,而所有的springboot项目都有一个统一的父工程,就是spring-boot-starter-parent。 与java语言类似,Maven不支持多继承,一个maven项目只能继承一个父工程,如果继承了spring-boot-starter-parent,就没法继承我们自己定义的父工程 tlias-parent了。

那怎么来解决这个问题呢?

大家可以想一下,Java虽然不支持多继承,但是可以支持多重继承,比如:A 继承 B, B 继承C。 那在Maven中也是支持多重继承的,所以呢,我们就可以让 我们自己创建的三个模块,都继承tlias-parent,而tlias-parent 再继承 spring-boot-starter-parent,就可以了。 具体结构如下:

2.1.1.2 实现

1). 创建maven模块 tlias-parent ,该工程为父工程,设置打包方式pom(默认jar)。

工程结构如下:

父工程tlias-parent的pom.xml文件配置如下:

Maven打包方式:

  • jar:普通模块打包,springboot项目基本都是jar包(内嵌tomcat运行)

  • war:普通web程序打包,需要部署在外部的tomcat服务器中运行

  • pom:父工程或聚合工程,该模块不写代码,仅进行依赖管理

2). 在子工程的pom.xml文件中,配置继承关系。

XML 复制代码
<parent>
    <groupId>com.itheima</groupId>
    <artifactId>tlias-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <relativePath>../tlias-parent/pom.xml</relativePath>
</parent>

<artifactId>tlias-utils</artifactId>
<version>1.0-SNAPSHOT</version>

这里是以 tlias-utils 为例,指定了其父工程。其他的模块,都是相同的配置方式。

注意:

  • 在子工程中,配置了继承关系之后,坐标中的groupId是可以省略的,因为会自动继承父工程的 。

  • relativePath指定父工程的pom文件的相对位置(如果不指定,将从本地仓库/远程仓库查找该工程)。

    • ../ 代表的上一级目录

3). 在父工程中配置各个工程共有的依赖(子工程会自动继承父工程的依赖)。

XML 复制代码
<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
    </dependency>
</dependencies>

此时,我们已经将各个子工程中共有的依赖(lombok),都定义在了父工程中,子工程中的这一项依赖,就可以直接删除了。删除之后,我们会看到父工程中配置的依赖 lombok,子工程直接继承下来了。

工程结构说明:

我们当前的项目结构为:

因为我们是项目开发完毕之后,给大家基于现有项目拆分的各个模块,tlias-web-management已经存在了,然后再创建各个模块与父工程,所以父工程与模块之间是平级的。

而实际项目中,可能还会见到下面的工程结构:

而在真实的企业开发中,都是先设计好模块之后,再开始创建模块,开发项目。一般都会先创建父工程 tlias-parent,然后将创建的各个子模块,都放在父工程parent下面。 这样层级结构会更加清晰一些。

2.1.2 版本锁定
2.1.2.1 场景

如果项目中各个模块中都公共的这部分依赖,我们可以直接定义在父工程中,从而简化子工程的配置。 然而在项目开发中,还有一部分依赖,并不是各个模块都共有的,可能只是其中的一小部分模块中使用到了这个依赖。

比如:在tlias-web-management、tlias-web-system、tlias-web-report这三个子工程中,都使用到了jwt的依赖。 但是 tlias-pojo、tlias-utils中并不需要这个依赖,那此时,这个依赖,我们不会直接配置在父工程 tlias-parent中,而是哪个模块需要,就在哪个模块中配置。

而由于是一个项目中的多个模块,那多个模块中,我们要使用的同一个依赖的版本要一致,这样便于项目依赖的统一管理。比如:这个jwt依赖,我们都使用的是 0.9.1 这个版本。

那假如说,我们项目要升级,要使用到jwt最新版本 0.9.2 中的一个新功能,那此时需要将依赖的版本升级到0.9.2,那此时该怎么做呢 ?

第一步:去找当前项目中所有的模块的pom.xml配置文件,看哪些模块用到了jwt的依赖。

第二步:找到这个依赖之后,将其版本version,更换为 0.9.2。

问题:如果项目拆分的模块比较多,每一次更换版本,我们都得找到这个项目中的每一个模块,一个一个的更改。 很容易就会出现,遗漏掉一个模块,忘记更换版本的情况。

那我们又该如何来解决这个问题,如何来统一管理各个依赖的版本呢?

答:Maven的版本锁定功能。

2.1.2.2介绍

在maven中,可以在父工程的pom文件中通过 <dependencyManagement> 来统一管理依赖版本。

父工程:

XML 复制代码
<!--统一管理依赖版本-->
<dependencyManagement>
    <dependencies>
        <!--JWT令牌-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
    </dependencies>
</dependencyManagement>

子工程:

XML 复制代码
<dependencies>
    <!--JWT令牌-->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
    </dependency>
</dependencies>

注意:

  • 在父工程中所配置的 <dependencyManagement> 只能统一管理依赖版本,并不会将这个依赖直接引入进来。 这点和 <dependencies> 是不同的。

  • 子工程要使用这个依赖,还是需要引入的,只是此时就无需指定 <version> 版本号了,父工程统一管理。变更依赖版本,只需在父工程中统一变更。

2.1.2.3 实现

接下来,可以将tlias-utils模块中单独配置的依赖,将其版本统一交给 tlias-parent 进行统一管理。

具体步骤如下:

1). tlias-parent 中的配置

XML 复制代码
<!--统一管理依赖版本-->
<dependencyManagement>
    <dependencies>
        <!--JWT令牌-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

        <!--阿里云OSS-->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>3.15.1</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.1</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!-- no more than 2.3.3-->
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>2.3.3</version>
        </dependency>
    </dependencies>
</dependencyManagement>

2). tlias-utils中的pom.xml配置

如果依赖的版本已经在父工程进行了统一管理,所以在子工程中就无需再配置依赖的版本了。

XML 复制代码
<dependencies>
    <!--JWT令牌-->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
    </dependency>

    <!--阿里云OSS-->
    <dependency>
        <groupId>com.aliyun.oss</groupId>
        <artifactId>aliyun-sdk-oss</artifactId>
    </dependency>
    <dependency>
        <groupId>javax.xml.bind</groupId>
        <artifactId>jaxb-api</artifactId>
    </dependency>
    <dependency>
        <groupId>javax.activation</groupId>
        <artifactId>activation</artifactId>
    </dependency>
    <!-- no more than 2.3.3-->
    <dependency>
        <groupId>org.glassfish.jaxb</groupId>
        <artifactId>jaxb-runtime</artifactId>
    </dependency>

    <!--WEB开发-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

我们之所以,在springboot项目中很多时候,引入依赖坐标,都不需要指定依赖的版本 <version> ,是因为在父工程 spring-boot-starter-parent中已经通过 <dependencyManagement>对依赖的版本进行了统一的管理维护。

2.1.2.4 属性配置

们也可以通过自定义属性及属性引用的形式,在父工程中将依赖的版本号进行集中管理维护。 具体语法为:

1). 自定义属性

XML 复制代码
<properties>
	<lombok.version>1.18.24</lombok.version>
</properties>

2). 引用属性

XML 复制代码
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>${lombok.version}</version>
</dependency>

接下来,我们就可以在父工程中,将所有的版本号,都集中管理维护起来。

XML 复制代码
<properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>

    <lombok.version>1.18.24</lombok.version>
    <jjwt.version>0.9.1</jjwt.version>
    <aliyun.oss.version>3.15.1</aliyun.oss.version>
    <jaxb.version>2.3.1</jaxb.version>
    <activation.version>1.1.1</activation.version>
    <jaxb.runtime.version>2.3.3</jaxb.runtime.version>
</properties>


<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
    </dependency>
</dependencies>

<!--统一管理依赖版本-->
<dependencyManagement>
    <dependencies>
        <!--JWT令牌-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>${jjwt.version}</version>
        </dependency>

        <!--阿里云OSS-->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>${aliyun.oss.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>${jaxb.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>${activation.version}</version>
        </dependency>
        <!-- no more than 2.3.3-->
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>${jaxb.runtime.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

版本集中管理之后,我们要想修改依赖的版本,就只需要在父工程中自定义属性的位置,修改对应的属性值即可。

面试题:<dependencyManagement><dependencies> 的区别是什么?

  • <dependencies> 是直接依赖,在父工程配置了依赖,子工程会直接继承下来。

  • <dependencyManagement> 是统一管理依赖版本,不会直接依赖,还需要在子工程中引入所需依赖(无需指定版本)

2.2 聚合

分模块设计与开发之后啊,我们的项目被拆分为多个模块,而模块之间的关系,可能错综复杂。 那就比如我们当前的案例项目,结构如下(相对还是比较简单的):

此时,tlias-web-management 模块的父工程是 tlias-parent,该模块又依赖了tlias-pojo、tlias-utils模块。 那此时,我们要想将 tlias-web-management 模块打包,是比较繁琐的。因为在进行项目打包时,maven会从本地仓库中来查找tlias-parent父工程,以及它所依赖的模块tlias-pojo、tlias-utils,而本地仓库目前是没有这几个依赖的。

所以,我们再打包tlias-web-management 模块前,需要将 tlias-parent、tlias-pojo、tlias-utils分别执行install生命周期安装到maven的本地仓库,然后再针对于 tlias-web-management 模块执行package进行打包操作。

如果开发一个大型项目,拆分的模块很多,模块之间的依赖关系错综复杂,那此时要进行项目的打包、安装操作,是非常繁琐的。 而我们接下来,要讲解的maven的聚合就是来解决这个问题的,通过maven的聚合就可以轻松实现项目的一键构建(清理、编译、测试、打包、安装等)。

2.2.1 介绍
  • **聚合:**将多个模块组织成一个整体,同时进行项目的构建。

  • **聚合工程:**一个不具有业务功能的"空"工程(有且仅有一个pom文件) 【PS:一般来说,继承关系中的父工程与聚合关系中的聚合工程是同一个】

  • **作用:**快速构建项目(无需根据依赖关系手动构建,直接在聚合工程上构建即可)

2.2.2 实现

在maven中,我们可以在聚合工程中通过 <moudules> 设置当前聚合工程所包含的子模块的名称。我们可以在 tlias-parent中,添加如下配置,来指定当前聚合工程,需要聚合的模块:

XML 复制代码
<!--聚合其他模块-->
<modules>
    <module>../tlias-pojo</module>
    <module>../tlias-utils</module>
    <module>../tlias-web-management</module>
</modules>

那此时,我们要进行编译、打包、安装操作,就无需在每一个模块上操作了。只需要在聚合工程上,统一进行操作就可以了。

**测试:**执行在聚合工程 tlias-parent 中执行 package 打包指令

那 tlias-parent 中所聚合的其他模块全部都会执行 package 指令,这就是通过聚合实现项目的一键构建(一键清理clean、一键编译compile、一键测试test、一键打包package、一键安装install等)。

2.3 继承与聚合对比

  • 作用

    • 聚合用于快速构建项目

    • 继承用于简化依赖配置、统一管理依赖

  • 相同点:

    • 聚合与继承的pom.xml文件打包方式均为pom,通常将两种关系制作到同一个pom文件中

    • 聚合与继承均属于设计型模块,并无实际的模块内容

  • 不同点:

    • 聚合是在聚合工程中配置关系,聚合可以感知到参与聚合的模块有哪些

    • 继承是在子模块中配置关系,父模块无法感知哪些子模块继承了自己

相关推荐
Swift社区3 小时前
在 Swift 中实现字符串分割问题:以字典中的单词构造句子
开发语言·ios·swift
没头脑的ht3 小时前
Swift内存访问冲突
开发语言·ios·swift
没头脑的ht3 小时前
Swift闭包的本质
开发语言·ios·swift
wjs20243 小时前
Swift 数组
开发语言
吾日三省吾码4 小时前
JVM 性能调优
java
stm 学习ing4 小时前
FPGA 第十讲 避免latch的产生
c语言·开发语言·单片机·嵌入式硬件·fpga开发·fpga
湫ccc5 小时前
《Python基础》之字符串格式化输出
开发语言·python
弗拉唐5 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi776 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器