在這個微服務化爲主流的階段,domain的重要性不言而喻,他即體現了一個服務的業務能力,又是服務間通信的契約,
市面上已經有一些很成熟的domain代碼生成工具,比如Mybatis-Generator, 但是基本沒看到可以支撐domain進行二次代碼修改的自動化工具.
這在很多場景下增加了額外的開發工作量
例如前後端分離,很多小夥伴會選擇swagger作爲api文檔生成工具,但這也意味着我們需要對原有的domain進行較大的修改,添加swagger註解.
例如在業務不清晰多變的場景,尤其是前端業務多變,增/減字段,後端小夥伴也要批量去修改相應的Rquest、VO
針對以上等現象,我開發了一個小工具https://github.com/zhaojun123/domainmanage.git
爲了方便演示效果,這裏創建一個測試項目(domain_test) 模擬前後端分離
創建目錄結構 controller, request(入參),vo(出參)
pom.xml 添加swagger支持
<dependency>
<groupId>com.spring4all</groupId>
<artifactId>swagger-spring-boot-starter</artifactId>
<version>1.9.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.3</version>
</dependency>
DomainTestApplication.java 啓用swagger
@EnableSwagger2Doc
@SpringBootApplication
@EnableSwaggerBootstrapUI
public class DomainTestApplication {
public static void main(String[] args) {
SpringApplication.run(DomainTestApplication.class, args);
}
}
UserController.java 添加@Api、@ApiOperation註解
@Api(tags = "用戶模塊")
@RestController
public class UserController {
@ApiOperation("添加用戶")
@PostMapping("/addUser")
public String addUser(@RequestBody AddUserForm addUserForm){
return null;
}
@ApiOperation("用戶查詢")
@GetMapping("/detail")
public UserDetailVO detail(String userId){
return null;
}
}
啓動項目 訪問http://localhost:9999/doc.html
點擊添加用戶菜單,可以看到swagger已經幫我們寫好了請求示例的json結構
但是並沒有相應的參數說明和示例數據, 所以這隻能算半成品,交出去會被前端小夥伴揍的
針對於這個例子,我們還需要完善AddUserForm 在每個field上添加@ApiModelProperty註解
AddUserForm
/**
* 添加用戶模型
*/
public class AddUserForm {
/**
* 賬號
*/
private String account;
/**
* 密碼
*/
private String password;
/**
* 年齡
*/
private Integer age;
/**
* 性別 男、女
*/
private String sex;
/**
* 姓名
*/
private String userName;
/**
* 添加所屬單位
*/
private OrganForAdd organ;
//省略get set
}
/**
* 添加單位模型
*/
public class OrganForAdd {
/**
* 單位名稱
*/
private String organName;
/**
* 單位地址
*/
private String organAddress;
/**
* 單位地區
*/
private String area;
//省略get set
}
這工作量就多了不少,如果這樣的domain有十個 百個,那一天寶貴的時間就全部去寫swagger註解了
接下來我們看下自動化工具是如何解決這個問題的,啓動domainmanage項目,訪問地址
http://localhost:8888/domainManage/domain
複製我們需要改造的doamin的物理地址, 然後點擊載入
勾選AddUserForm、OrganForAdd, 然後點擊批量生成Swagger註解
等上一秒鐘,然後就可以看到效果了
再看看 http://localhost:9999/doc.html
發現field相對應的參數說明已經有了,但是@ApiModelProperty註解裏面的example依然爲空,這要一個個寫的話依然很麻煩, 而且其中還包含了大量重複的field, 例如常用的userId、organId、userName等
我們還是需要用工具解決這個問題, 點擊設置example選項卡
因爲我們是第一次使用這個功能,所以顯示的是空的列表, 點擊快速生成example
這個功能會分析你載入的domain,提取所有的field,並且根據fieldType類型以及是否重複做過濾,最終會生成一個列表, 可以在這裏快速的填寫example
填寫的數據會保存在你當前的工作目錄下${user.dir}/example.properties
也可以通過導入功能,導入別人寫好的example.properties,該操作是增量操作,不會覆蓋你本地已寫好的example,所以任何情況都可以大膽的導入
可以通過git 、svn等將example.properties進行合併管理,方便大家導入使用
填寫好了example 我們需要先點擊清除swagger註解,再重新生成swagger註解, 這是爲了防止將你手動修改過的代碼覆蓋了
看下生成後的效果
至此一個api文檔就完成了
自動生成javax.validation註解
點擊相應的類可以進入修改頁面
在這裏可以對field字段添加validation註解,目前只做了@NotNull 、@NotEmpty、NotBlank,大家也可以根據需要自己做擴展,
已經生成好相應的錯誤提示,只需要勾選相應的註解, 點擊修改即可
其它功能
該工具還包括
批量添加/刪除 field、 查詢 、排序 、field字典 、代碼查看等小功能, 主要作用還是方便使用, 也可以根據自身需要在這個基礎上做擴展
部分代碼解析
該工具的原理是讀取java文件, 根據正則表達式解析相應的 package、import、class、field、method、annotation、註釋 , 然後根據需要對其進行相應的增刪改,再重新寫回java文件
Java代碼解析類com.zkml.domainmanage.support.metadata.DomainDelegate
/**
* 逐行讀取內容,進行正則匹配處理
*/
public DomainMetadata init(){
String content = domainMetadata.getContent();
if(!StringUtils.isBlank(content)){
contentList = Collections.unmodifiableList(Arrays.asList(content.split("\n")));
domainMetadata.setContentList(contentList);
}
for(lineIndex=0;lineIndex<contentList.size();lineIndex++){
String contentLine = contentList.get(lineIndex).trim();
if(analysisPackage(contentLine))
continue;
if(analysisImport(contentLine))
continue;
if(analysisNote(contentLine))
continue;
if(analysisAnnotation(contentLine))
continue;
if(analysisClass(contentLine))
continue;
if(analysisField(contentLine))
continue;
if(analysisMethod(contentLine))
continue;
}
domainMetadata.setImportList(Collections.unmodifiableList(importList));
domainMetadata.setFieldMetadataList(Collections.unmodifiableList(fieldList));
domainMetadata.setMethodMetadataList(Collections.unmodifiableList(gsMethodList));
return domainMetadata;
}
解析後會生成相應的屬性模型
其中 swaggerMetadata、validationMetadata是根據業務需要做的擴展
com.zkml.domainmanage.support.metadata.DomainMetadataHandle
對field、method、class等屬性進行增刪減操作的接口,所有業務邏輯只要涉及到修改java代碼都會調用這個接口
默認實現類是
com.zkml.domainmanage.support.metadata.DefaultDomainMetadataHandle
public interface DomainMetadataHandle {
/**
* 初始化DomainMetadata,這裏傳入的DomainMetadata包含localPath、fileName,content、className
* 需要解析fieldMetadataList、methodMetadataList、importList、classNote、fullClassName
* @param domainMetadata
*/
DomainMetadata init(DomainMetadata domainMetadata);
/**
* 刪除field
* @param fieldMetadata 需要刪除的fieldMetadata
* @param domainMetadata field所屬的domainMetadata
* @param status ALL,GET,SET,NONE 是否對get/set方法進行操作
*/
void delete(FieldMetadata fieldMetadata, DomainMetadata domainMetadata,FieldMetadata.Status status);
/**
* 批量刪除field
* @param fieldMetadataList
* @param domainMetadata
* @param status
*/
void delete(List <FieldMetadata> fieldMetadataList, DomainMetadata domainMetadata,FieldMetadata.Status status);
/**
* 添加field
* @param fieldMetadata 包含fieldContent getMethodContent,setMethodContent,sortNo,status
* @param domainMetadata field所屬的domainMetadata
* @param status ALL,GET,SET,NONE 是否對get/set方法進行操作
*/
void add(FieldMetadata fieldMetadata,DomainMetadata domainMetadata,FieldMetadata.Status status);
/**
* 批量添加field
* @param fieldMetadataList
* @param domainMetadata
* @param status
*/
void add(List<FieldMetadata> fieldMetadataList,DomainMetadata domainMetadata,FieldMetadata.Status status);
/**
* 對class的 annotation note進行修改
* @param classMetadata
* @param domainMetadata
*/
void update(ClassMetadata classMetadata,DomainMetadata domainMetadata);
/**
* 修改field
* @param fieldMetadata
* @param domainMetadata field所屬的domainMetadata
* @param status ALL,GET,SET,NONE 是否對get/set方法進行操作
*/
void update(FieldMetadata fieldMetadata,DomainMetadata domainMetadata,FieldMetadata.Status status);
/**
* 批量修改field
* @param fieldMetadataList
* @param domainMetadata
* @param status
*/
void update(List<FieldMetadata> fieldMetadataList,DomainMetadata domainMetadata,FieldMetadata.Status status);
/**
* 批量添加import
* @param importList
* @param domainMetadata
*/
void add(List<OtherMetadata> importList,DomainMetadata domainMetadata);
/**
* 批量刪除import
* @param importList
* @param domainMetadata
*/
void delete(List<OtherMetadata> importList,DomainMetadata domainMetadata);
}
以上倆個類就是本工具的核心了,所有功能都是基於以上倆個類做的擴展,大家也可以根據需要做一些定製化的功能
因爲是試用版本,肯定有很多不足之處,也歡迎大家留下寶貴的意見, 後續我會持續改進