Gradle 高级篇之构建多模块项目的方法

基于 IDEA 创建 Gradle 多模块项目。

  • IDEA 版本:2023.3.4
  • SpringBoot 版本:3.5.3
  • Gradle 版本:gradle-8.12.1
  • JDK 版本:jdk-21.0.7

1. 创建父项目

1.1. 新建项目

文件 -> 新建 -> 项目

输入项目名称:gradle-multi-module-demo。

点击下一步,创建父项目,这里什么都没选。

点击创建,就建好了一个 SpringBoot 项目。

1.2. 修改项目

我们是要创建多模块项目,所以作为父项目,有些内容是可以删除的。下图中选中的内容可以删除。

项目目录下仅保留了:.gradle \ .idea \ .gitattributes \ .gitignore \ setting.gradle

删除后的项目结构:

2. 创建子模块

2.1. 创建模块

选中父项目 -> 新建 -> 模块

输入模块名称:demo-core。(这里模块名称最好是一个单词,多个单词的话注意修改软件包名称,多个单词间加点号)

点击下一步,作为示例项目,我仅选择了 lombok 和 spring web。

点击创建,就建好了一个 SpringBoot 模块。

2.2. 修改模块

创建好子模块后,需要在右上角的 Gradle 下删除最外层的子模块目录。

然后将子模块包含到父项目下:

刷新后,在右上角的 Gradle 下的父项目下能看到这个子模块了。

删除子模块的settings.gradle文件。

使用同样的方法再创建一个子模块demo-mgmt

因为我选的依赖一样,所以这两个子模块的 build.gradle 内容一样:

gradle 复制代码
plugins {
    id 'java'
    id 'org.springframework.boot' version '3.5.3'
    id 'io.spring.dependency-management' version '1.1.7'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
    useJUnitPlatform()
}

3. 提取公共配置到父项目

现在发现两个子模块的 build.gradle 内容基本一样,所以可以把这些相同的配置提到父项目的 build.gradle 文件中,前面把这个文件删除了,这里可以选一个子模块的该文件复制出来。

修改方法:

  1. plugins下添加一个subprojects,将下面内容都复制到里面。
  2. 添加一个apply,将plugins中的插件进行应用。
  3. 修改dependencies中的内容,仅保留子模块们共用的依赖。

修改完的 build.gradle 内容:

gradle 复制代码
plugins {
    id 'java'
    id 'org.springframework.boot' version '3.5.3'
    id 'io.spring.dependency-management' version '1.1.7'
}

subprojects {
    apply {
        plugin('java')
        plugin('org.springframework.boot')
        plugin('io.spring.dependency-management')
    }

    group = 'com.example'
    version = '0.0.1-SNAPSHOT'

    java {
        toolchain {
            languageVersion = JavaLanguageVersion.of(21)
        }
    }

    configurations {
        compileOnly {
            extendsFrom annotationProcessor
        }
    }

    repositories {
        mavenCentral()
    }

    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-web'
        compileOnly 'org.projectlombok:lombok'
        annotationProcessor 'org.projectlombok:lombok'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
        testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    }

    tasks.named('test') {
        useJUnitPlatform()
    }
}

子模块的 build.gradle 文件内容修改,排除共用的依赖,仅保留单独的依赖(分别加了两个不同的依赖)。

两个子模块的 build.gradle 文件内容如下:

gradle 复制代码
dependencies {
    implementation("com.alibaba.fastjson2:fastjson2:2.0.57")
}
gradle 复制代码
dependencies {
    implementation 'cn.hutool:hutool-all:5.8.38'
}

4. 创建公共子模块

上面内容是对公共配置抽取,现在需要搭建一个公共代码子模块:

创建完毕后,参考 2.2,同样的修改方式

接下来修改 common 模块的 build.gradle 文件:

gradle 复制代码
plugins {
    id 'java-library'
}

接下来修改父项目的 build.gradle 文件,添加一个判断引入,表示非 common 模块都引入 common 模块:

gradle 复制代码
if (project.name != 'demo-common'){
    dependencies {
        implementation project(':demo-common')
    }
}

