基於IDEA Plugin插件開發,擼一個DDD腳手架
作者:小傅哥
部落格://bugstack.cn
沉澱、分享、成長,讓自己和他人都能有所收穫!😄
- 最近很感興趣結合 IDEA Plugin 開發能力,擴展各項功能。也基於此使用不同的案例,探索 IDEA Plugin 插件開發技術。希望這樣的成體系學習和驗證總結,能給更多需要此技術的夥伴,帶來幫助。
- 源碼地址://github.com/fuzhengwei/CodeGuide#1-%E6%BA%90%E7%A0%81
一、前言
研發,要避免自嗨!
你做這個東西的價值是什麼?有競品調研嗎?能賦能業務嗎?那不已經有同類的了,你為什麼還自己造輪子?
你是不是也會被問到這樣的問題,甚至可能還有些頭疼。但做的時候挺嗨,研究技術嘛,還落地了,多刺激。不過要說價值,好像一時半會還體現不出來,能不能賦能業務就不更不一定了。
可誰又能保證以後不能呢,技術的點是一個個攻克嘗試的才有機會再深度學習後把這些內容連成一片,就像單說水、單說沙子、單說泥巴,好像並沒有啥用,但把它們湊到一塊再給把火,就燒成了磚,磚就碼成了牆,牆就蓋成房。
二、需求目的
我們這一章節把 freemarker 能力與 IDEA Plugin 插件能力結合,開發一個DDD 腳手架 IDEA 插件
,可能你會想為什麼要把腳手架開發到插件里呢?還有不是已經有了成型的腳手架可以用嗎?
首先我們目前看到的腳手架基本都是網頁版的,也就是一次性創建工程使用,不過在我們實際使用的時候,還希望在工程創建過程中把資料庫、ES、Redis等生成對應的 ORM 程式碼,減少開發工作量。並且在使用的工程骨架的過程中,還希望可以隨著開發需要再次補充新的功能進去,這個時候網頁版的腳手架都不能很好的支援了。此外一些大廠都會自己的技術體系,完全是使用市面的腳手架基本很難滿足自身的需求,所以就需要有一個符合自己場景的腳手架了。
那麼,我們本章節就把腳手架的開發放到 IDEA 插件開發中,一方面學習腳手架的建設,另外一方面學習如何改變工程嚮導,創建出自己需要的DDD結構腳手架。
三、案例開發
1. 工程結構
guide-idea-plugin-scaffolding
├── .gradle
└── src
├── main
│ └── java
│ └── cn.bugstack.guide.idea.plugin
│ ├── domain
│ │ ├── model
│ │ │ └── ProjectConfigVO.java
│ │ └── service
│ │ ├── impl
│ │ │ └── ProjectGeneratorImpl.java
│ │ ├── AbstractProjectGenerator.java
│ │ ├── FreemarkerConfiguration.java
│ │ └── IProjectGenerator.java
│ ├── factory
│ │ └── TemplateFactory.java
│ ├── infrastructure
│ │ ├── DataSetting.java
│ │ ├── DataState.java
│ │ ├── ICONS.java
│ │ └── MsgBundle.java
│ ├── module
│ │ ├── DDDModuleBuilder.java
│ │ └── DDDModuleConfigStep.java
│ └── ui
│ ├── ProjectConfigUI.java
│ └── ProjectConfigUI.form
├── resources
│ ├── META-INF
│ │ └── plugin.xml
│ └── template
│ ├── pom.ftl
│ └── yml.ftl
├── build.gradle
└── gradle.properties
源碼獲取:#公眾號:bugstack蟲洞棧
回復:idea
即可下載全部 IDEA 插件開發源碼
在此 IDEA 插件工程中,主要分為5塊區域:
- domain:領域層,提供創建 DDD 模板工程的服務,其實這部分主要使用的就是 freemarker
- factory:工廠層,提供工程創建模板,這一層的作用就是我們在 IDEA 中創建新工程的時候,可以添加上我們自己的內容,也就是創建出我們定義好的 DDD 工程結構。
- infrastructure:基礎層,提供數據存放、圖片載入、資訊映射這些功能。
- module:模組層,提供 DDD 模板工程的創建具體操作和步驟,也就是說我們創建工程的時候是一步步選擇的,你可以按需添加自己的步驟頁面,允許用戶選擇和添加自己需要的內容。比如你需要連庫、選擇表、添加工程所需要的技術棧等
- ui:介面層,提供Swing 開發的 UI 介面,用於用戶圖形化選擇和創建。
2. UI 工程配置窗體
public class ProjectConfigUI {
private JPanel mainPanel;
private JTextField groupIdField;
private JTextField artifactIdField;
private JTextField versionField;
private JTextField packageField;
}
- 使用 Swing UI Designer 創建一個配置工廠資訊的 UI 窗體,通過這樣的方式創建可以直接拖拽。
- 在這個 UI 窗體中我們主要需要;
roupId
、artifactId
、version
、package
3. 配置工程步驟創建
3.1 數據存放
cn.bugstack.guide.idea.plugin.infrastructure.DataSetting
@State(name = "DataSetting",storages = @Storage("plugin.xml"))
public class DataSetting implements PersistentStateComponent<DataState> {
private DataState state = new DataState();
public static DataSetting getInstance() {
return ServiceManager.getService(DataSetting.class);
}
@Nullable
@Override
public DataState getState() {
return state;
}
@Override
public void loadState(@NotNull DataState state) {
this.state = state;
}
public ProjectConfigVO getProjectConfig(){
return state.getProjectConfigVO();
}
}
- 在基礎層提供數據存放的服務,把創建工程的配置資訊存放到服務中,這樣比較方便設置和獲取。
3.2 擴展步驟
cn.bugstack.guide.idea.plugin.module.DDDModuleConfigStep
public class DDDModuleConfigStep extends ModuleWizardStep {
private ProjectConfigUI projectConfigUI;
public DDDModuleConfigStep(ProjectConfigUI projectConfigUI) {
this.projectConfigUI = projectConfigUI;
}
@Override
public JComponent getComponent() {
return projectConfigUI.getComponent();
}
@Override
public boolean validate() throws ConfigurationException {
// 獲取配置資訊,寫入到 DataSetting
ProjectConfigVO projectConfig = DataSetting.getInstance().getProjectConfig();
projectConfig.set_groupId(projectConfigUI.getGroupIdField().getText());
projectConfig.set_artifactId(projectConfigUI.getArtifactIdField().getText());
projectConfig.set_version(projectConfigUI.getVersionField().getText());
projectConfig.set_package(projectConfigUI.getPackageField().getText());
return super.validate();
}
}
- 繼承
ModuleWizardStep
開發一個自己需要的步驟,這個步驟就會出現到我們創建新的工程中。 - 同時在重寫的 validate 方法中,把從工程配置 UI 窗體中獲取到資訊,寫入到數據配置文件中。
3.3 配置步驟
cn.bugstack.guide.idea.plugin.module.DDDModuleBuilder
public class DDDModuleBuilder extends ModuleBuilder {
private IProjectGenerator projectGenerator = new ProjectGeneratorImpl();
@Override
public Icon getNodeIcon() {
return ICONS.SPRING_BOOT;
}
/**
* 重寫 builderId 掛載自定義模板
*/
@Nullable
@Override
public String getBuilderId() {
return getClass().getName();
}
@Override
public ModuleWizardStep[] createWizardSteps(@NotNull WizardContext wizardContext, @NotNull ModulesProvider modulesProvider) {
// 添加工程配置步驟,可以自己定義需要的步驟,如果有多個可以依次添加
DDDModuleConfigStep moduleConfigStep = new DDDModuleConfigStep(new ProjectConfigUI());
return new ModuleWizardStep[]{moduleConfigStep};
}
}
- 在 createWizardSteps 方法中,把我們已經創建好的
DDDModuleConfigStep
添加工程配置步驟,可以自己定義需要的步驟,如果有多個可以依次添加。 - 同時需要注意,只有重寫了 getBuilderId() 方法後,你新增加的嚮導步驟才能生效。
4. 開發腳手架服務
cn.bugstack.guide.idea.plugin.domain.service.AbstractProjectGenerator
public abstract class AbstractProjectGenerator extends FreemarkerConfiguration implements IProjectGenerator {
@Override
public void doGenerator(Project project, String entryPath, ProjectConfigVO projectConfig) {
// 1.創建工程主POM文件
generateProjectPOM(project, entryPath, projectConfig);
// 2.創建四層架構
generateProjectDDD(project, entryPath, projectConfig);
// 3.創建 Application
generateApplication(project, entryPath, projectConfig);
// 4. 創建 Yml
generateYml(project, entryPath, projectConfig);
// 5. 創建 Common
generateCommon(project, entryPath, projectConfig);
}
}
- 在 domain 領域層添加用於創建腳手架框架的 FreeMarker 服務,它是一款 模板引擎: 即一種基於模板和要改變的數據, 並用來生成輸出文本(HTML網頁,電子郵件,配置文件,源程式碼等)的通用工具。FreeMarker 在線手冊://freemarker.foofun.cn
- 按照 DDD 工程結構,分層包括:application、domain、infrastructure、interfaces,那麼我們把這些創建過程抽象到模板方法中,具體交給子類來創建。
5. 調用腳手架服務
cn.bugstack.guide.idea.plugin.module.DDDModuleBuilder
public class DDDModuleBuilder extends ModuleBuilder {
private IProjectGenerator projectGenerator = new ProjectGeneratorImpl();
@Override
public Icon getNodeIcon() {
return ICONS.SPRING_BOOT;
}
@Override
public void setupRootModel(@NotNull ModifiableRootModel rootModel) throws ConfigurationException {
// 設置 JDK
if (null != this.myJdk) {
rootModel.setSdk(this.myJdk);
} else {
rootModel.inheritSdk();
}
// 生成工程路徑
String path = FileUtil.toSystemIndependentName(Objects.requireNonNull(getContentEntryPath()));
new File(path).mkdirs();
VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(path);
rootModel.addContentEntry(virtualFile);
Project project = rootModel.getProject();
// 創建工程結構
Runnable r = () -> new WriteCommandAction<VirtualFile>(project) {
@Override
protected void run(@NotNull Result<VirtualFile> result) throws Throwable {
projectGenerator.doGenerator(project, getContentEntryPath(), DataSetting.getInstance().getProjectConfig());
}
}.execute();
}
}
- 在
DDDModuleBuilder#setupRootModel
中,添加創建DDD工程框架
的服務,projectGenerator.doGenerator(project, getContentEntryPath(), DataSetting.getInstance().getProjectConfig());
- 另外這裡需要用到 IDEA 提供的執行緒調用方法,
new WriteCommandAction
才能正常創建。
6. 配置模板工程
6.1 模板工廠
cn.bugstack.guide.idea.plugin.factory.TemplateFactory
public class TemplateFactory extends ProjectTemplatesFactory {
@NotNull
@Override
public String[] getGroups() {
return new String[]{"DDD腳手架"};
}
@Override
public Icon getGroupIcon(String group) {
return ICONS.DDD;
}
@NotNull
@Override
public ProjectTemplate[] createTemplates(@Nullable String group, WizardContext context) {
return new ProjectTemplate[]{new BuilderBasedTemplate(new DDDModuleBuilder())};
}
}
- 模板工廠的核心在於把我們用於創建
DDD 的步驟
添加createTemplates
方法中,這樣算把整個創建自定義腳手架工程的鏈路就串聯完成了。
6.2 文件配置
plugin.xml
<idea-plugin>
<id>cn.bugstack.guide.idea.plugin.guide-idea-plugin-scaffolding</id>
<name>Scaffolding</name>
<vendor email="[email protected]" url="//bugstack.cn">小傅哥</vendor>
<!-- please see //www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
on how to target different products -->
<depends>com.intellij.modules.platform</depends>
<extensions defaultExtensionNs="com.intellij">
<projectTemplatesFactory implementation="cn.bugstack.guide.idea.plugin.factory.TemplateFactory"/>
<applicationService serviceImplementation="cn.bugstack.guide.idea.plugin.infrastructure.DataSetting"/>
</extensions>
</idea-plugin>
- 接下來還需要把我們創建的工程模板以及數據服務配置到
plugin.xml
中,這樣在插件啟動的時候就可以把我們自己插件啟動起來了。
四、測試驗證
- 點擊
Plugin
啟動 IDEA 插件,之後創建工程如下:
- 快拿去試試吧,啟動插件,點擊創建工程,傻瓜式點擊,就可以創建出一個 DDD 工程結構了。
五、總結
- 學習使用 IDEA Plugin 開發技術,改變創建工程嚮導,添加自己需要的工程創建模板,這樣就可以創建出一個 DDD 腳手架工程骨架了,接下來你還可以結合自己實際的業務場景添加自己需要的一些技術棧到腳手架中。
- 如果你願意嘗試可以在工程創建中鏈接到資料庫,把資料庫中對應的表生成Java程式碼,這樣一些簡單的配置、查詢、映射,就不用自己動手寫了。
- 在開發 DDD 腳手架的源碼中還有一些細節過程,包括圖標的展示、文案的資訊、Freemarker的使用細節,這些你都可以在源碼中學習並調試驗證。
六、系列推薦
- 使用 Freemarker,創建 SpringBoot 腳手架
- 發布Jar包到Maven中央倉庫(為開發開源中間件做準備)
- DDD 領域層決策規則樹服務設計
- 工作兩三年了,整不明白架構圖都畫啥?
- CodeGuide Github 倉庫開源啦!