作者:小傅哥
博客:https://bugstack.cn
沉淀、分享、成长,让自己和他人都能有所收获!
一、前言
为什么我们要去造*?
造*的核心目的,是为了解决通用共性问题的凝练和复用。
虽然市面上已经有了大量成熟稳定用于支撑系统建设的*,也就是服务、框架、组件、工具等,但对于一些较大型的公司来说,这些*可能并不一定能很好的支撑起系统需要承载的服务体量,这个时候就需要自建一些*。
而提倡的不重复造*,新造*不一定能保证稳定性。一般用在以官网推出的核心*上是适合的,比如 SpringBoot、Netty、HBase 等。但对于一些特殊场景的解决方案工具型组件,通常是没有完全符合的*的,就像 SpringBoot 脚手架。
其实每个较大型的公司都会有很多同类技术服务的组件,例如 RPC、数据库路由、注册中心、分布式任务、MQ队列消息等,而这时候脚手架的开发就需要适配这些组件,搭建出符合自己公司技术栈实现需要的系统架构。这不同于一些较小的互联网公司,可以完全使用 SpringBoot 提供的一整套解决方案
另外,造*是个人技术沉淀、也是薪资待遇的积累!别说造不了飞机,只是你没有提供场地!
有什么场景还能造*?
用于架构基建下的所有模块都可以成为*,通常我们都是在这些场景下:负载均衡
、服务网关
、服务治理
、框架语言
、服务组件
、数据承载
、框架结构
、部署方式
、工具插件
,建设需要的*。
其实一个较成熟的互联网公司,大部分场景下的*,已基本建造完了。剩下的一般是用于解决业务场景下非业务逻辑的通用性组件,例如,高并发下的缓存热Key、Redis 层路由、活动邀请的不唯一短码生成,等等类似这样的场景。但此类场景的*建设也是非常有价值的,在公司层面使用稳定后,还可以推广到市场获得一定的认可,以及更好的会被收入到 Apache 项目。
二、什么是脚手架呢?
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 官网脚手架
- 推荐:
- 链接:https://start.spring.io
- 源码:https://github.com/spring-io/start.spring.io
- 描述:Spring Initializr 本质上也是一个 Web 应用,它可以通过 Web 界面、Spring Tool Suite、IntelliJ IDEA 等方式,构建出一个基本的 Spring Boot 项目结构。同时可以使用它的源码进行本地部署
2、阿里云脚手架
- 推荐:
- 链接:https://start.spring.io
- 描述: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 定义生成模板,其他地方就没有太大的差异了。
接下来简单介绍下这个工程的代码,让大家可以理解这样的工程是如何开发的,也可以通过这样工程继续完善成自己需要的结构。
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那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据, 而在模板之外可以专注于要展示什么数据。
- FreeMarker 在线手册:http://freemarker.foofun.cn
3.1 application.ftl
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
<?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
server:
port: 8081
以上,只是用于生成框架文件的基础 ftl 文件,有需要一些特殊判断和逻辑的,可以参考FreeMarker 在线手册,编写自己需要的 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
,这三方面。
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 四层描述文件
综上,就是整个脚手架生成的简要介绍,其实并没有多复杂,主要就是 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 下,这个路径也可以配置到其他路径里。
- 有了新生成的工程就可以通过 IDEA 打开了,与我们手动创建的工程是一样的。
五、源码下载
- 源码下载:https://github.com/fuzhengwei/EasyRiggerInitializr
- 项目介绍:SpringBoot 脚手架,简化项目构建。目前的项目工程还比较简单,非常适合新人学习使用。后续我们会在这个版本的基础上陆续完善一些功能,把RPC、MQ、注册中心、网关、等各类组件融合进来,方便选择性的构建和扩展。
六、总结
- 站在公司角度不重复造*是为了各部门职责和资源的成本,但对个人来说,不能因为一句不重复造*,就放弃了对知识栈深入学习的机会。
- 没有这些根基的学习,也压根不会理解技术的迁移、服务的提取、组件的凝练。反反复复的总是做一些 API 的应用包壳,对个人技术上也就没有什么成长。
- 最后说回来,哪怕公司不需要你造*,没关系,你可以造给自己,可以分享到 Github 社区。一方面是自己的学习汇总,另一方面也是对技术的沉淀和贡献。
七、系列推荐
- 方案设计:基于IDEA插件开发和字节码插桩技术,实现研发交付质量自动分析
- 技术扫盲:关于低代码编程的可持续性交付设计和分析
- 工作两三年了,整不明白架构图都画啥?
- 互联网架构的演变过程
- 领域驱动设计架构基于SpringCloud搭建微服务
看了 Spring 官网脚手架真香,也撸一个 SpringBoot DDD 微服务的脚手架!的更多相关文章
-
真香,撸一个SpringBoot在线代码修改器
前言 项目上线之后,如果是后端报错,只能重新编译打包部署然后重启:如果仅仅是前端页面.样式.脚本修改,只需要替换到就可以了. 小公司的话可能比较*,可以随意替换,但是有些公司权限设置的比较严格,需要 ...
-
Spring官网阅读(十八)Spring中的AOP
文章目录 什么是AOP AOP中的核心概念 切面 连接点 通知 切点 引入 目标对象 代理对象 织入 Spring中如何使用AOP 1.开启AOP 2.申明切面 3.申明切点 切点表达式 excecu ...
-
Spring官网阅读(十七)Spring中的数据校验
文章目录 Java中的数据校验 Bean Validation(JSR 380) 使用示例 Spring对Bean Validation的支持 Spring中的Validator 接口定义 UML类图 ...
-
Spring官网阅读(十六)Spring中的数据绑定
文章目录 DataBinder UML类图 使用示例 源码分析 bind方法 doBind方法 applyPropertyValues方法 获取一个属性访问器 通过属性访问器直接set属性值 1.se ...
-
Spring官网阅读(十三)ApplicationContext详解(下)
文章目录 BeanFactory 接口定义 继承关系 接口功能 1.HierarchicalBeanFactory 2.ListableBeanFactory 3.AutowireCapableBea ...
-
Spring官网阅读(二)(依赖注入及方法注入)
上篇文章我们学习了官网中的1.2,1.3两小节,主要是涉及了容器,以及Spring实例化对象的一些知识.这篇文章我们继续学习Spring官网,主要是针对1.4小节,主要涉及到Spring的依赖注入.虽 ...
-
Spring官网阅读(三)自动注入
上篇文章我们已经学习了1.4小结中关于依赖注入跟方法注入的内容.这篇文章我们继续学习这结中的其他内容,顺便解决下我们上篇文章留下来的一个问题-----注入模型. 文章目录 前言: 自动注入: 自动注入 ...
-
Spring官网阅读(一)容器及实例化
从今天开始,我们一起过一遍Spring的官网,一边读,一边结合在路神课堂上学习的知识,讲一讲自己的理解.不管是之前关于动态代理的文章,还是读Spring的官网,都是为了之后对Spring的源码做更全面 ...
-
Spring官网下载dist.zip的几种方法
Spring官网下载dist.zip的几种方法 Spring官网改版后,很多项目的完整zip包下载链接已经隐掉了,虽然Spring旨在引导大家用更“高大上”的maven方式来管理所依赖的jar包, ...
随机推荐
-
WP7、WP8 格式化时间为距当前多少时间
方法一: 使用 toolkit的 RelativeTimeConverter,使用方式 <phone:PhoneApplicationPage.Resources> <toolkit ...
-
point\polyline\polygon的转化(转)
首先你要明白Polyline是由path对象构成,Polygon是由ring对象构成,因此实现polyline向polygon的转换,思路如下:1.提取polyline中的所有path对象2.将pat ...
-
Java中正则表达式的使用
public class Test{ public static void main(String args[]) { String str="@Shang Hai Hong Qiao Fe ...
-
php array_combine 把array的默认键改成指定的字符串
array(2) { [0] => array(6) { [0] => string(1) "1" [1] => string(10) "1470650 ...
-
修改UISearchBar placeholder textColor
[[UILabel appearanceWhenContainedIn:[UISearchBar class], nil] setTextColor:[UIColor redColor]];
-
sql 成绩表 case then
select * from dbo.tb_Scroe select Name,(select count(*) from tb_Scroe where Name = t.Name and Scroe ...
-
在WINDOWS中安装使用SIGPACK(MinGW64+Sublime Text3 &;Visual Studio)
本文介绍在Windows下安装使用SigPack库,涉及Armadillo.OpenBLAS安装使用说明,最终实现对SigPack示例CPP基于MinGW64在Sublime Text3下的编译运行, ...
-
groupadd语法
groupadd [选项] 组 创建一个新的组.Groupadd命令使用命令行中指定的值加上系统默认值创建新的组账户.新组将根据需要输入系统. (1).选项 1 2 3 4 5 6 7 -f,--fo ...
-
JavaScript对象之深度克隆
也不知道从什么时候开始,前端圈冒出了个新词:对象深度克隆.看起来好像很高大上的样子,实际上并不新鲜,在我们的实际项目开发中,你可能早已用到,只不过由于汉字的博大精深,有些原本很简单的事物被一些看似专业 ...
-
GregorianCalendar公里类
使用GregorianCalendar 获得日期是年中第几周第几天 public class Common { /// <summary> /// 获取日期是年中的第几周 /// < ...