3. 项目结构搭建:Maven/Gradle项目结构、模块划分、包命名规范、配置文件管理
好,咱们进入第三章。这一章我打算聊聊项目结构。你可能觉得这有什么好讲的?不就是建几个文件夹嘛。嗯,我当年也这么想,直到在一个大型微服务项目里,因为结构混乱,找配置文件找了整整一下午……从那以后,我对项目结构就特别较真。
说白了,项目结构就是代码的骨架。骨架歪了,后面长出来的肉(业务代码)肯定也歪。咱们今天就把Maven和Gradle这两种主流构建工具的项目结构、模块划分、包命名和配置文件管理,一次性说清楚。
3.1 Maven项目结构:约定优于配置
Maven最核心的思想就是“约定优于配置”。什么意思?就是它规定好了你的代码该放哪,你照着做就行,不用自己瞎折腾。我个人习惯,只要是Java后端项目,首选Maven标准结构。
一个标准的Maven项目长这样:
my-project/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/ # 主代码
│ │ ├── resources/ # 配置文件
│ │ └── webapp/ # Web资源(可选)
│ └── test/
│ ├── java/ # 测试代码
│ └── resources/ # 测试配置文件
└── target/ # 编译输出目录(自动生成)
这里有个坑,我提醒一下。很多新手会把配置文件乱放,比如把数据库连接配置扔到src/main/java里。千万别!java目录只放.java文件,配置文件一律进resources。我曾经在一个项目里看到有人把application.yml放在java目录下,结果打包后死活找不到配置……
target目录是编译产物,不要提交到版本控制。记得在.gitignore里加上target/。
3.2 Gradle项目结构:灵活但要有规矩
Gradle比Maven灵活,但灵活过头就容易乱。我见过有的团队用Gradle,每个模块的构建脚本写得五花八门,最后维护成本比Maven还高。
Gradle的标准结构其实和Maven很像:
my-project/
├── build.gradle # 构建脚本
├── settings.gradle # 项目设置
├── gradle.properties # 属性配置
├── src/
│ ├── main/
│ │ ├── java/
│ │ └── resources/
│ └── test/
│ ├── java/
│ └── resources/
└── build/ # 编译输出目录
Gradle有个好处,就是可以用Kotlin DSL写构建脚本。我个人比较推荐,因为类型安全,写起来不容易出错。不过如果你团队里都是Java老手,Groovy DSL也完全够用。
settings.gradle里可以配置项目名称和模块列表。我习惯把项目名和Git仓库名保持一致,这样CI/CD配置起来省事。
3.3 模块划分:别让一个项目变成“大泥球”
模块划分,说白了就是怎么切分你的代码。我见过最夸张的项目,一个模块里塞了200多个类,从数据库访问到前端渲染全在一个包里。这种项目,维护起来简直噩梦。
我的经验是,模块划分要遵循几个原则:
- 单一职责:每个模块只做一件事。比如用户模块只管用户,订单模块只管订单。
- 依赖方向:依赖关系要单向,不能循环依赖。我曾经在一个项目里,A模块依赖B,B又依赖A,结果编译都过不去……
- 可独立部署:每个模块最好能独立打包和部署,这样微服务化的时候直接拆出去就行。
一个典型的多模块Maven项目结构:
my-platform/
├── pom.xml # 父POM
├── my-platform-common/ # 公共模块
│ └── pom.xml
├── my-platform-user/ # 用户模块
│ └── pom.xml
├── my-platform-order/ # 订单模块
│ └── pom.xml
└── my-platform-gateway/ # 网关模块
└── pom.xml
父POM里统一管理依赖版本:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
3.4 包命名规范:从域名到代码的映射
包命名,说白了就是给你的代码起个“身份证”。Java里包名通常用公司域名的倒序开头,比如com.example.project。这个大家应该都知道,我就不多说了。
我想重点说的是,包名内部的层次怎么设计。我的习惯是:
- 按功能分层:
controller、service、repository、model - 按业务模块:
user、order、payment - 两者结合:先按业务模块,再按功能分层
举个例子:
com.example.platform
├── user
│ ├── controller
│ ├── service
│ ├── repository
│ └── model
├── order
│ ├── controller
│ ├── service
│ ├── repository
│ └── model
└── common
├── config
├── util
└── exception
这种结构的好处是,每个业务模块自成一体,找代码特别快。我当年在一个项目里,所有代码都堆在com.example.controller、com.example.service这种平铺结构里,找用户相关的代码得翻半天……
com.example.users。Java社区惯例用单数。另外,包名里不要有下划线或大写字母,全小写。
3.5 配置文件管理:环境分离与敏感信息保护
配置文件管理,这是个大话题。我见过太多项目把数据库密码、直接写在配置文件里,然后提交到Git仓库。嗯,后果可想而知。
我的做法是:
- 环境分离:每个环境(开发、测试、生产)有独立的配置文件
- 敏感信息外部化:密码、密钥等通过环境变量或配置中心注入
- 配置模板化:提交到Git的是模板文件,实际值由CI/CD替换
Spring Boot项目里,我习惯这样组织配置文件:
src/main/resources/
├── application.yml # 公共配置
├── application-dev.yml # 开发环境
├── application-test.yml # 测试环境
└── application-prod.yml # 生产环境
在application.yml里指定激活哪个环境:
spring:
profiles:
active: dev
对于敏感信息,比如数据库密码,我建议用环境变量:
spring:
datasource:
url: ${DB_URL}
username: ${DB_USER}
password: ${DB_PASSWORD}
然后在部署时通过环境变量传入。这样就算代码泄露,密码也不会暴露。
.env.example文件,里面列出所有需要的环境变量和说明。这样新同事来了,照着这个文件配置就行,不用到处问。
3.6 知识结构图:项目搭建全景
下面这张图,是我对项目结构搭建的整体理解。你可以把它当作一个检查清单,每次新建项目时对照着来。
这张图把项目结构搭建的四个核心维度串起来了。你新建项目时,按这个顺序走一遍,基本不会出大问题。
3.7 总结
项目结构搭建,说白了就是定规矩。规矩定好了,团队协作就顺畅,代码维护就轻松。我见过太多项目,前期图省事随便建个结构,后期改得痛不欲生。
最后送你一句话:项目结构不是写出来的,是设计出来的。花点时间在设计上,后面能省十倍的时间。
- Maven用标准结构,Gradle保持一致性
- 模块划分遵循单一职责和单向依赖
- 包名用域名倒序+业务+功能分层
- 配置文件环境分离,敏感信息用环境变量