CHARLIE SAYS

查理如是说
DATE 2026-08-24
THEME
SERIES / JHIPSTER / P-239 · JHipster 项目开发

JHipster 开发 24:子生成器二次开发与蓝图机制

上一篇讲到”改得多了就固化进 blueprint”,这篇兑现这句话。当团队的定制点从三个涨到三十个——统一的公司异常码规范、内部组件库、合规要求的默认配置——继续靠 CUSTOM 标记搬运就是苦役。Blueprint 机制让这些定制变成”生成器原生行为”:你生成的项目第一天就长成公司的样子。

Blueprint 是什么

Blueprint 本质是一个 npm 包,包名约定以 generator-jhipster- 开头(如 generator-jhipster-foo)。安装后通过 --blueprints 选项激活:

npm install -g generator-jhipster-foo
jhipster --blueprints foo
# 或在 .yo-rc.json 中固化:
{
  "generator-jhipster": {
    "blueprints": [{ "name": "generator-jhipster-foo", "version": "1.2.0" }]
  }
}

激活后,blueprint 可以接管或扩展任意子生成器(app、entity、spring-controller、kubernetes……)的任意 priority 阶段:修改上下文变量、替换模板、追加文件。核心概念叫 composability——blueprint 不 fork JHipster,而是声明”我要在哪些钩子上做什么”,JHipster 升级时 blueprint 跟着小版本走即可,fork 那种每次 major 都要重对齐的痛苦被结构性地缓解了。

flowchart TB
  subgraph JH["generator-jhipster 9.x"]
    A["app 子生成器<br/>initializing → prompting →<br/>configuring → writing → ..."]
  end
  subgraph BP["generator-jhipster-foo(npm 包)"]
    B["声明钩子:<br/>inherit generator<br/>覆盖某模板 / 追加文件 / 改 context"]
  end
  JH -->|"composable 组合"| BP
  BP --> C["产出的应用 =<br/>JHipster 标准 + 公司定制"]

什么场景值得做 blueprint

判断标准:定制点是否跨多个项目且稳定

场景典型定制值不值
公司脚手架统一包名前缀、统一分层目录、内部 parent pom值,一次投入全公司复用
内部组件库接入前端注入公司 UI 库、主题、icon set
合规默认值审计日志默认开、密码策略、特定安全 header值,且避免每个项目漏配
生成部署脚本公司内部 PaaS 的部署描述文件
单个项目的业务定制某个实体的特殊接口不值,放业务代码(第 23 篇)
一次性尝试试试某新技术组合不值,先用 CLI 选项拼

我们是在第三个 JHipster 项目立项时动手的:三个项目各自维护一份”初始化后手工改造清单”(17 条),每条都是新人入职的学习成本。做成 blueprint 后清单归零。

开发一个最小 blueprint

generator-jhipster-company 为例,五步走完一个能用的骨架。

第一步:脚手架。 社区提供专用工具生成 blueprint 骨架:

npx generator-jhipster-bootstrap-blueprint company
# 产出 generator-jhipster-company/ 工程
cd generator-jhipster-company
npm install && npm run build

目录结构(TypeScript,与 9.x 生成器同构):

generator-jhipster-company/
├── generators/
│   ├── app/index.ts              # 接管 app 子生成器
│   └── entity/index.ts           # 接管 entity 子生成器(可选)
├── generators/app/templates/     # 你的模板(EJS)
├── package.json                  # name 必须 generator-jhipster-company
└── tsconfig.json

第二步:声明继承关系。 generators/app/index.ts

import BaseGenerator from 'generator-jhipster/generators/base';

export default class extends BaseGenerator {
  constructor(args, opts, features) {
    super(args, opts, { ...features });
    // 声明依赖 JHipster 的 app generator,sbsGenerator 表示组合而非整体替换
    this.jhipsterContext = this.options.jhipsterContext;
  }

  get [BaseGenerator.INITIALIZING]() {
    return {
      ...super.initializing,
      validateCompanyConfig() {
        this.log('company blueprint: initializing');
      },
    };
  }

  get [BaseGenerator.WRITING]() {
    return {
      ...super.writing,
      writeCompanyFiles() {
        this.renderTemplate(
          'security-headers.ejs',                       // blueprint 模板
          this.destinationPath('src/main/resources/config/company-security.yml'),
        );
      },
    };
  }
}

关键是两个动作:...super.writing 保留 JHipster 原生行为(扩展),再追加自己的 writeCompanyFiles。如果去掉 super 调用,就是整体替换该 priority——composability 的两种粒度都在这。

第三步:改 context 而不是改模板。 想让生成的所有 *Resource.java 带公司异常码?不要复制修改 JHipster 模板(升级就脱轨),而是在 priority 里改共享 context,或在 blueprint 里用文件级后处理:

get [BaseGenerator.POST_WRITING]() {
  return {
    injectAuditDependency() {
      // 给 pom.xml 追加公司审计依赖
      this.packageJson.mergeDestinationJson?.('pom.xml', { /* ... */ });
    },
  };
}

第四步:注册与使用。 本地开发用 npm link:

cd generator-jhipster-company && npm link
mkdir /tmp/demo && cd /tmp/demo
jhipster --blueprints company --skip-checks
# 验证产物包含 company-security.yml 与追加的依赖

第五步:模板维护策略。 修改 JHipster 已有模板的正规姿势是”模板替换 + 上游对齐”:把上游模板拷进 blueprint 声明覆盖,并在 CI 里加一个每日任务 diff 上游模板,JHipster 一更新模板就提醒对齐。覆盖面要克制——覆盖的模板越多,跟版成本越高,这与第 22 篇的升级哲学一脉相承。

官方与社区 blueprint 生态

Blueprint说明
generator-jhipster-nodejs后端改用 Node.js(NestJS 风格)
generator-jhipster-dotnetcore后端改用 .NET Core
generator-jhipster-kotlinSpring 后端改用 Kotlin
generator-jhipster-micronaut后端改用 Micronaut
generator-jhipster-migrate官方升级辅助(第 22 篇主力工具)
generator-jhipster-docker-azure 等特定部署目标定制
社区各类graphql、grpc、特定前端库等,质量参差

选型时看三点:最近 release 是否跟上了当前 JHipster major、issue 区对 9.x 的支持情况、覆盖的模板数量(越少越稳)。

维护成本警告

Blueprint 不是免费午餐,投入前必须知道:

  • 跟版义务:JHipster 半年一个 major,blueprint 每次都要验证。9.x 生成器 TypeScript 重写后,老 blueprint 从 JS 迁到 TS 是一次实打实的重构;
  • 测试矩阵:blueprint 要在”多种应用选项组合”下都不坏,官方用 blueprint 测试套件跑组合矩阵,自研 blueprint 至少要覆盖自己常用的 3-4 种组合;
  • 人员单点:懂生成器 internals 的人团队里通常只有一两个,文档化(为什么加这个钩子、上游模板为什么这么改)比业务代码更不能省;
  • 退路:定制点失效时要能从 blueprint 摘除。每个钩子保持小而独立,避免互相纠缠成一张拆不掉的网。

我们的量级参考:初始投入约 3 人周,此后每次 JHipster major 花 2-4 人日对齐,换来三个项目 × 每年两次升级的定制零搬运。项目数量少于两个时,这笔账是亏的。

小结

  • Blueprint 是 npm 包 + --blueprints 选项,composability 让你在钩子上扩展而非 fork JHipster,天然比 fork 抗升级。
  • 跨项目且稳定的定制(脚手架、组件库、合规默认值)值得做,单项目业务定制不值得。
  • 最小闭环五步:脚手架 → 继承声明 → 改 context/追加文件 → npm link 验证 → 上游模板对齐机制。
  • 维护成本真实存在:跟版、测试矩阵、人员单点,两个项目以下慎入。

系列导航

← 设计模式 024:策略(Strategy) 目录 算法 025:图:最小生成树(Prim & Kruskal) →
← 返回文章列表