zoukankan      html  css  js  c++  java
  • domain代码生成修改工具,批量生成swagger、javax.validation注解,减少重复工作

    在这个微服务化为主流的阶段,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的物理地址, 然后点击载入


    等上一秒钟,然后就可以看到效果了


    再看看 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进行合并管理,方便大家导入使用

    看下生成后的效果


    至此一个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("
    ")));
                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);
    
    }
    

    以上俩个类就是本工具的核心了,所有功能都是基于以上俩个类做的扩展,大家也可以根据需要做一些定制化的功能

    因为是试用版本,肯定有很多不足之处,也欢迎大家留下宝贵的意见, 后续我会持续改进

  • 相关阅读:
    django学习笔记(一)
    Python之函数进阶
    Python之函数初识
    Python之文件操作
    基础补充
    字典
    神奇的列表
    万能的字符串
    Python基础(二)
    使用paramiko进行ssh连接
  • 原文地址:https://www.cnblogs.com/fangh816/p/13437778.html
Copyright © 2011-2022 走看看