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

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

ztj100 2025-02-20 18:55 22 浏览 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模块和它所依赖的模块

相关推荐

Docker安全开放远程访问连接权限(docker 远程授权访问)

1、Docker完全开放远程访问Docker服务完全开放对外访问权限操作如下:#开启端口命令(--permanent永久生效,没有此参数重启后失效)firewall-cmd--zone=pu...

SpringCloud系列——4OpenFeign简介及应用

学习目标什么是OpenFeign以及它的作用RPC到底怎么理解OpenFeign的应用第1章OpenFeign简介在前面的内容中,我们分析了基于RestTemplate实现http远程通信的方法。并...

Spring Boot集成qwen:0.5b实现对话功能

1.什么是qwen:0.5b?模型介绍:Qwen1.5是阿里云推出的一系列大型语言模型。Qwen是阿里云推出的一系列基于Transformer的大型语言模型,在大量数据(包括网页文本、书籍、代码等)...

JDK从8升级到21的问题集(jdk8升级到11)

一、背景与挑战1.升级动因oOracle长期支持策略o现代特性需求:协程、模式匹配、ZGC等o安全性与性能的需求oAI新技术引入的版本要求...

大白话详解Spring Cloud服务降级与熔断

1.Hystrix断路器概述1.1分布式系统面临的问题复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。这就造成有可能会发生...

面试突击43:lock、tryLock、lockInterruptibly有什么区别?

在Lock接口中,获取锁的方法有4个:lock()、tryLock()、tryLock(long,TimeUnit)、lockInterruptibly(),为什么需要这么多方法?这些方法都有...

了解网络编程 TCP/IP 协议与UDP 协议

因为iP地址比较难记忆,很多情况下可以使用域名代替iP地址。1.TCP/IP协议与UDP协议通过IP地址与端口号确定计算机在网络中的位置后,接下来考虑通讯的问题:因为不同计算机的软硬件平台...

Semaphore与Exchanger的区别(semaphore和signal)

Semaphore和Exchanger是Java并发编程中两个常用的同步工具类,它们都可以用于协调多个线程之间的执行顺序和状态,但它们的作用和使用方式有所不同:Semaphore类表示一个...

Java教程:什么是分布式任务调度?怎样实现任务调度?

通常任务调度的程序是集成在应用中的,比如:优惠卷服务中包括了定时发放优惠卷的的调度程序,结算服务中包括了定期生成报表的任务调度程序...

java多线程—Runnable、Thread、Callable区别

多线程编程优点:进程之间不能共享内存,但线程之间共享内存非常容易。系统创建线程所分配的资源相对创建进程而言,代价非常小。Java中实现多线程有3种方法:继承Thread类实现Runnable...

工厂模式详解(工厂模式是啥意思)

工厂模式详解简单工厂简单工厂模式(SimpleFactoryPattern)是指由一个工厂对象决定创建出哪一种产品类的实例。简单工厂适用于工厂类负责创建的对象较少的场景,且客户端只需要传入工厂类的...

我们程序员眼中的母亲节(你眼中的程序员是什么样子的?程序员的薪酬如何?)

导语:对于我们成人来说,尤其是漂泊在外的程序员,陪伴父母的时间太少了。每逢佳节倍思亲,我们流浪外在的游子应该深有感触。母亲,是世界上最伟大的人,她承载着对我们的爱,更是负担和压力。我们作为子女,只会嫌...

死锁的 4 种排查工具(死锁检测方法要解决两个问题)

死锁(DeadLock)指的是两个或两个以上的运算单元(进程、线程或协程),都在等待对方停止执行,以取得系统资源,但是没有一方提前退出,就称为死锁。死锁示例接下来,我们先来演示一下Java中最简...

1. 工厂模式详解(工厂模式示例)

我们的项目代码也是由简而繁一步一步迭代而来的,但对于调用者来说却是越来越简单化。简单工厂模式简单工厂模式(SimpleFactoryPattern)是指由一个工厂对象决定创建出哪一种产品类的实例。...

Jmeter(二十):jmeter对图片验证码的处理

jmeter对图片验证码的处理在web端的登录接口经常会有图片验证码的输入,而且每次登录时图片验证码都是随机的;当通过jmeter做接口登录的时候要对图片验证码进行识别出图片中的字段,然后再登录接口中...

取消回复欢迎 发表评论: