百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

learnMaven系列(一)如何使用Maven构建多模块的项目

ztj100 2025-02-20 18:55 27 浏览 0 评论


Maven目前算是我们后端开发项目管理必不可少的工具了(现在gradle用得也比较多),虽然对开发而言,大部分时候项目是已经搭建好的,个人可能只需要做些clean、package、install等常用操作。但是,如果是作为项目的搭建者,就需要关注下项目构建方面的细节了,毕竟一个好的框架结构,是基石,后续能够更好维护,其他参与你项目的伙伴用起来也比较顺手便捷。当然,另一方面多了解一下maven方面的知识,也有助于自己去更好地参与到他人的项目中,能够更快地让项目“跑”起来,依稀还记得曾经自己新接手项目时,拉到本地一片爆红,被maven支配的恐惧。

现在也是结合自己的一些经历和体会,对遇到过或者觉得比较重要的点进行简要整理和记录吧。

另外,我搭建了简单的示例demo,可以参考这样的模式搭建多模块项目,代码已上传仓库:
https://gitee.com/panda00hi/learnMaven.git

介绍

目前企业级项目因为业务复杂,并且为了方便扩展和维护,同时提高功能代码的复用性,往往会根据功能进行一些模块的拆分,这其实也就带来了一些项目管理上的更高的要求。比如,经常会遇到的情况,对公共组件,如,log4j、logback、sl4j等,如果不做约束,可能会导致每个项目都有依赖类似的组件;而对于模块之间,也可能有依赖,这时又会误引入其他模块引入的组件,总之因为多模块的方式,是需要进行某种规范性约束的,否则随着项目日益复杂,单纯搞依赖配置都要费好大劲。

针对这样的情况,官方也有解决方案,就是pom父子依赖机制,这一点我们应该都有用到,只是可能没有太关注,或者用得不够彻底。

有关于pom的详细介绍和使用案例:https://maven.apache.org/guides/introduction/introduction-to-the-pom.html

Project Inheritance vs Project Aggregation

If you have several Maven projects, and they all have similar configurations, you can refactor your projects by pulling out those similar configurations and making a parent project. Thus, all you have to do is to let your Maven projects inherit that parent project, and those configurations would then be applied to all of them.

And if you have a group of projects that are built or processed together, you can create a parent project and have that parent project declare those projects as its modules. By doing so, you'd only have to build the parent and the rest will follow.

But of course, you can have both Project Inheritance and Project Aggregation. Meaning, you can have your modules specify a parent project, and at the same time, have that parent project specify those Maven projects as its modules. You'd just have to apply all three rules:

Specify in every child POM who their parent POM is.

Change the parent POMs packaging to the value "pom" .

Specify in the parent POM the directories of its modules (children POMs)

创建示例项目

此处使用新建普通maven项目的方式,完成后的项目最为精简,可根据需要自行添加spring boot的相关依赖和启动类。

当然,也可以直接利用spring boot initializer新建项目和模块时,直接生成更丰富的项目模版。

通过继承方式进行管理,只需要应用这三项规则:

  • 在每个子POM中指定其父POM是谁。
  • 将父POM打包更改为值“pom”。
  • 在父POM中指定其模块的目录(子POM)

创建父项目

  1. 新建普通maven项目,作为父项目
  2. pom文件中添加打包方式为

pom

新建完成后

添加打包类型为pom

com.panda00hi.demo
learnMaven
1.0-SNAPSHOT
pom

创建子模块

如,有以下子模块

  • common:一些公共使用的部分,如utils、config、model、mapper等
  • service:抽象的业务服务接口和实现,通过调用dao,各个业务功能,或提供远程调用的方法等
  • web:与前端交互,开放的api、静态资源等
  • task:定时任务或异步相关等一些操作,一般是从web中分离出来,为了方便对一些高耗时的操作进行管理

创建时idea已经可以允许选中基于某模块作为parent创建,创建完成后,子模块自动引入parent,而parent的pom文件中,也会自动添加子模块的标签。

