在这个微服务化为主流的阶段,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.spring4allgroupId>
<artifactId>swagger-spring-boot-starterartifactId>
<version>1.9.0.RELEASEversion>
dependency>
<dependency>
<groupId>com.github.xiaoymingroupId>
<artifactId>swagger-bootstrap-uiartifactId>
<version>1.9.3version>
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文档就完成了
点击相应的类可以进入修改页面
在这里可以对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);
}
以上俩个类就是本工具的核心了,所有功能都是基于以上俩个类做的扩展,大家也可以根据需要做一些定制化的功能
因为是试用版本,肯定有很多不足之处,也欢迎大家留下宝贵的意见, 后续我会持续改进