Spring Boot多模块项目模板:从零搭建团队高效协作开发骨架 📅 2026/8/17 15:12:32 1. 从单体到模块化为什么我们需要一个团队开发模板如果你在一个超过三个人的Spring Boot团队里待过大概率经历过这样的场景张三在UserController里加了个新接口顺手把UserService的某个方法签名改了结果李四负责的订单模块直接编译报错王五的支付模块也跟着遭殃。大家花了一下午时间互相扯皮、合并代码、解决冲突最后发现只是因为一个方法名没沟通好。又或者新来的同事对着一个几百个Java文件的单体工程一脸茫然不知道业务逻辑从哪里开始看也不知道自己改的代码会不会影响到其他八竿子打不着的功能。这些问题根源往往不在于技术而在于项目结构。一个清晰、约束力强的多模块项目结构就是团队开发的“交通规则”和“城市规划图”。它规定了不同功能的代码应该放在哪里模块之间应该如何依赖公共代码如何被复用。今天要聊的就是如何从零开始搭建一个专为团队协作设计的Spring Boot 3多模块项目工程模板。这个模板的目的不是炫技而是实实在在地解决协作效率、代码边界和工程规范的问题让你和你的团队能把精力更多地花在业务逻辑上而不是在项目结构里“捉迷藏”。2. 模板核心思想职责分离与依赖管理在动手敲命令之前我们必须先统一思想。一个好的多模块模板其核心设计原则可以概括为两点清晰的职责分离和严格的依赖管理。这两点是所有后续操作的基石。2.1 模块的“宪法”单一职责与高内聚每个模块都应该像一个独立的“部门”有自己明确的职责和边界。比如user-service模块就只负责用户相关的业务逻辑、数据实体和接口order-service模块就只管订单。它们内部应该是“高内聚”的即模块内的类彼此联系紧密共同完成一个明确的业务目标。这样做的好处是显而易见的代码更容易理解和维护测试范围更明确也更容易进行独立部署或替换如果需要的话。在我们的模板里我们会严格区分不同类型的模块业务模块承载核心业务逻辑如user-module,product-module。它们是独立的Spring Boot应用或业务单元。通用模块存放被多个业务模块复用的代码如common-core工具类、通用异常、常量、common-database数据源配置、MyBatis通用配置。API模块定义服务间通信的接口和DTO数据传输对象如user-api。这常用于微服务架构下的Feign客户端声明但在单体拆分模块时也能强制定义清晰的接口契约。启动模块通常命名为application或直接使用项目根目录名。它本身不写业务代码只负责依赖管理、全局配置和启动应用。它是整个项目的“总开关”和“粘合剂”。2.2 依赖的“法律”向下依赖与循环禁止依赖关系是模块间的“通行证”。我们的原则是依赖必须单向、向下。这意味着高层模块如业务模块可以依赖底层模块如通用模块但反过来绝对不行。通用模块不应该知道任何业务逻辑。同时严格禁止循环依赖。A模块依赖BB又依赖A这种结构在编译期可能通过某些技巧绕过但在运行时和团队心智上是灾难它意味着模块边界已经失效代码耦合到了一起。为了管理好这些依赖我们会重度依赖Maven或Gradle的“依赖管理”功能。在父POM中统一定义所有第三方库的版本所有子模块继承这个版本避免出现同一个项目里Jackson有三个不同版本的混乱情况。同时父POM中只声明dependencyManagement不直接引入依赖具体依赖由各子模块按需声明保持灵活性。3. 手把手搭建从零到一的工程骨架理论说再多不如动手做一遍。我们使用Spring Boot 3.2.x和Maven来演示。假设我们的项目叫做team-project-template。3.1 创建父工程总控POM首先创建一个最外层的目录这就是我们的项目根目录。在这个目录下创建一个pom.xml文件这就是父工程的POM。?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion !-- 项目坐标 -- groupIdcom.example.team/groupId artifactIdteam-project-template/artifactId version1.0.0-SNAPSHOT/version !-- 关键打包方式为pom表示这是一个管理型项目不生成jar/war -- packagingpom/packaging nameteam-project-template/name description团队Spring Boot多模块开发模板/description !-- 模块声明这里列出所有子模块Maven会按顺序构建 -- modules modulecommon-core/module modulecommon-database/module moduleuser-api/module moduleuser-service/module moduleorder-service/module moduleapplication/module /modules !-- 统一属性管理 -- properties java.version17/java.version project.build.sourceEncodingUTF-8/project.build.sourceEncoding maven.compiler.source${java.version}/maven.compiler.source maven.compiler.target${java.version}/maven.compiler.target !-- Spring Boot 版本 -- spring-boot.version3.2.5/spring-boot.version !-- 其他第三方依赖版本 -- lombok.version1.18.30/lombok.version mapstruct.version1.5.5.Final/mapstruct.version mybatis-spring-boot-starter.version3.0.3/mybatis-spring-boot-starter.version mysql-connector-j.version8.3.0/mysql-connector-j.version /properties !-- 核心依赖管理锁定所有子模块使用的依赖版本 -- dependencyManagement dependencies !-- Spring Boot BOM管理所有Spring相关依赖的版本 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-dependencies/artifactId version${spring-boot.version}/version typepom/type scopeimport/scope /dependency !-- 非Spring Boot管理的依赖需要在这里显式声明版本 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId version${lombok.version}/version scopeprovided/scope /dependency dependency groupIdorg.mapstruct/groupId artifactIdmapstruct/artifactId version${mapstruct.version}/version /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version${mybatis-spring-boot-starter.version}/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId version${mysql-connector-j.version}/version /dependency /dependencies /dependencyManagement !-- 所有子模块共享的构建配置 -- build pluginManagement plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId version${spring-boot.version}/version /plugin plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.11.0/version configuration source${java.version}/source target${java.version}/target annotationProcessorPaths path groupIdorg.projectlombok/groupId artifactIdlombok/artifactId version${lombok.version}/version /path !-- MapStruct需要放在lombok之后 -- path groupIdorg.mapstruct/groupId artifactIdmapstruct-processor/artifactId version${mapstruct.version}/version /path /annotationProcessorPaths /configuration /plugin /plugins /pluginManagement /build /project这个父POM是整个项目的基石。它做了几件关键事1) 声明了packagingpom/packaging表明自己是管理者2) 在modules里列出了所有子模块Maven会依据这个列表进行构建3) 在dependencyManagement里统管了所有重要依赖的版本实现了“一处定义处处生效”4) 在build里配置了公共的插件特别是编译器插件中配置了Lombok和MapStruct的注解处理器路径这个顺序很重要Lombok必须在MapStruct之前处理。3.2 创建通用基础模块common-core在项目根目录下创建子目录common-core并在其中创建其pom.xml。?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdcom.example.team/groupId artifactIdteam-project-template/artifactId version1.0.0-SNAPSHOT/version /parent artifactIdcommon-core/artifactId packagingjar/packaging dependencies !-- 常用工具包 -- dependency groupIdorg.apache.commons/groupId artifactIdcommons-lang3/artifactId /dependency dependency groupIdcom.google.guava/groupId artifactIdguava/artifactId /dependency !-- 序列化 -- dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId /dependency !-- 验证 -- dependency groupIdorg.hibernate.validator/groupId artifactIdhibernate-validator/artifactId /dependency !-- 日志 -- dependency groupIdorg.slf4j/groupId artifactIdslf4j-api/artifactId /dependency !-- Lombok -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId scopeprovided/scope /dependency /dependencies /projectcommon-core模块的POM非常简单它继承了父POM然后声明了自己需要的依赖。注意这里的依赖不需要写版本号因为版本已经在父POM的dependencyManagement中定义好了。这个模块里我们会放一些项目全局通用的东西比如Result统一API响应封装类。BusinessException/GlobalExceptionHandler自定义业务异常和全局异常处理器虽然处理器更常放在启动模块但异常定义可以在这里。Constants全局常量定义。DateUtils,StringUtils补充Apache Commons未覆盖的自定义工具类。通用的枚举、注解等。实操心得common-core要保持“纯净”不要引入任何与特定技术栈如Spring MVC, MyBatis强耦合的依赖。它的目标是成为所有其他模块包括非Web模块都可以安全依赖的基础库。如果有些工具类只和Web相关可以考虑再拆一个common-web模块。3.3 创建数据访问通用模块common-database创建子目录common-database及其pom.xml。?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdcom.example.team/groupId artifactIdteam-project-template/artifactId version1.0.0-SNAPSHOT/version /parent artifactIdcommon-database/artifactId packagingjar/packaging dependencies !-- 依赖基础核心模块 -- dependency groupIdcom.example.team/groupId artifactIdcommon-core/artifactId version${project.version}/version /dependency !-- Spring Boot Data JPA 或 MyBatis 核心依赖 -- dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId /dependency !-- 数据库驱动版本由父POM管理 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId /dependency !-- 连接池Spring Boot默认使用HikariCP已通过starter引入 -- !-- 分页插件 -- dependency groupIdcom.github.pagehelper/groupId artifactIdpagehelper-spring-boot-starter/artifactId version2.1.0/version /dependency /dependencies /project这个模块依赖于common-core并引入了数据访问相关的依赖。在这里我们可以放置通用的MyBatisConfig配置类配置驼峰命名映射、插件等。分页参数的统一封装和处理器。数据源的基础配置更详细的配置应在业务模块或启动模块覆盖。通用的BaseEntity、BaseMapper接口等。注意事项数据库驱动、连接池等依赖的版本在父POM中管理。common-database模块提供的是通用配置和能力具体的数据库连接URL、用户名密码等敏感信息不应该写死在这里而应该由使用它的业务模块通过application.yml来配置。3.4 创建API契约模块user-api在微服务或强调接口契约的架构中API模块非常重要。创建user-api目录和pom.xml。?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdcom.example.team/groupId artifactIdteam-project-template/artifactId version1.0.0-SNAPSHOT/version /parent artifactIduser-api/artifactId packagingjar/packaging dependencies dependency groupIdcom.example.team/groupId artifactIdcommon-core/artifactId version${project.version}/version /dependency !-- 如果需要Feign客户端可以引入openfeign但注意scope -- !-- dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-openfeign/artifactId scopeprovided/scope /dependency -- !-- 参数校验注解 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId scopeprovided/scope /dependency /dependencies /projectAPI模块通常非常“轻”它主要包含DTOData Transfer Object用于接口请求和响应的数据对象如UserDTO、CreateUserRequest、UserVO等。这些类通常会有详细的JSR 303校验注解如NotBlank,Email。常量/枚举与用户业务相关的枚举如UserStatusEnum,GenderEnum。Feign客户端接口如果是微服务声明服务调用的接口。注意这里对spring-boot-starter-validation等依赖使用了scopeprovided/scope意味着这个依赖在编译和测试时需要但不会打包进最终的jar。因为API模块通常是被其他服务依赖的验证器等实现应该由调用方或服务提供方提供避免传递依赖冲突。3.5 创建业务服务模块user-service这是承载具体业务逻辑的地方。创建user-service目录和pom.xml。?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdcom.example.team/groupId artifactIdteam-project-template/artifactId version1.0.0-SNAPSHOT/version /parent artifactIduser-service/artifactId packagingjar/packaging dependencies !-- 内部模块依赖 -- dependency groupIdcom.example.team/groupId artifactIdcommon-core/artifactId version${project.version}/version /dependency dependency groupIdcom.example.team/groupId artifactIdcommon-database/artifactId version${project.version}/version /dependency dependency groupIdcom.example.team/groupId artifactIduser-api/artifactId version${project.version}/version /dependency !-- Spring Boot Starters -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency !-- 对象映射 -- dependency groupIdorg.mapstruct/groupId artifactIdmapstruct/artifactId /dependency !-- 其他业务特定依赖 -- /dependencies build plugins !-- 继承父POM中定义的插件配置 -- plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId /plugin /plugins /build /project业务模块的依赖是最丰富的。它依赖了之前定义的所有基础模块和API模块并引入了Spring Boot Web、测试等必要的starter。在这个模块里你会看到标准的MVC分层结构controller,service,mapper/dao,entity。entity是数据库实体mapper是MyBatis接口service是业务逻辑层controller是暴露的HTTP接口。它们通过依赖user-api中的DTO来完成与外部的数据交换通过MapStruct将Entity、DTO、VO进行转换。3.6 创建应用启动模块application最后创建聚合所有模块并负责启动的application模块。创建目录和pom.xml。?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdcom.example.team/groupId artifactIdteam-project-template/artifactId version1.0.0-SNAPSHOT/version /parent artifactIdapplication/artifactId packagingjar/packaging dependencies !-- 依赖所有需要集成的业务模块 -- dependency groupIdcom.example.team/groupId artifactIduser-service/artifactId version${project.version}/version /dependency dependency groupIdcom.example.team/groupId artifactIdorder-service/artifactId version${project.version}/version /dependency !-- 也可以依赖一些通用的配置模块 -- dependency groupIdcom.example.team/groupId artifactIdcommon-database/artifactId version${project.version}/version /dependency /dependencies build plugins !-- Spring Boot Maven Plugin 放在最终可执行的模块 -- plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration !-- 指定主启动类 -- mainClasscom.example.team.application.Application/mainClass /configuration executions execution goals goalrepackage/goal /goals /execution /executions /plugin /plugins /build /project启动模块的pom.xml主要做两件事1) 通过dependencies引入所有需要一起打包运行的业务模块2) 配置spring-boot-maven-plugin并指定主启动类。这个模块的src/main/java目录下通常只有一个主启动类Application.java和全局配置文件application.yml。package com.example.team.application; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication // 使用scanBasePackages显式指定扫描范围避免模块化后的扫描问题 SpringBootApplication(scanBasePackages {com.example.team}) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }application.yml则集中管理整个应用的所有配置包括数据源、Redis、服务器端口等。4. 关键配置详解与团队协作约定骨架搭好了但要让这个模板在团队中顺畅运行还需要一些关键的配置和约定。这些细节往往决定了模板的成败。4.1 资源文件与配置的模块化隔离在单体多模块项目中配置文件的管理是个学问。推荐的做法是全局配置在启动模块模块特有配置在各业务模块。启动模块 (application)存放application.yml定义所有模块共享的配置如服务器端口、激活的Profile、日志基础配置、跨域设置等。还可以通过application-{profile}.yml来管理不同环境dev, test, prod的配置。业务模块 (如user-service)可以在自己的src/main/resources下创建application-user.yml。然后在启动模块的application.yml中通过spring.config.import将其导入。# application.yml (位于启动模块) spring: config: import: - classpath:application-user.yml - classpath:application-order.yml # 其他全局配置...这样用户模块的数据库连接池配置、Redis键前缀等就可以写在user-service模块的application-user.yml里实现了配置的物理隔离和逻辑聚合。4.2 包扫描与组件注册Spring Boot默认会扫描主类所在包及其子包下的组件。在多模块项目中我们的业务模块如user-service的代码包名可能是com.example.team.user而主类在com.example.team.application。默认情况下Spring是扫描不到user包下的Service,Controller的。解决方案是在主启动类上使用SpringBootApplication(scanBasePackages {com.example.team})将扫描范围扩大到整个项目的基础包。更精细的控制可以为特定模块创建配置类并使用ComponentScan。4.3 Maven构建与依赖传递的陷阱多模块项目在构建时最常遇到的就是“找不到符号”的编译错误。这通常是因为模块间依赖关系没处理好。循环依赖这是红线。Maven可能会允许编译但会导致不可预知的行为。必须通过重构代码提取公共部分到新模块或使用接口解耦就像user-api那样来打破循环。依赖作用域Scope理解compile默认、provided、runtime、test的区别至关重要。在API模块中对Spring相关依赖使用provided就是为了防止将不必要的实现传递到调用方。版本冲突这正是父POM中使用dependencyManagement的最大价值。确保所有子模块的依赖版本都由父POM统一管理可以彻底消除版本冲突。构建顺序Maven会依据父POM中modules声明的顺序以及模块间的依赖关系来决定构建顺序。如果A模块依赖B模块那么B必须先于A被构建。通常我们把基础模块common-*,*-api放在前面业务模块次之启动模块放在最后。4.4 为团队定制的代码规范与目录约定一个可维护的模板必须包含代码规范。这不仅仅是Checkstyle或SpotBugs插件更是团队共识。目录结构统一每个业务模块内部必须遵循相同的结构。例如src/main/java/com/example/team/{module}/ /controller/ /service/ (可细分impl接口) /mapper/ 或 /repository/ /entity/ /config/ (模块特有配置) src/main/resources/mapper/ (MyBatis XML文件)命名约定控制器XxxController服务接口XxxService服务实现XxxServiceImpl数据访问XxxMapper实体XxxEntity或Xxx(如User)DTO:XxxDTO,XxxRequest,XxxVO转换器XxxConverter(使用MapStruct)接口契约先行鼓励团队在开发新功能时先在一个*-api模块中定义好DTO和接口哪怕是内部Service接口然后再去实现。这能强制大家思考接口的合理性减少后期改动。文档与注释在README.md或项目Wiki中明确记录模块职责、依赖关系图、新模块创建步骤、配置覆盖规则等。新成员 onboarding 时这份文档就是最好的指南。5. 进阶考量模板的扩展性与边界一个模板不仅要解决当前问题还要能适应未来的变化。在模板设计初期就需要考虑一些扩展性场景。5.1 多环境配置与敏感信息处理团队开发必然涉及多环境开发、测试、生产。我们的模板可以通过Spring Profiles轻松支持。在启动模块的resources目录下创建application-dev.yml,application-test.yml,application-prod.yml。在通用的application.yml中设置默认激活的profile如spring.profiles.active: dev。不同环境的差异化配置如数据库地址、Redis连接、第三方API密钥写在对应的profile文件中。敏感信息如密码、密钥绝对不要提交到代码库。应该使用环境变量、配置中心如Nacos, Apollo或加密配置。在模板中可以约定使用Value(${secret.key})并从环境变量读取并在项目文档中明确说明。5.2 测试策略的模块化测试也应该模块化。在父POM中可以统一管理测试依赖的版本如JUnit, Mockito。每个业务模块都有自己的src/test目录进行单元测试和集成测试。对于common-core这类工具模块测试应侧重于工具方法的正确性对于user-service测试应覆盖Service层逻辑和Controller层接口。可以建立一个integration-test模块专门用于编写需要启动整个应用或涉及多个模块的端到端测试。但这个模块的构建应该从默认生命周期中排除只在需要时手动执行以免拖慢日常构建速度。5.3 未来向微服务演变的可能性当前模板是一个单体多模块应用但它已经为未来可能的微服务拆分打下了良好基础。清晰的边界每个业务模块user-service,order-service内部已经是高内聚的具备了独立服务的能力。API契约user-api模块的存在使得用户服务的接口被明确定义。如果将来要把用户模块拆成独立服务这个API模块可以直接作为Feign客户端的依赖提供给其他服务调用接口契约无需大变。数据库隔离虽然在单体中可能共用一个数据库但在模块设计时可以鼓励每个业务模块操作自己对应的数据库表避免跨模块的复杂SQL join为以后分库分表做准备。当决定拆分时每个业务模块加上自己的启动类和独立配置就可以快速改造成一个独立的Spring Boot微服务。而common-core和common-database可以打包成公司内部的私有基础库供所有微服务引用。5.4 代码质量门禁与CI/CD集成模板的最后一环是将其与自动化流程结合。可以在父POM中集成代码质量检查插件并配置预定义的规则。Maven Checkstyle Plugin检查代码风格是否符合约定。SpotBugs静态代码缺陷分析。JaCoCo代码覆盖率检查可以设定一个团队认可的覆盖率阈值如80%。Git Hooks利用husky等工具在提交代码前自动运行代码格式化如Spotless和基础检查。在CI/CD流水线如Jenkins, GitLab CI中配置构建步骤1) 编译所有模块2) 运行所有单元测试并生成覆盖率报告3) 运行静态代码分析4) 如果都通过再打包部署。将这些检查作为“门禁”可以确保所有合并到主分支的代码都符合团队的质量标准。搭建这样一个模板初期确实会花费一些时间并可能让习惯单体的开发者感到些许束缚。但一旦团队适应了这种结构化和契约化的开发方式其带来的长期收益——清晰的代码归属、更少的合并冲突、更快的 onboarding 速度、更顺畅的跨模块协作——将远远超过初期的投入。它不仅仅是一个项目骨架更是一套促进团队高效协作的工程实践和共同语言。