子模块pom文件已引入父模块的parent标签

注意:

1、子模块自己的groupId、version标签无需声明,将通过父模块约束。

2、默认是使用上一级,若需要自己指定,可使用 ../pom.xml


    learnMaven
    com.panda00hi.demo
    1.0-SNAPSHOT

4.0.0

common

父模块pom文件已添加子模块的modules标签


    common
    service
    web
    task

完成后,maven工具栏可看到父模块的root标识


使用spring boot启动项目

1、在父模块,添加spring boot starter parent的parent声明

 

    org.springframework.boot
    spring-boot-starter-parent
    2.6.6
     

2、在web和task模块,由于是web项目,引入web依赖

无需指定版本号,因为父模块依赖的springboot parent已经指定了


    
        org.springframework.boot
        spring-boot-starter-web
    

可以看到,springboot的依赖已经导入了

3、新建启动类和测试controller

启动类

package com.panda00hi.web;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* @author panda00hi
* @date 2022/4/14
*/
// 配置扫描包路径,默认当前模块根目录下及子目录
@SpringBootApplication(scanBasePackages = {"com.panda00hi.web"})
public class WebApplication {
    public static void main(String[] args) {
        SpringApplication.run(WebApplication.class, args);
    }
}

controller

package com.panda00hi.web.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author panda00hi
 * @date 2022/4/14
 */
@RestController
public class TestController {
    
    @GetMapping("/test01")
    public String controller01(Integer id) {
        System.out.println(id);
        return "test01 receive id is :" + id;
    }
}

4、启动项目,验证

启动项目,默认8080端口。浏览器请求localhost:8080/test01?id=1,成功返回。

子模块之间的依赖

如实际工作中,common模块,一般会被其他多个模块依赖,service模块,会被web和task模块所依赖。注意:若service依赖了common,那么web也将获得common依赖,无需重复添加。

操作步骤:

子模块的dependencies标签下,添加需要依赖的其他子模块即可。

注意,添加时GAV三要素中,groupId和version,由于各个子模块均有依赖共同的父模块,且继承了是用来父模块的groupId和version信息,所以,此处依赖其他子模块时,groupId和version使用引用方式即可,如${project.parent.groupId}、${project.parent.version}


  
  
    ${project.parent.groupId}
    service
    ${project.parent.version}
  
  
  
    org.springframework.boot
    spring-boot-starter-web
    
  

父模块进行版本的管理dependencyManagement

为什么springboot项目引用某些依赖时,无需指定版本?

对于添加依赖是需要具备GAV三要素的,但是为什么在springboot项目的pom引入一些依赖的时候不需要指定版本呢?如,spring-boot-starter-web,引用时,并没有声明version信息,这是为什么呢?

原因:实际上,也是通过父模块进行管理的。

依次找到上一级父目录

spring-boot-starter-parent 》spring-boot-dependencies 在dependencyManagement标签下即可看到各个已经声明的依赖,很多都是常见依赖。

实践

采用类似的方式,我们在自己的父模块中,也统一进行依赖的版本管理。

父模块pom



    
        
            ${project.groupId}
            common
            ${project.version}
        
        
            ${project.groupId}
            service
            ${project.version}
        
    

子模块pom,如web模块


    
    
        ${project.parent.groupId}
        service
    

    
        org.springframework.boot
        spring-boot-starter-web
        
    

项目构建

1、使用maven工具,进行mvn package打包操作

注意:若使用父模块进行构建,那么它下面的所有子模块也会构建。

均已构建成功。

2、构建顺序

对于多个模块的项目,尤其是第一次导入新项目,大概率是一片爆红,而且打包命令也是各种执行报错,弄不好心态还崩了,个人也是深有体会。

  • 一般情况会按照父模块的modules标签声明的顺序,进行构建
  • 特殊情况:子模块有先后依赖时,先尝试install被依赖的模块。如,A依赖B和C,那么需要先把B和C打包后,再进行A的打包操作;再就是打包前选中如下图标,跳过单元测试。

