Maven项目骨架搭建指南
Maven 项目动态骨架搭建指南
什么是 Maven Archetype?
Maven Archetype 是一个项目模板工具包。它能够帮助开发者创建可复用的项目模板,并通过参数化配置,快速生成符合特定结构和规范的新项目。使用动态骨架,可以极大地提升项目初始化的效率和标准化程度。
核心步骤概览
- 创建原型项目:搭建一个标准的、作为模板基础的项目结构。
- 生成元数据:通过 Maven 命令在原型项目的基础上生成 Archetype 元数据文件。
- 重命名模块为动态占位符 ⭐:把
archetype-resources/下写死的模块目录名改成__rootArtifactId__-xxx。 - 配置元数据:同步修改
archetype-metadata.xml与父pom.xml,定义动态参数和文件生成规则。 - 安装骨架:将配置好的动态骨架安装到本地 Maven 仓库,并用
jar tf验证内容。 - 使用骨架:通过 IDE 或 Maven 命令使用该骨架创建新项目。
详细步骤
1. 创建原型项目
首先,创建一个你希望作为模板的 Maven 项目。一个典型的分层项目结构如下:
legend-frame-archetype-std/
├── legend-frame-std-api/ # API 接口层
├── legend-frame-std-app/ # 应用启动和配置模块
├── legend-frame-std-domain/ # 领域模型层
├── legend-frame-std-infrastructure/ # 基础设施层(如 MyBatis)
├── legend-frame-std-trigger/ # Trigger 触发器层
├── legend-frame-std-types/ # 通用类型定义层
└── pom.xml # 父工程 POM
⚠️ 生成前建议先清理:如果原型项目里已经有
target/目录(历史编译产物、.iml缓存等),create-from-project会一并扫描进去,导致骨架里混入垃圾文件。执行前先跑一次mvn clean,或直接删掉所有模块的target/目录。
2. 生成 Archetype 元数据
在原型项目的根目录(即 legend-frame-archetype-std/)下,打开终端并执行以下 Maven 命令:
mvn archetype:create-from-project执行成功后,项目会在 target/generated-sources/archetype/ 目录下生成一个 archetype 项目,其中包含了骨架的元数据文件。
3. 配置 archetype-metadata.xml
进入生成的 archetype 项目,找到并打开 src/main/resources/META-INF/maven/archetype-metadata.xml 文件。这是定义动态骨架行为的核心。
生成的骨架目录位于:
legend-frame-archetype-std/
└── target/generated-sources/archetype/ ← 后面所有操作都在这个目录里进行
├── pom.xml
└── src/main/
├── resources/META-INF/maven/archetype-metadata.xml
└── archetype-resources/ ← 骨架真正的“模板文件”都在这里
├── pom.xml
├── legend-frame-std-api/
├── legend-frame-std-app/
└── ...3.1 【关键】把硬编码的模块名改成动态占位符
mvn archetype:create-from-project 只是把原型项目原样照抄了一份,模块目录名 legend-frame-std-api 这类是写死的。如果不改,以后不管用什么 artifactId 生成新项目,模块目录名永远都叫 legend-frame-std-api,这就失去了动态骨架的意义。
需要把 archetype-resources/ 下的每个模块目录重命名为 __rootArtifactId__-xxx 格式(前后各两个下划线):
| 修改前(写死) | 修改后(动态) |
|---|---|
legend-frame-std-api | __rootArtifactId__-api |
legend-frame-std-app | __rootArtifactId__-app |
legend-frame-std-domain | __rootArtifactId__-domain |
legend-frame-std-infrastructure | __rootArtifactId__-infrastructure |
legend-frame-std-trigger | __rootArtifactId__-trigger |
legend-frame-std-types | __rootArtifactId__-types |
命名约定:
__xxx__是 Archetype 保留语法,只有写在__之间的变量名才会被识别并参与文件/目录名的替换。常见的有__rootArtifactId__、__artifactId__、__package__。
重命名后的结构:
archetype-resources/
├── __rootArtifactId__-api/
├── __rootArtifactId__-app/
├── __rootArtifactId__-domain/
├── __rootArtifactId__-infrastructure/
├── __rootArtifactId__-trigger/
├── __rootArtifactId__-types/
└── pom.xml修改目录名后,还需要对应修改 archetype-metadata.xml 和 archetype-resources/pom.xml 两个文件,三者必须完全一致:
① archetype-metadata.xml —— 声明模块 + 文件收集规则:
<?xml version="1.0" encoding="UTF-8"?>
<archetype-descriptor xsi:schemaLocation="https://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.1.0 http://maven.apache.org/xsd/archetype-descriptor-1.1.0.xsd" name="legend-frame-archetype-std"
xmlns="https://maven.apache.org/plugins/maven-archetype-plugin/archetype-descriptor/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<fileSets>
<fileSet filtered="true" encoding="UTF-8">
<directory>generator-dao</directory>
<includes>
<include>**/*.xml</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
<directory>generator-dao/src</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
<directory>.idea</directory>
<includes>
<include>**/*.xml</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
<directory>.idea/shelf</directory>
<includes>
<include>**/*.xml</include>
</includes>
</fileSet>
<fileSet encoding="UTF-8">
<directory>.idea/.cache</directory>
<includes>
<include>**/*.db</include>
</includes>
</fileSet>
<fileSet encoding="UTF-8">
<directory>.idea</directory>
<includes>
<include>**/*.gitignore</include>
</includes>
</fileSet>
<fileSet encoding="UTF-8">
<directory>.idea/shelf</directory>
<includes>
<include>**/*.patch</include>
</includes>
</fileSet>
<fileSet encoding="UTF-8">
<directory></directory>
<includes>
<include>.gitignore</include>
<include>legend-frame-std-api.iml</include>
<include>legend-frame-std-app.iml</include>
<include>legend-frame-std-domain.iml</include>
<include>legend-frame-std-infrastructure.iml</include>
<include>README.md</include>
</includes>
</fileSet>
</fileSets>
<modules>
<module id="${rootArtifactId}-app" dir="__rootArtifactId__-app" name="${rootArtifactId}-app">
<fileSets>
<fileSet filtered="true" packaged="true" encoding="UTF-8">
<directory>src/main/java</directory>
<includes>
<include>**/*.java</include>
</includes>
</fileSet>
<fileSet filtered="true" encoding="UTF-8">
<directory>src/main/resources</directory>
<includes>
<include>**/*.xml</include>
</includes>
</fileSet>
<fileSet encoding="UTF-8">
<directory>src/main/resources</directory>
<includes>
<include>**/*.yml</include>
</includes>
</fileSet>
</fileSets>
</module>
<!-- domain / infrastructure / trigger / types 照上面的格式补齐即可 -->
</archetype-descriptor>② archetype-resources/pom.xml —— 父 pom 的 <modules> 必须同步改名:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>${groupId}</groupId>
<artifactId>${rootArtifactId}</artifactId>
<version>${version}</version>
<packaging>pom</packaging>
<modules>
<module>${rootArtifactId}-app</module>
<module>${rootArtifactId}-types</module>
<module>${rootArtifactId}-trigger</module>
<module>${rootArtifactId}-api</module>
<module>${rootArtifactId}-domain</module>
<module>${rootArtifactId}-infrastructure</module>
</modules>
<dependencyManagement>
<dependencies>
<!--工程模块-->
<dependency>
<groupId>cn.legend</groupId>
<artifactId>${rootArtifactId}-types</artifactId>
<version>${legend.version}</version>
</dependency>
<dependency>
<groupId>cn.legend</groupId>
<artifactId>${rootArtifactId}-trigger</artifactId>
<version>${legend.version}</version>
</dependency>
<!-- 其余模块 照上面的格式补齐即可 -->
</dependencies>
</dependencyManagement>
</project>子模块 pom 中 <parent> 的 artifactId 保持 ${rootArtifactId} 即可。因为子模块 pom 的 fileSet 配了 filtered="true",${rootArtifactId} 会在生成时被自动替换,无需手工处理。

pom还需要加上模块名称

💡 踩坑提醒(血泪):
requiredModules、<module name="...">、<modules>这三处的模块名,必须与archetype-resources/下真实存在的目录名一模一样。只要有一处对不上,mvn clean install不会报错,但生成的新项目会缺模块(最常见的是整个子模块直接消失,或 pom 里<modules>引用了不存在的目录导致构建失败)。改完建议用下面的自检命令核对一遍。
快速自检:确认三处名称完全一致(PowerShell):
cd target/generated-sources/archetype
# 1. 实际目录名
ls src/main/archetype-resources | ? psIscontainer | % name
# 2. metadata 中声明的模块
select-string -Path src/main/resources/META-INF/maven/archetype-metadata.xml -Pattern 'name="(__rootArtifactId__-)[a-z]+"'
# 3. 父 pom 的 modules
select-string -Path src/main/archetype-resources/pom.xml -Pattern '<module>'三份输出的模块名列表必须完全相同。
Windows 重命名失败的处理:__rootArtifactId__-api 这类带连续下划线的名称是合法的,但若资源管理器重命名报错(例如被 IDE 占用),关闭 IDEA 后再改,或用命令行批量重命名:
cd src/main/archetype-resources
gci -d -Filter "legend-frame-std-*" | % {
Rename-Item $_.FullName ($_.Name -replace '^legend-frame-std-', '__rootArtifactId__-')
}3.2 文件过滤与打包
filtered="true":此属性表示该fileSet下的文件将会被扫描并替换其中的占位符(变量)。packaged="true":此属性表示文件将被放置在用户输入的groupId对应的包结构下。
注意:请检查
fileSet配置,移除不需要包含在最终骨架中的文件(例如.idea,.git,*.iml等)。
3.3 动态命名与内容替换
- 动态文件名:要使文件名动态化,请使用
__占位符名称__的格式。例如,__rootArtifactId__-api。 - 动态文件内容:在文件内部,使用
${占位符名称}的格式来定义变量。
常用占位符:
| 占位符 | 写法 | 含义 | 示例(groupId=com.example、artifactId=demo) |
|---|---|---|---|
${package} | 内容替换 | 由 groupId 转换而来的包名 | com.example |
${groupId} | 内容替换 | 用户输入的 groupId | com.example |
${artifactId} | 内容替换 | 当前模块的 artifactId | demo-app |
${rootArtifactId} | 内容替换 | 父工程 artifactId | demo |
${version} | 内容替换 | 项目版本 | 1.0-SNAPSHOT |
__rootArtifactId__ | 文件/目录名 | 父工程 artifactId | demo-app(目录名) |
重要区别:目录名/文件名只能用
__xxx__形式(${}在文件名里不会被解析);文件内容里用${xxx}。两套语法不能混用。
包路径动态化:packaged="true" 时,archetype-resources/xxx/src/main/java/com/example/... 里的 com/example 部分会被自动搬到 ${package} 对应的目录下。因此模板里保留原型项目的原始包路径即可,不要手写成 __package__,除非你的包目录中间还带固定的子层级。
4. 安装并验证骨架
完成 archetype-metadata.xml 的配置后,在生成的 archetype 项目目录(target/generated-sources/archetype/)下执行 install 命令:
mvn clean install该命令会将你的动态骨架打包并安装到本地 Maven 仓库中。你可以在本地仓库的对应 groupId 和 artifactId 目录下看到安装好的骨架文件。

安装后务必验证骨架内容(避免安装了一个“半成品”骨架):
# 查看本地仓库中的骨架 jar 里到底装进了哪些文件
jar tf ~/.m2/repository/com/xxx/legend-frame-archetype-std/1.0/legend-frame-archetype-std-1.0.jarWindows 下路径为 %USERPROFILE%\.m2\repository\...(这个是你本地仓库的路径),也可以用:
# 列出 jar 内所有文件,检查是否包含 __rootArtifactId__-api 等模块与需要的 java 文件
jar tf "$env:USERPROFILE\.m2\repository\com\xxx\legend-frame-archetype-std\1.0\legend-frame-archetype-std-1.0.jar" | more重点确认三件事:
META-INF/maven/archetype-metadata.xml存在;archetype-resources/下的模块目录名是__rootArtifactId__-xxx(不是老的legend-frame-std-xxx);- 各模块的
.java/.yml等资源文件都在(文件没被收集 = fileSet 的 directory 路径或 includes 写错了)。
5. 使用骨架创建新项目
方式一:命令行生成(最快、最适合验证)
mvn archetype:generate \
-DarchetypeGroupId=org.legend \
-DarchetypeArtifactId=legend-frame-archetype-std \
-DarchetypeVersion=1.0 \
-DgroupId=com.example.demo \
-DartifactId=my-demo \
-Dversion=1.0-SNAPSHOT \
-DinteractiveMode=false参数说明:
| 参数 | 作用 |
|---|---|
archetypeGroupId/ArtifactId/Version | 骨架自身的坐标(第 4 步安装的那个) |
groupId | 新项目的 groupId,同时决定新项目的包名 |
artifactId | 新项目根 artifactId,会替换所有 __rootArtifactId__ 与 ${rootArtifactId} |
interactiveMode=false | 非交互模式,不传则逐项询问 |
生成结果应立即得到 my-demo-api、my-demo-app 等目录,且父 pom 的 <modules> 已替换为 my-demo-xxx。
本地仓库已有骨架却报
Could not find archetype:多半是骨架坐标写错,或安装时target/generated-sources/archetype/pom.xml里的groupId/artifactId/version与你传的不一致——打开该 pom 核对即可。
方式二:IDEA 图形界面
现在,你可以通过 IDE(如 IntelliJ IDEA)来使用这个骨架创建新项目。
- 在 IDEA 中选择
File -> New -> Project...。 - 勾选
Create from archetype,然后点击Add Archetype...。 - 填入你刚刚安装的骨架的
GroupId、ArtifactId和Version,然后确认。

- 选择你添加的骨架,点击
Next,然后输入新项目的GroupId、ArtifactId和Version,即可快速生成项目。

IDEA 小技巧:Add Archetype 时若 Version 下拉为空,先在终端执行一次
mvn archetype:generate(或IDEA → Settings → Build Tools → Maven → Reload),确保本地仓库索引刷新。
生成后的验收清单
常见问题排查
| 现象 | 根因 | 处理 |
|---|---|---|
| 生成的新项目只有一个 pom,子模块全没了 | 目录名改了,但 requiredModules / <module name> 没同步 | 三处名称对齐(见 3.1 自检命令) |
子模块目录名仍是 legend-frame-std-api | 只改了 xml,没重命名 archetype-resources/ 下的目录 | 重命名目录后重新 install |
mvn install 成功,但用骨架生成后 pom 里 <modules> 找不到目录 | 父 pom 的 <modules> 未改成 __rootArtifactId__-xxx | 改 archetype-resources/pom.xml |
java 文件生成到了 src/main/java 根下,包结构丢失 | fileSet 的 packaged 写成 false | 含源码的 fileSet 设 packaged="true" |
| 资源文件(yml/xml)根本没生成 | directory 路径写错,或 includes 没覆盖到 | 用 jar tf 检查骨架 jar 是否含该文件 |
文件里的 ${rootArtifactId} 变成了字面量没替换 | 该 fileSet 的 filtered="false" | 改成 filtered="true" |
| 改了 xml 但不生效 | 忘记重新 mvn clean install | 每次改完都要重装(IDEA 也要重新 Add Archetype 的 Version) |
骨架里混进 target/、.idea、*.iml | 生成前未清理原型项目 | 原型 mvn clean 后重新 create-from-project |
修改-重装-验证 标准循环
动态骨架调试期间,每改一次都走这三步,别跳:
# 1. 重新安装骨架(在 target/generated-sources/archetype/ 下)
mvn clean install
# 2. 清掉旧的生成结果,换一个新 artifactId 验证
cd /tmp && rm -rf my-demo
# 3. 重新生成并编译
mvn archetype:generate -DarchetypeGroupId=org.legend -DarchetypeArtifactId=legend-frame-archetype-std \
-DarchetypeVersion=1.0 -DgroupId=com.example.demo -DartifactId=my-demo \
-Dversion=1.0-SNAPSHOT -DinteractiveMode=false
cd my-demo && mvn clean compile注意:
target/generated-sources/archetype/是在原型项目执行create-from-project时生成的。如果原型项目结构变了(增删模块、改包名),需要重新执行create-from-project,之前对生成目录做的手工修改会全部丢失——建议把改好的 archetype 目录单独复制一份到版本库保存。
发布到远程仓库
(待续)