修改完的父项目的 build.gradle 文件内容:

gradle 复制代码
plugins {
    id 'java'
    id 'org.springframework.boot' version '3.5.3'
    id 'io.spring.dependency-management' version '1.1.7'
}

subprojects {
    apply {
        plugin('java')
        plugin('org.springframework.boot')
        plugin('io.spring.dependency-management')
    }

    group = 'com.example'
    version = '0.0.1-SNAPSHOT'

    java {
        toolchain {
            languageVersion = JavaLanguageVersion.of(21)
        }
    }

    configurations {
        compileOnly {
            extendsFrom annotationProcessor
        }
    }

    repositories {
        mavenCentral()
    }

    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-web'
        compileOnly 'org.projectlombok:lombok'
        annotationProcessor 'org.projectlombok:lombok'
        testImplementation 'org.springframework.boot:spring-boot-starter-test'
        testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    }

    if (project.name != 'demo-common'){
        dependencies {
            implementation project(':demo-common')
        }
    }

    tasks.named('test') {
        useJUnitPlatform()
    }
}

实际上,可以在需要依赖 common 的模块中直接添加依赖即可,能更灵活一些。

5. 测试公共子模块

首先在 common 模块配置一个全局异常拦截类:

java 复制代码
package com.example.democommon.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ProblemDetail;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * @author wangbo
 * @since 2025/7/13
 */
@Slf4j
@RestControllerAdvice
public class GlobalControllerAdvice {
    @ExceptionHandler
    public ProblemDetail exceptionHandler(Exception e){
        log.info("==============");
        log.atError().setMessage(e::getMessage).setCause(e).log();
        return ProblemDetail.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage());
    }
}

在 mgmt 模块导入该类:

Java 复制代码
package com.example.demomgmt;

import com.example.democommon.config.GlobalControllerAdvice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;

@Import({GlobalControllerAdvice.class})
@SpringBootApplication
public class DemoMgmtApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoMgmtApplication.class, args);
    }

}

在 demo-mgmt 模块中创建一个 Controller 类,写上一个异常方法,启动项目,看异常是否会被拦截。

java 复制代码
package com.example.demomgmt.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author wangbo
 * @since 2025/7/13
 */
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("/a")
    public String test1(){
        log.info("aaaaa");
        return "aaaaa";
    }

    @GetMapping("/b")
    public String test2(){
        log.info("bbbbb");
        int i = 1 / 0;
        return "bbbbb";
    }
}

测试会被拦截,证明 common 模块中的全局异常拦截对 mgmt 模块生效了。

相关推荐
喜欢敲代码的程序员18 分钟前
SpringBoot+Mybatis+MySQL+Vue+ElementUI前后端分离版:日志管理(四)集成Spring Security
spring boot·mysql·spring·vue·mybatis
Resean02231 小时前
SpringMVC 6+源码分析(二)DispatcherServlet实例化流程 1
java·spring boot·spring·servlet·springmvc
hqxstudying6 小时前
SpringBoot相关注解
java·spring boot·后端
江湖中的阿龙10 小时前
SpringBoot:基于 Redis 自定义注解实现后端接口防重复提交校验(幂等操作)
spring boot·redis·后端·幂等操作
孟婆来包棒棒糖~11 小时前
Docker快速入门
运维·spring boot·docker·容器·tomcat
hqxstudying11 小时前
SpringBoot启动项目详解
java·spring boot·后端
一枚小小程序员哈12 小时前
基于springboot/java/VUE的旅游管理系统/旅游网站的设计与实现
spring boot·spring·java-ee·maven·intellij-idea·旅游
程序员葵安13 小时前
【苍穹外卖项目】Day05
spring boot·后端
Code blocks18 小时前
关于“LoggerFactory is not a Logback LoggerContext but Logback is on ......“的解决方案
java·spring boot·后端
飞翔的佩奇19 小时前
基于SpringBoot+MyBatis+MySQL+VUE实现的经方药食两用服务平台管理系统(附源码+数据库+毕业论文+部署教程+配套软件)
数据库·vue.js·spring boot·mysql·毕业设计·mybatis·经方药食两用平台