3、指定构建的模块

如果只想构建某一模块以及它所依赖的其他模块,可以使用命令操作。如,只打包web模块(其中依赖了service,service依赖了common)

mvn -DskipTests==true package -pk web -am


idea的maven工具执行

修改package的运行配置

添加参数package -pl web -am -f pom.xml。注意:-am参数表示将所依赖的模块也一同打包

保存后,通过启动器运行

构建完成,可以看到打包的操作只有web模块和它所依赖的模块

相关推荐

sharding-jdbc实现`分库分表`与`读写分离`

一、前言本文将基于以下环境整合...

三分钟了解mysql中主键、外键、非空、唯一、默认约束是什么

在数据库中,数据表是数据库中最重要、最基本的操作对象,是数据存储的基本单位。数据表被定义为列的集合,数据在表中是按照行和列的格式来存储的。每一行代表一条唯一的记录,每一列代表记录中的一个域。...

MySQL8行级锁_mysql如何加行级锁

MySQL8行级锁版本:8.0.34基本概念...

mysql使用小技巧_mysql使用入门

1、MySQL中有许多很实用的函数,好好利用它们可以省去很多时间:group_concat()将取到的值用逗号连接,可以这么用:selectgroup_concat(distinctid)fr...

MySQL/MariaDB中如何支持全部的Unicode?

永远不要在MySQL中使用utf8,并且始终使用utf8mb4。utf8mb4介绍MySQL/MariaDB中,utf8字符集并不是对Unicode的真正实现,即不是真正的UTF-8编码,因...

聊聊 MySQL Server 可执行注释,你懂了吗?

前言MySQLServer当前支持如下3种注释风格:...

MySQL系列-源码编译安装(v5.7.34)

一、系统环境要求...

MySQL的锁就锁住我啦!与腾讯大佬的技术交谈,是我小看它了

对酒当歌,人生几何!朝朝暮暮,唯有己脱。苦苦寻觅找工作之间,殊不知今日之事乃我心之痛,难道是我不配拥有工作嘛。自面试后他所谓的等待都过去一段时日,可惜在下京东上的小金库都要见低啦。每每想到不由心中一...

MySQL字符问题_mysql中字符串的位置

中文写入乱码问题:我输入的中文编码是urf8的,建的库是urf8的,但是插入mysql总是乱码,一堆"???????????????????????"我用的是ibatis,终于找到原因了,我是这么解决...

深圳尚学堂:mysql基本sql语句大全(三)

数据开发-经典1.按姓氏笔画排序:Select*FromTableNameOrderByCustomerNameCollateChinese_PRC_Stroke_ci_as//从少...

MySQL进行行级锁的?一会next-key锁,一会间隙锁,一会记录锁?

大家好,是不是很多人都对MySQL加行级锁的规则搞的迷迷糊糊,一会是next-key锁,一会是间隙锁,一会又是记录锁。坦白说,确实还挺复杂的,但是好在我找点了点规律,也知道如何如何用命令分析加...

一文讲清怎么利用Python Django实现Excel数据表的导入导出功能

摘要:Python作为一门简单易学且功能强大的编程语言,广受程序员、数据分析师和AI工程师的青睐。本文系统讲解了如何使用Python的Django框架结合openpyxl库实现Excel...

用DataX实现两个MySQL实例间的数据同步

DataXDataX使用Java实现。如果可以实现数据库实例之间准实时的...

MySQL数据库知识_mysql数据库基础知识

MySQL是一种关系型数据库管理系统;那废话不多说,直接上自己以前学习整理文档:查看数据库命令:(1).查看存储过程状态:showprocedurestatus;(2).显示系统变量:show...

如何为MySQL中的JSON字段设置索引

背景MySQL在2015年中发布的5.7.8版本中首次引入了JSON数据类型。自此,它成了一种逃离严格列定义的方式,可以存储各种形状和大小的JSON文档,例如审计日志、配置信息、第三方数据包、用户自定...

取消回复欢迎 发表评论: