- 文档
- 教程
- 后端
【免费下载链接】CodeGuide
:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助,请给予支持(关注、点赞、分享)!
作者:小傅哥
脚手架并非只有 Spring 官网和阿里云才能提供,在大型互联网公司中,通用脚手架往往无法融入公司自研的 RPC、数据库路由、注册中心、分布式任务、MQ 队列等组件,此时就需要结合模板引擎自行开发一套符合公司技术栈的统一脚手架。本文以 CodeGuide 仓库中的《不重复造轮子只是骗小孩子的,教你手撸 SpringBoot 脚手架!》为核心,完整演示如何使用 FreeMarker(FTL 模板)从零构建一个 SpringBoot 脚手架工程,并借助仓库中后续的 DDD 脚手架演进案例,讲解脚手架从"生成基础工程"到"支撑 DDD 四层架构与分布式技术栈"的完整落地路径。
一、为什么我们要去造轮子?
造轮子的核心目的,是为了解决通用共性问题的凝练和复用。
虽然市面上已经有了大量成熟稳定用于支撑系统建设的轮子,也就是服务、框架、组件、工具等,但对于一些较大型的公司来说,这些轮子可能并不一定能很好的支撑起系统需要承载的服务体量,这个时候就需要自建一些轮子。
而提倡的不重复造轮子,一般用在以官网推出的核心轮子上是适合的,比如 SpringBoot、Netty、HBase 等。但对于一些特殊场景的解决方案工具型组件,通常是没有完全符合的轮子的,就像 SpringBoot 脚手架。
其实每个较大型的公司都会有很多同类技术服务的组件,例如 RPC、数据库路由、注册中心、分布式任务、MQ 队列消息等,而这时候脚手架的开发就需要适配这些组件,搭建出符合自己公司技术栈实现需要的系统架构。这不同于一些较小的互联网公司,可以完全使用 SpringBoot 提供的一整套解决方案。
另外,造轮子是个人技术沉淀、也是薪资待遇的积累!
什么场景还能造轮子?
用于架构基建下的所有模块都可以成为轮子,通常我们都是在这些场景下建设需要的轮子:
负载均衡:统一流量的分发与调度。服务网关:统一外部请求的协议转换、鉴权、熔断、限流。服务治理:熔断、降级、限流、切量、黑白名单等非入侵式增强。框架语言:对编程框架的二次封装与扩展。服务组件:缓存热 Key 处理、Redis 层路由、活动邀请不唯一短码生成等业务通用组件。数据承载:对数据库、缓存、搜索引擎的封装与路由。框架结构:脚手架本身,统一工程创建标准。部署方式:镜像打包、环境编排、自动发布。工具插件:IDEA 插件、命令行工具等研发效能工具。
一个较成熟的互联网公司,大部分场景下的轮子已基本建造完了。剩下的一般是用于解决业务场景下非业务逻辑的通用性组件,例如,高并发下的缓存热 Key、Redis 层路由、活动邀请的不唯一短码生成等等类似这样的场景。但此类场景的轮子建设也是非常有价值的,在公司层面使用稳定后,还可以推广到市场获得一定的认可。
二、什么是脚手架呢?
What is scaffolding? Is it a term for a particular platform?
Scaffolding is a meta-programming method of building database-backed software applications. It is a technique supported by some model-view-controller frameworks, in which the programmer may write a specification that describes how the application database may be used. The compiler uses this specification to generate code that the application can use to create, read, update and delete database entries, effectively treating the template as a "scaffold" on which to build a more powerful application.
结合社区上的这段定义,脚手架是一种元编程方法,用于构建基于数据的应用。创建系统架构的程序员编写一份规格说明书,用于描述怎么去使用数据库。而脚手架可以根据这份规则说明书生成相应的框架代码。我们把这种模式称为脚手架,在脚手架上更高效的构建出 powerful 的应用!
说白了就是简化具有共性重复操作的简单工作,不再需要程序员还得一点点粘贴复制,克隆出一个已经存在的架构。只需要在界面或者公用接口上,传入必要的参数,就可以创建出一个应用开发框架。
三、谁提供了脚手架?
1、Spring 官网脚手架
- 推荐:⭐⭐⭐⭐
- 描述:Spring Initializr 本质上也是一个 Web 应用,它可以通过 Web 界面、Spring Tool Suite、IntelliJ IDEA 等方式,构建出一个基本的 Spring Boot 项目结构。同时可以使用它的源码进行本地部署。
2、阿里云脚手架
- 推荐:⭐⭐⭐⭐
- 描述:Aliyun Java Initializr 和 Spring Initializr 是同类的 Web 服务,是代码框架生成器,一键生成你的代码框架,有完善的工具链,免费的 IDEA 插件,方便直接在 IDE 中生成,同时也非常适合国内用户的网络环境。
其实,这两个脚手架都能很好的生成项目结构,让程序员可以在统一的标准下快速的进入开发环境。只是依赖于自身选择的支撑服务,选择不同的框架就可以了。
四、手撸一个脚手架!
都有脚手架了,那为什么要自己撸一个呢?
脚手架的目的是为了在统一的标准下快速建设系统框架,把系统开发过程中需要的配置、组件、服务、测试,一并通过配置引入到系统开发中。
但有些时候在互联网公司通用的脚手架是不太合适使用的,因为它没有把公司内的一些自研性质的组件引入进去,也不能很好的融合。如果已经用脚手架生成后还得需要研发人员自己大量复制进去一些特定的组件,就破坏了脚手架本身能力,也是破坏了准则和规范。
所以,需要结合脚手架的开发能力,包装各类特定组件、服务、配置,实现符合公司领域的统一脚手架。
那么,接下来就带着大家看看一个脚手架,该如何开发实现。其实并没有太复杂,我们可以使用 FreeMarker 的能力,构建系统框架。
1. 工程框架
EasyRiggerInitializr └── src ├── main │ ├── java │ │ └── cn.bugstack.initializr.rigger │ │ ├── application │ │ │ └── IProjectGenerator.java │ │ ├── domain │ │ │ ├── model │ │ │ │ └── ApplicationInfo.java │ │ │ │ └── ProjectInfo.java │ │ │ └── service │ │ │ ├── module │ │ │ │ ├── impl │ │ │ │ │ ├── GenerationApplication.java │ │ │ │ │ ├── GenerationIgnore.java │ │ │ │ │ ├── GenerationPackageInfo.java │ │ │ │ │ ├── GenerationPom.java │ │ │ │ │ ├── GenerationTest.java │ │ │ │ │ └── GenerationYml.java │ │ │ │ └── BaseModule.java │ │ │ └── ProjectGeneratorImpl.java │ │ └── RiggerApplication.java │ └── resources │ ├── generator │ │ ├── application.ftl │ │ ├── ignore.ftl │ │ ├── package-info.ftl │ │ ├── pom.ftl │ │ ├── test.ftl │ │ └── yml.ftl │ └── application.yml └── test └── java └── cn.bugstack.initializr.rigger.test └── ApiTest.java整个用于创建脚手架的工程并不复杂,主要就是通过 FreeMarker 对各类定义的 ftl 模板文件,生成对应的系统框架结构。这里包括:工程主体、框架结构、启动类、配置文件、测试类等,也可以结合自身需求把对应 ORM 的类和映射关系生成出来。
整个工程结构偏 DDD 层次结构,domain 领域中建设了所有的生成方式,resources/generator定义生成模板,其他地方就没有太大的差异了。关于 DDD 分层结构的详细拆解,可以参考仓库中的 MVC2DDD - 架构重构 一文,其中讲解了 DDD 各分层(应用层、领域层、基础层、触发器层等)的职责与调用链路。
接下来简单介绍下这个工程的代码,让大家可以理解这样的工程是如何开发的,也可以通过这样工程继续完善成自己需要的结构。
2. 应用层定义生成类接口
cn.bugstack.initializr.rigger.application.IProjectGenerator.java
public interface IProjectGenerator { void generator(ProjectInfo projectInfo) throws Exception; }- DDD 的分层结构,通常都会在 application 这个比较薄的层定义接口,再由 domain 领域层做相应的实现。
- 这个接口的定义主要是为了,让外部调用方可以通过此接口创建工程框架。
3. FTL 模板定义
什么是 FreeMarker?
FreeMarker 是一款模板引擎:即一种基于模板和要改变的数据,并用来生成输出文本(HTML 网页,电子邮件,配置文件,源代码等)的通用工具。它不是面向最终用户的,而是一个 Java 类库,是一款程序员可以嵌入他们所开发产品的组件。
模板编写为 FreeMarker Template Language(FTL)。它是简单的、专用的语言,不是像 PHP 那样成熟的编程语言。那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算,之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据,而在模板之外可以专注于要展示什么数据。
3.1 application.ftl
这是生成 SpringBoot 启动类的模板,${packageName}与${className}是两个动态占位符,在运行时由模板引擎用实际数据替换:
package ${packageName}; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ${className} { public static void main(String[] args) { SpringApplication.run(${className}.class, args); } }3.2 pom.ftl
这是生成 Maven 工程描述文件 pom.xml 的模板,通过${groupId}、${artifactId}、${version}、${name}、${description}五个占位符注入工程元数据,并统一继承 SpringBoot 官方父工程,保证依赖版本的一致性:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>${groupId}</groupId> <artifactId>${artifactId}</artifactId> <version>${version}</version> <name>${name}</name> <description>${description}</description> </project>3.3 yml.ftl
这是生成 application.yml 配置文件的模板,示例中预置了服务端口 8081,实际使用时可以按需扩展数据源、Redis、MQ 等配置项:
server: port: 8081以上,只是用于生成框架文件的基础 ftl 文件,有需要一些特殊判断和逻辑的,可以参考 FreeMarker 在线手册,编写自己需要的 ftl 文件。例如可以在模板中使用<#if>、<#list>等 FTL 指令做条件判断与循环生成,让一个模板适配多种工程形态。
4. FTL 生成文件
cn.bugstack.initializr.rigger.domain.service.module.impl.GenerationApplication.java
@Service public class GenerationApplication extends BaseModule { private Logger logger = LoggerFactory.getLogger(GenerationApplication.class); public void doGeneration(ProjectInfo projectInfo, String projectsRoot, String lastPackageName, StringBuffer applicationJavaName) throws Exception { ApplicationInfo applicationInfo = new ApplicationInfo( projectInfo.getGroupId() + "." + lastPackageName, applicationJavaName.toString() ); String packagePath = applicationInfo.getPackageName().replace(".", "/") + "/"; File file = new File(projectsRoot + projectInfo.getArtifactId() + "/src/main/java/" + packagePath, applicationInfo.getClassName() + ".java"); // 写入文件 super.writeFile(file, "application.ftl", applicationInfo); logger.info("创建主入口类 Application.java {}", file.getPath()); } }关于 ftl 文件的使用,无论用于生成哪一层的文件,基本都是通用。这里只展示一下关于 Application.java 的创建:
- 定义入参
ApplicationInfo:封装生成启动类所需的包名与类名。 - 定义文件位置:把包名中的
.替换为/,拼装出/src/main/java/下的完整物理路径。 - 写入到文件
super.writeFile:由父类BaseModule统一封装 FreeMarker 模板与数据模型的合并、以及文件的落盘逻辑。
类中的lastPackageName与applicationJavaName是在框架入口ProjectGeneratorImpl中根据artifactId计算得出的,下一节会看到完整的计算过程。
5. 创建框架入口
cn.bugstack.initializr.rigger.domain.service.ProjectGeneratorImpl.java
@Service public class ProjectGeneratorImpl implements IProjectGenerator { private Logger logger = LoggerFactory.getLogger(ProjectGeneratorImpl.class); @Resource private GenerationApplication generationApplication; @Resource private GenerationYml generationYml; @Resource private GenerationPom generationPom; @Resource private GenerationTest generationTest; @Resource private GenerationIgnore generationIgnore; @Resource private GenerationPackageInfo generationPackageInfo; @Override public void generator(ProjectInfo projectInfo) throws Exception { URL resource = this.getClass().getResource("/"); String projectsRoot = resource.getFile() + "/projects/"; String lastPackageName = projectInfo.getArtifactId().replaceAll("-", "").toLowerCase(); //启动类名称 String[] split = projectInfo.getArtifactId().split("-"); StringBuffer applicationJavaName = new StringBuffer(); Arrays.asList(split).forEach(s -> { applicationJavaName.append(s.substring(0, 1).toUpperCase() + s.substring(1)); }); applicationJavaName.append("Application"); // 1. 创建 Application.java generationApplication.doGeneration(projectInfo, projectsRoot, lastPackageName, applicationJavaName); // 2. 生成 application.yml generationYml.doGeneration(projectInfo, projectsRoot); // 3. 生成 pom.xml generationPom.doGeneration(projectInfo, projectsRoot); // 4. 创建测试类 ApiTest.java generationTest.doGeneration(projectInfo, projectsRoot, lastPackageName, applicationJavaName); // 5. 生成 .gitignore generationIgnore.doGeneration(projectInfo, projectsRoot); // 6. DDD 四层描述文件 generationPackageInfo.doGeneration(projectInfo, projectsRoot, lastPackageName, applicationJavaName); } }ProjectGeneratorImpl类,就是应用层接口IProjectGenerator在领域层的具体实现。这里包括了如下内容:
- 创建 Application.java
- 生成 application.yml
- 生成 pom.xml
- 创建测试类 ApiTest.java
- 生成 .gitignore
- DDD 四层描述文件
其中两个关键推导逻辑值得展开说明:
- 包名推导:
lastPackageName = projectInfo.getArtifactId().replaceAll("-", "").toLowerCase(),例如artifactId = web-test,则推导出包名后缀webtest,最终包名为groupId + ".webtest",即cn.bugstack.demo.webtest。 - 启动类名推导:按
-拆分artifactId,对每一段做首字母大写拼接,再追加Application后缀。例如web-test会生成启动类名WebTestApplication。这个规则保证了"创建出来的工程可以直接运行",也体现了脚手架统一规范的价值。
综上,就是整个脚手架生成的简要介绍,其实并没有多复杂,主要就是 ftl 文件的定义和使用,这种创建脚手架的方式还是很方便的。
6. 测试验证
单元测试
@Test public void test_IProjectGenerator() throws Exception { ProjectInfo projectInfo = new ProjectInfo( "cn.bugstack.demo", "web-test", "1.0.0-SNAPSHOT", "web-test", "Demo project for Spring Boot" ); iProjectGenerator.generator(projectInfo); }- 脚手架把创建出来的工程生成到
test-classes下的projects目录中,这个路径在ProjectGeneratorImpl中通过this.getClass().getResource("/")取得 classpath 根路径后拼装,也可以配置到其他路径里。 - 有了新生成的工程就可以通过 IDEA 打开了,与我们手动创建的工程是一样的。运行单元测试后,即可在对应输出目录看到包含
pom.xml、application.yml、启动类、测试类、.gitignore的完整工程骨架。
五、从"模板生成"到"DDD 脚手架"的演进
上文演示的 FreeMarker 脚手架方案,核心价值在于"通过 ftl 模板 + 入参数据,批量生成工程文件"。而这一思路在 CodeGuide 仓库后续的脚手架实践中被进一步放大,演进出两条更完整的落地路径:
1. 面向 Maven 的 DDD 脚手架(archetype 方案)
仓库的 DDD 脚手架【Maven 本地版】 与 DDD 脚手架【Maven 在线版】 两篇文档,展示了另一套脚手架实现机制:使用maven-archetype-plugin的archetype:create-from-project命令,把已经搭好的 DDD 工程逆向生成为脚手架模板,再通过mvn clean install安装到本地 Maven 仓库,之后在 IntelliJ IDEA 创建工程时选择 Maven Archetype 即可一键生成 DDD 四层架构工程。
这套 DDD 脚手架分两套形态:
- 轻量版(lite):以提供 HTTP 服务为主,缓存使用 Guava、任务使用 Quartz、消息使用 Spring/Guava 替代,适合中小业务场景。
- 标准版(std):综合使用分布式技术栈,Dubbo 提供 RPC 接口 + Nacos 作为注册中心、Redis 提供缓存与加锁、ShardingJdbc 提供分库分表、XXL-JOB 提供分布式任务调度、RocketMQ 提供异步消息,适合中大型场景。
值得注意的在线版实现细节是:脚手架本质上是一个 Jar 包,而 Maven 构建工程时产生的archetype-catalog.xml就是脚手架的定义文件,把它上传到可公网访问的域名根目录,再在 IDEA 中配置该地址,即可让脚手架以"在线目录"的方式被所有人使用。这与本文"手撸 FreeMarker 脚手架"殊途同归——目标都是把通用、共性的工程搭建动作沉淀为可复用的产物。
2. 面向 IDEA 插件的 DDD 脚手架(freemarker 方案)
仓库中 《IntelliJ IDEA 插件开发》第四节:扩展创建工程向导步骤,开发DDD脚手架 则把 FreeMarker 与 IDEA 插件能力结合,通过继承ModuleBuilder、ModuleWizardStep扩展创建工程向导,并使用ProjectTemplatesFactory注册自定义工程模板。其 domain 层核心服务同样基于 FreeMarker(AbstractProjectGenerator extends FreemarkerConfiguration),通过模板方法抽象出 DDD 分层结构(application、domain、infrastructure、interfaces)的创建流程。这恰好印证了本文脚手架方案的可扩展性——同一套"FTL 模板 + 数据模型"的思想,既可以用于 Web 接口方式创建工程,也可以内嵌到 IDE 中成为向导步骤。
3. 脚手架在实战项目中的落地
脚手架不只是玩具,它已经在仓库的实战项目中真实落地。例如 大营销平台系统 - 使用脚手架创建工程 中,就明确使用 DDD 脚手架完成工程创建并推送代码到仓库;Lottery 抽奖系统 - 搭建DDD四层架构 中,也指出"一般在互联网企业这部分工作可能不需要反复处理,只需要在承接产品需要后使用脚手架或者直接复制以往工程就可以创建现有需要使用的工程了",但由于需要基于 DDD 四层架构进行模块化拆分并引入 RPC 等分布式组件,才有了自建脚手架的必要。这正是本文开篇所述"公司通用脚手架不适配自研技术栈"的真实注脚。
六、总结
- 站在公司角度不重复造轮子是为了各部门职责和资源的成本,但对个人来说,不能因为一句不重复造轮子,就放弃了对知识栈深入学习的机会。
- 没有这些根基的学习,也压根不会理解技术的迁移、服务的提取、组件的凝练。反反复复的总是做一些 API 的应用包壳,对个人技术上也就没有什么成长。
- 哪怕公司不需要你造轮子,没关系,你可以造给自己,可以分享到社区。一方面是自己的学习汇总,另一方面也是对技术的沉淀和贡献。
核心要点回顾:本文通过EasyRiggerInitializr工程,完整演示了"应用层定义接口 → 领域层按模块实现生成逻辑 →resources/generator下定义 FTL 模板 →ProjectGeneratorImpl编排生成流程 → 单元测试验证产出"的脚手架开发全链路。这套方案的本质,是把 FreeMarker 模板引擎作为"元编程"工具,用模板 + 数据模型的方式把工程创建的共性操作程序化。后续无论是演进为 Maven Archetype 形式的 DDD 脚手架,还是内嵌到 IDEA 插件中,核心思想都是一致的:以模板化、配置化的方式,把企业技术栈沉淀为统一、可复用的工程生成能力。
- 文档
- 教程
- 后端
【免费下载链接】CodeGuide
:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助,请给予支持(关注、点赞、分享)!
相关推荐
SingGuard-4b-GGUF最佳实践:构建企业级AI安全防护系统的10个技巧
SingGuard 4b GGUF最佳实践:构建企业级AI安全防护系统的10个技巧 SingGuard 4b GGUF是一款策略自适应的多模态LLM安全防护模型
蛋白质复合物建模实战:使用RoseTTAFold预测蛋白质相互作用
蛋白质复合物建模实战:使用RoseTTAFold预测蛋白质相互作用 蛋白质 蛋白质相互作用(PPI)是生命活动的核心机制,而准确预测蛋白质复合物结构对理解疾病机
人工智能深度学习生物信息学科学计算终极指南:3分钟掌握Steam游戏数据提取的完整方法
终极指南:3分钟掌握Steam游戏数据提取的完整方法 想要快速获取Steam游戏信息却不知道从何入手?GetDataFromSteam SteamDB这款开源工
网页爬虫
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考