Java-代码生成器的实现

文章目录

  • 前言
  • 一、概述
  • 二、手写代码
    • 1. 简要说明
    • 2. 代码编写
    • 3. 完整代码
    • 4. 测试效果
  • 三、项目源码


前言

最近看了一个开源的项目,jfinal-layui,然后这个项目里面有一个 代码生成器 的功能

在这里插入图片描述

之前虽然有用过代码生成器,但是从来没有看过相关的源码,所以就研究了一下,个人感觉这个项目的代码生成器还是蛮好的,能够根据指定的数据库和表生成前后端的代码,不过这个项目的框架是 jfinal ,直接把这个代码生成器相关的代码拷到自己的项目当中是运行不起来,而且每个项目的结构都存在一些特有的性质,很难找到一个拿来就能用的代码生成器,介于这一点,我就根据自己项目的架构,利用 jfinal 框架造的轮子,写了一个较为简单代码生成器,同时分享一下我是怎样实现的。

倘如各位想研究下 jfinal-layui 这个项目,可以点击 https://gitee.com/QinHaiSenLin/Jfinal-layui 这个链接去拉取这个项目的源码。


一、概述

代码生成器顾名思义就是一个生成代码的软件,在日常开发中我们可以使用代码生成器来减少一些重复的开发工作,提高开放效率,像我们经常使用的 mybatis 就有一个逆向工程,它能自动帮我们生产数据库对应的实体类,mapper 和 xml 文件等,其实就是一个代码生成器。

那代码生成器的原理是什么呢?

其实很简单,我们要生成的文件肯定是得有一定的规律,然后我们根据这个规律去编写一个统一的模板,按照这个模板去生成相应的代码。


二、手写代码


1. 简要说明

以下内容仅供参考,我提供的只是一种实现方案,可能并不是适用于你的项目,但是只要你理解了我是怎么实现的,你也可以编写一个适合你自己项目的代码生成器。

脱离实际场景的业务没什么意义,前面我也说过,任何项目都有它的独特性,我是根据自己的项目来编写一个符合我要求的代码生成器,所以我先说下我自己的这个项目简单的一个架构。

比如说我的这个项目,要实现一个完整的增删改查的一套的接口,需要有以下几个类的存在,其中红色框框选的部分(实体类 User.java、mapper 文件 UserMapper.java 和 xml 文件 UserMapper.xml)都可以通过 mybatis 的逆向工程生成,而绿色框框选的部分是需要我手动去编写的,所以我需要写的代码生成器是能够帮我去生成绿色框框选部分的文件,比如: Controller ServiceServiceImp请求体 dto相应体 dto 以及 subclass 包下的转换器

在这里插入图片描述

关于我这个项目具体的搭建可查看这篇博客:SpringBoot - 快速搭建

那我该如何去实现了,首先我建议你自己写一个标准的增删改查,然后对照你写的东西,去写一个模板,比如我写了一个岗位的 Controller ,代码如下:

package com.duojiala.mikeboot.controller;

import com.duojiala.mikeboot.domain.req.QueryPostListReq;
import com.duojiala.mikeboot.domain.req.SavePostReq;
import com.duojiala.mikeboot.domain.dto.ResponseBean;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import java.util.List;
import com.duojiala.mikeboot.service.PostService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
 * 顶部导航 Controller
 * @author: mike
 * @date: 2023-04-26 17:03
 */
@Slf4j
@RestController
@RequestMapping(value = "/post")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@CrossOrigin(origins = "*", methods = {RequestMethod.POST, RequestMethod.GET})
public class PostController {

    private final PostService postService;

    @PostMapping(value = "/save")
    @ApiOperation(value = "保存")
    public ResponseBean save(@RequestBody @Validated SavePostReq req) {
        postService.save(req);
        return ResponseBean.success();
    }

    @PostMapping(value = "/update")
    @ApiOperation(value = "修改")
    public ResponseBean update(@RequestBody @Validated SavePostReq req) {
        postService.update(req);
        return ResponseBean.success();
    }

    @PostMapping(value = "/detail")
    @ApiOperation(value = "查询详情")
    public ResponseBean detail(@RequestParam(required = false) String id) {
        return ResponseBean.success(postService.detail(id));
    }

    @PostMapping(value = "/delete")
    @ApiOperation(value = "删除")
    public ResponseBean delete(@RequestParam(required = false) String id) {
        postService.delete(id);
        return ResponseBean.success();
    }

    @PostMapping(value = "/batch-delete")
    @ApiOperation(value = "批量删除")
    public ResponseBean batchDelete(@RequestParam(required = false) List<String> ids) {
        postService.batchDelete(ids);
        return ResponseBean.success();
    }

    @PostMapping(value = "/paginate-list")
    @ApiOperation(value = "分页查询列表")
    public ResponseBean paginateList(@RequestBody @Validated QueryPostListReq req) {
        return ResponseBean.success(postService.paginateList(req));
    }


}

所以我写得模板文件 controller_template.jf 就是这样的

package #(controllerPackagePath);

#if(isCRUD)
import com.duojiala.mikeboot.domain.req.#(QueryTargetListReq??);
import com.duojiala.mikeboot.domain.req.#(SaveTargetReq??);
import com.duojiala.mikeboot.domain.dto.ResponseBean;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.annotation.Validated;
import java.util.List;
#end
import com.duojiala.mikeboot.service.#(ServiceName??);
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
 * #(controllerRemark??) Controller
 * @author: #(author?? 'JBolt-Generator')
 * @date: #date()
 */
@Slf4j
@RestController
@RequestMapping(value = "/#(pathValue??)")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@CrossOrigin(origins = "*", methods = {RequestMethod.POST, RequestMethod.GET})
public class #(ControllerName??) {

    private final #(ServiceName??) #(serviceName??);

#if(isCRUD)
    @PostMapping(value = "/save")
    @ApiOperation(value = "保存")
    public ResponseBean save(@RequestBody @Validated #(SaveTargetReq??) req) {
        #(serviceName??).save(req);
        return ResponseBean.success();
    }

    @PostMapping(value = "/update")
    @ApiOperation(value = "修改")
    public ResponseBean update(@RequestBody @Validated #(SaveTargetReq??) req) {
        #(serviceName??).update(req);
        return ResponseBean.success();
    }

    @PostMapping(value = "/detail")
    @ApiOperation(value = "查询详情")
    public ResponseBean detail(@RequestParam(required = false) String id) {
        return ResponseBean.success(#(serviceName??).detail(id));
    }

    @PostMapping(value = "/delete")
    @ApiOperation(value = "删除")
    public ResponseBean delete(@RequestParam(required = false) String id) {
        #(serviceName??).delete(id);
        return ResponseBean.success();
    }

    @PostMapping(value = "/batch-delete")
    @ApiOperation(value = "批量删除")
    public ResponseBean batchDelete(@RequestParam(required = false) List<String> ids) {
        #(serviceName??).batchDelete(ids);
        return ResponseBean.success();
    }

#if(needPaginate)
    @PostMapping(value = "/paginate-list")
    @ApiOperation(value = "分页查询列表")
    public ResponseBean paginateList(@RequestBody @Validated #(QueryTargetListReq??) req) {
        return ResponseBean.success(#(serviceName??).paginateList(req));
    }
#else
    @PostMapping(value = "/list")
    @ApiOperation(value = "查询列表")
    public ResponseBean list(@RequestBody @Validated #(QueryTargetListReq??) req) {
        return ResponseBean.success(#(serviceName??).list(req));
    }
#end
#end


}

这是通过自己项目中常写的代码,然后将公用的地方保留起来,结合一些模板引擎的语法将有差异的地方替换掉就行了,这里暂时看不懂也没关系(模板引擎的语法在写这个代码生成器时我也不懂,不过看别人写的对比下还是能写出来的)

最终效果


2. 代码编写

因为是借了 jfinal 的轮子,所以这里我有引用到 jfinal 的一个依赖

        <!--JFianl-->
        <dependency>
            <groupId>com.jfinal</groupId>
            <artifactId>jfinal</artifactId>
            <version>5.0.4</version>
        </dependency>

还有就是一些工具类(这些根据你自己项目来选择)

        <!-- 转化器自动转换 -->
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>1.4.2.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.4.2.Final</version>
        </dependency>

        <!-- 分页插件 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.12</version>
        </dependency>

        <!--工具类-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.18</version>
        </dependency>

然后我还写了一个枚举类,希望每插入一个表,就能往这个枚举类中添加一个该表的枚举

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 表名枚举
 */
@Getter
@AllArgsConstructor
public enum TableEnum {
    USER("用户表"),
    ;

    private final String desc;
}

建一个包用于存放代码生成器 LogicGenerator.java,我先来给大家看一下生成 Controller 层的方法是如何实现的吧

    /**
     * 生成 Controller 层的代码
     */
    public void genController(LogicBean logicBean) {
        // 获取实体类名称
        String entityName = logicBean.getEntityClass().getSimpleName();
        // 生成 Controller 类的名称
        String controllerName = entityName + "Controller";
        // 生成 Controller 类的完整名称
        String controllerFullName = controllerName + ".java";
        // 获取 Controller 类的路径
        String controllerPackagePath = logicBean.getControllerPackagePath();
        this.printMessageWithDate(String.format("正在生成 Controller:%s...", controllerFullName));
        // 获取 Controller 生成路径
        String targetOutputDir = this.getDirFromPackage(controllerPackagePath);
        this.printMessageWithDate(String.format("Controller 生成路径:%s...", targetOutputDir));
        // 判断将要生成的 Controller.java 是否存在
        if (FileUtil.exist(targetOutputDir + File.separator + controllerFullName)) {
            this.printMessageWithDate(String.format("Controller 文件[%s]已存在,忽略生成 ~~ ", controllerFullName));
        } else {
            String serviceName = entityName + "Service";
            // 替换 .jf 文件中的内容,比如在 #(author??) 会替换成 logicBean.getAuthor() 这个值
            Kv<String,Object> data = Kv.by("author", (Object)logicBean.getAuthor())
                    .set("controllerPackagePath", controllerPackagePath)
                    .set("QueryTargetListReq", "Query"+entityName+"ListReq")
                    .set("SaveTargetReq", "Save"+entityName+"Req")
                    .set("ServiceName", serviceName)
                    .set("controllerRemark", logicBean.getTableEnumDes())
                    .set("pathValue", StringUtils.isBlank(logicBean.getPathValue())?toFirstLower(entityName):logicBean.getPathValue())
                    .set("ControllerName", controllerName)
                    .set("serviceName", toFirstLower(serviceName))
                    .set("needPaginate", logicBean.isNeedPaginate())
                    .set("isCRUD",logicBean.isCrudType());
            // 配置好模板路径,通过 engine 去生成相应的内容
            String content = this.engine.getTemplate(controllerTemplate).renderToString(data);
            // IO 写文件
            this.writeToFile(targetOutputDir, controllerFullName, content);
        }
    }

再写一个带有 main 方法的类,作为启动生成器的入口

public class MainLogicGenerator extends LogicGenerator {
    public static void main(String[] args) {
        // 启动代码生成器
    }
}    

3. 完整代码

LogicGenerator.java 代码如下:

package com.duojiala.mikeboot.extend.gen;

import cn.hutool.core.io.FileUtil;
import com.duojiala.mikeboot.utils.DateUtil;
import com.jfinal.kit.StrKit;
import com.jfinal.template.Engine;
import org.apache.commons.lang3.StringUtils;

import javax.persistence.Table;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 代码生成器主类
 */
public class LogicGenerator {

    // 分隔符
    public final String SEPARATOR;
    // 项目路径
    private final String PROJECT_PATH;
    // 枚举类路径
    private final String PROJECT_TABLE_ENUM_PATH;
    // Controller 层模板
    private static final String controllerTemplate = "/genera/controller_template.jf";
    // Service 模板
    private static final String serviceTemplate = "/genera/service_template.jf";
    // ServiceImpl 模板
    private static final String serviceImplTemplate = "/genera/service_impl_template.jf";
    // 转换器模板
    private static final String converterTemplate = "/genera/converter_template.jf";
    // 通用 dto 模板
    private static final String commonDtoTemplate = "/genera/common_dto_template.jf";
    // 查询 dto 模板
    private static final String queryDtoTemplate = "/genera/query_dto_template.jf";
    // 生成类队列
    private final List<LogicBean> logicBeans;
    // jfinal 处理 jf 文件的引擎
    private Engine engine;

    /**
     * 初始化
     */
    public LogicGenerator() {
        this.SEPARATOR = File.separator;
        this.PROJECT_PATH = System.getProperty("user.dir");
        this.PROJECT_TABLE_ENUM_PATH = this.PROJECT_PATH + this.SEPARATOR + "src" + this.SEPARATOR + "main" + this.SEPARATOR + "java" + this.SEPARATOR + "com" + this.SEPARATOR + "duojiala" + this.SEPARATOR + "mikeboot" + this.SEPARATOR + "extend" + this.SEPARATOR + "system" + this.SEPARATOR + "TableEnum.java";

        logicBeans = new ArrayList<>();
        this.initGenConfig();
        this.initEngine();
    }

    /**
     * 初始化模板引擎
     */
    private void initEngine() {
        this.printMessageWithDate("初始化模板引擎 ...");
        this.engine = new Engine();
        this.engine.setToClassPathSourceFactory();
        this.engine.addSharedMethod(new StringKit());
    }

    /**
     * 根据生成类队列里面的信息生成代码
     */
    public void generate() {
        this.printMessageWithDate("开始执行生成......");
        this.logicBeans.forEach(this::generateOne);
        this.printMessageWithDate("全部生成 结束 ...");
        this.printMessageWithDate("==请刷新项目目录,检查生成结果==");
    }

    public void generateOne(LogicBean logicBean) {
        this.printMessageWithDate("正在生成 ===> ["+logicBean.getEntityClass().getName()+"]");
        // 生成枚举
        this.genTableEnum(logicBean);
        // 生成 Controller 类
        this.genController(logicBean);
        // 生成 Service 类
        this.genService(logicBean);
        // 生成 ServiceImpl 类
        this.genServiceImpl(logicBean);
        // 生成 相应的 DTO 类
        this.genReqRespConverter(logicBean);
    }

    public void genTableEnum(LogicBean logicBean) {
        Class<?> entityClass = logicBean.getEntityClass();
        String simpleName = entityClass.getSimpleName();
        this.printMessageWithDate("正在生成 "+simpleName+" 表枚举类型");
        boolean exist = FileUtil.exist(this.PROJECT_TABLE_ENUM_PATH);
        if (!exist) {
            throw new RuntimeException(this.PROJECT_TABLE_ENUM_PATH + "文件不存在");
        } else {
            List<String> codeLines = FileUtil.readLines(this.PROJECT_TABLE_ENUM_PATH, "UTF-8");
            if (codeLines != null && codeLines.size() != 0) {
                Table annotation = entityClass.getAnnotation(Table.class);
                String reallyName = annotation.name();
                String tableEnumName = StringUtils.upperCase(reallyName);
                String tableEnumDes = logicBean.getTableEnumDes()==null?tableEnumName:logicBean.getTableEnumDes();
                String code = tableEnumName + "(\"" + tableEnumDes + "\"),";
                if (this.checkFileContainCode(codeLines, code)) {
                    this.printMessageWithDate(String.format("TableEnum 文件中已存在 [%s][%s] 的定义,略过",tableEnumName,tableEnumDes));
                } else {
                    int size = codeLines.size();
                    int insertIndex = 0;
                    int startIndex = -1;

                    for(int i = 1; i < size; ++i) {
                        String tmpCode = (String)codeLines.get(i);
                        if (!StringUtils.isBlank(tmpCode)) {
                            if (startIndex == -1 && tmpCode.trim().startsWith("public enum TableEnum")) {
                                startIndex = i + 1;
                            }

                            if (tmpCode.trim().equals(";")) {
                                insertIndex = i;
                            }
                        }
                    }

                    boolean needProcessUpComma = true;
                    if (insertIndex == 0) {
                        insertIndex = startIndex;
                        code = code + ",";
                        needProcessUpComma = false;
                    }

                    if (startIndex > 0) {
                        codeLines.add(insertIndex, "\t" + code);
                        if (needProcessUpComma) {
                            for(int i = insertIndex - 1; i > startIndex; --i) {
                                String tmp = (String)codeLines.get(i);
                                if (!StringUtils.isBlank(tmp)) {
                                    String tmpTrim = tmp.trim();
                                    if (tmpTrim.endsWith(",") || tmpTrim.endsWith(";") || tmpTrim.endsWith("{") || tmpTrim.endsWith("}")) {
                                        break;
                                    }

                                    if (tmpTrim.charAt(tmpTrim.length() - 1) != ',') {
                                        codeLines.set(i, "\t" + tmpTrim + ",");
                                        break;
                                    }
                                }
                            }
                        }

                        this.printMessageWithDate("正在重新生成新的 TableEnum...");
                        FileUtil.writeLines(codeLines, this.PROJECT_TABLE_ENUM_PATH, "UTF-8");
                    }
                }
            } else {
                throw new RuntimeException(this.PROJECT_TABLE_ENUM_PATH + "文件内容异常");
            }
        }
    }

    private boolean checkFileContainCode(List<String> codeLines, String code) {
        Iterator<String> var4 = codeLines.iterator();

        String tmpCode;
        String codeLine;
        do {
            if (!var4.hasNext()) {
                return false;
            }

            codeLine = (String)var4.next();
            tmpCode = codeLine.trim();
        } while(StringUtils.isBlank(tmpCode) || tmpCode.startsWith("package ") || tmpCode.startsWith("public class ") || tmpCode.startsWith("return ") || tmpCode.equals("}") || !codeLine.contains(code));

        return true;
    }

    public void genController(LogicBean logicBean) {
        String entityName = logicBean.getEntityClass().getSimpleName();
        String controllerName = entityName + "Controller";
        String controllerFullName = controllerName + ".java";
        String controllerPackagePath = logicBean.getControllerPackagePath();
        this.printMessageWithDate(String.format("正在生成 Controller:%s...", controllerFullName));
        String targetOutputDir = this.getDirFromPackage(controllerPackagePath);
        this.printMessageWithDate(String.format("Controller 生成路径:%s...", targetOutputDir));
        if (FileUtil.exist(targetOutputDir + File.separator + controllerFullName)) {
            this.printMessageWithDate(String.format("Controller 文件[%s]已存在,忽略生成 ~~ ", controllerFullName));
        } else {
            String serviceName = entityName + "Service";
            Kv<String,Object> data = Kv.by("author", (Object)logicBean.getAuthor())
                    .set("controllerPackagePath", controllerPackagePath)
                    .set("QueryTargetListReq", "Query"+entityName+"ListReq")
                    .set("SaveTargetReq", "Save"+entityName+"Req")
                    .set("ServiceName", serviceName)
                    .set("controllerRemark", logicBean.getTableEnumDes())
                    .set("pathValue", StringUtils.isBlank(logicBean.getPathValue())?toFirstLower(entityName):logicBean.getPathValue())
                    .set("ControllerName", controllerName)
                    .set("serviceName", toFirstLower(serviceName))
                    .set("needPaginate", logicBean.isNeedPaginate())
                    .set("isCRUD",logicBean.isCrudType());
            String content = this.engine.getTemplate(controllerTemplate).renderToString(data);
            this.writeToFile(targetOutputDir, controllerFullName, content);
        }
    }

    private void genService(LogicBean logicBean) {
        Class<?> entityClass = logicBean.getEntityClass();
        String entityName = entityClass.getSimpleName();
        String serviceName = entityName + "Service";
        String serviceFullName = serviceName + ".java";
        String servicePackagePath = logicBean.getServicePackagePath();
        this.printMessageWithDate(String.format("正在生成 Service:%s...", serviceFullName));
        String targetOutputDir = this.getDirFromPackage(servicePackagePath);
        this.printMessageWithDate(String.format("Service 生成路径:%s...", targetOutputDir));
        if (FileUtil.exist(targetOutputDir + File.separator + serviceFullName)) {
            this.printMessageWithDate(String.format("Service 文件[%s]已存在,忽略生成 ~~ ", serviceFullName));
        } else {
            Kv<String,Object> data = Kv.by("author", (Object)logicBean.getAuthor())
                    .set("servicePackagePath", servicePackagePath)
                    .set("QueryTargetListReq", "Query"+entityName+"ListReq")
                    .set("SaveTargetReq", "Save"+entityName+"Req")
                    .set("EntityName", entityName)
                    .set("QueryTargetListResp", "Query"+entityName+"ListResp")
                    .set("ServiceName", serviceName)
                    .set("needPaginate", logicBean.isNeedPaginate())
                    .set("isCRUD",logicBean.isCrudType());
            String content = this.engine.getTemplate(serviceTemplate).renderToString(data);
            this.writeToFile(targetOutputDir, serviceFullName, content);
        }
    }

    private void genServiceImpl(LogicBean logicBean) {
        Class<?> entityClass = logicBean.getEntityClass();
        String entityName = entityClass.getSimpleName();
        String serviceImplName = entityName + "ServiceImpl";
        String serviceImplFullName = serviceImplName + ".java";
        String serviceImplPackagePath = logicBean.getServiceImplPackagePath();
        this.printMessageWithDate(String.format("正在生成 ServiceImpl:%s...", serviceImplFullName));
        String targetOutputDir = this.getDirFromPackage(serviceImplPackagePath);
        this.printMessageWithDate(String.format("ServiceImpl 生成路径:%s...", targetOutputDir));
        if (FileUtil.exist(targetOutputDir + File.separator + serviceImplFullName)) {
            this.printMessageWithDate(String.format("ServiceImpl 文件[%s]已存在,忽略生成 ~~ ", serviceImplFullName));
        } else {
            List<Toggle> toggleList = new ArrayList<>();
            Field[] declaredFields = entityClass.getDeclaredFields();
            boolean isSoftDel = false;
            for (Field declaredField : declaredFields) {
                String fieldName = declaredField.getName();
                if ("delFlag".equals(fieldName)) {
                    isSoftDel = true;
                    continue;
                }
                Type genericType = declaredField.getGenericType();
                String[] genericArr = genericType.toString().split("\\.");
                String type = genericArr[genericArr.length - 1];
                if ("Boolean".equals(type)) {
                    toggleList.add(new Toggle(fieldName,"req.get" + toFirstUpper(fieldName) + "()"));
                }
            }

            String keywordSelect = logicBean.getKeywordSelect();
            boolean hasKeyword = false;
            if (StringUtils.isNotBlank(keywordSelect)) {
                // 有关键字,则拼装查询条件
                hasKeyword = true;
                String[] keywordArr = keywordSelect.split(",");
                StringBuilder builder = new StringBuilder();
                int i = 0;
                builder.append("criteria.andCondition(\"");
                do {
                    if (i != 0) {
                        builder.append("+ \" or \" + \"");
                    }
                    builder.append(keywordArr[i]).append(" like \" + ").append("\"%\"+").append("req.getKeyword()").append("+\"%\"");
                } while (i++<keywordArr.length-1);
                builder.append(");");
                keywordSelect = builder.toString();
            }

            Kv<String,Object> data = Kv.by("author", (Object)logicBean.getAuthor())
                    .set("serviceImplPackagePath", serviceImplPackagePath)
                    .set("QueryTargetListRespConverter", "Query"+entityName+"ListRespConverter")
                    .set("queryTargetListRespConverter", "query"+entityName+"ListRespConverter")
                    .set("SaveTargetReqConverter", "Save"+entityName+"ReqConverter")
                    .set("saveTargetReqConverter", "save"+entityName+"ReqConverter")
                    .set("QueryTargetListReq", "Query"+entityName+"ListReq")
                    .set("SaveTargetReq", "Save"+entityName+"Req")
                    .set("EntityName", entityName)
                    .set("entityName", toFirstLower(entityName))
                    .set("QueryTargetListResp", "Query"+entityName+"ListResp")
                    .set("ServiceName", entityName + "Service")
                    .set("ServiceImplName", serviceImplName)
                    .set("toggleList", toggleList)
                    .set("hasKeyword", hasKeyword)
                    .set("keywordSelect", keywordSelect)
                    .set("isSoftDel", isSoftDel)
                    .set("needPaginate", logicBean.isNeedPaginate())
                    .set("isCRUD",logicBean.isCrudType());
            String content = this.engine.getTemplate(serviceImplTemplate).renderToString(data);
            this.writeToFile(targetOutputDir, serviceImplFullName, content);
        }
    }

    private void genReqRespConverter(LogicBean logicBean) {
        boolean isCRUD = logicBean.isCrudType();
        if (isCRUD) {
            genRequestBody(logicBean,OptionEnum.SAVE);
            genRequestBody(logicBean,OptionEnum.PAGINATE);
            genResponseBody(logicBean);
        }
    }

    private void genRequestBody(LogicBean logicBean, OptionEnum optionEnum) {
        // 生成请求体
        Class<?> entityClass = logicBean.getEntityClass();
        String entityName = entityClass.getSimpleName();
        String targetReq = null;
        boolean havePageInfo = false;
        switch (optionEnum) {
            case SAVE:
                targetReq = "Save" + entityName + "Req";
                break;
            case PAGINATE:
                targetReq = "Query" + entityName + "ListReq";
                havePageInfo = true;
                break;
            default:
                return;
        }

        String targetReqFullName = targetReq + ".java";
        String requestPackagePath = logicBean.getRequestPackagePath();
        this.printMessageWithDate(String.format("正在生成 RequestBoy:%s...", requestPackagePath));
        String targetOutputDir = this.getDirFromPackage(requestPackagePath);
        this.printMessageWithDate(String.format("RequestBoy 生成路径:%s...", targetOutputDir));
        if (FileUtil.exist(targetOutputDir + File.separator + targetReqFullName)) {
            this.printMessageWithDate(String.format("RequestBoy 文件[%s]已存在,忽略生成 ~~ ", targetReqFullName));
        } else {
            List<String> inserts = new ArrayList<>();
            // 通过反射获取目标对象的属性
            Field[] declaredFields = entityClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                String fieldName = declaredField.getName();
                switch (optionEnum) {
                    case SAVE:
                        if ("createTime".equals(fieldName)) continue;
                        if ("updateTime".equals(fieldName)) continue;
                        if ("delFlag".equals(fieldName)) continue;
                        inserts.add("private String " + fieldName + ";");
                        break;
                    case PAGINATE:
                        if ("delFlag".equals(fieldName)) continue;
                        Type genericType = declaredField.getGenericType();
                        String[] genericArr = genericType.toString().split("\\.");
                        String type = genericArr[genericArr.length - 1];
                        if ("Boolean".equals(type)) {
                            inserts.add("private " + type + " " + fieldName + ";");
                        }
                        break;
                }
            }
            if (OptionEnum.PAGINATE == optionEnum) {
                inserts.add("private String keyword;");
            }
            Kv<String,Object> data = Kv.by("author", (Object)logicBean.getAuthor())
                    .set("dtoPackagePath",requestPackagePath)
                    .set("DtoName", targetReq)
                    .set("havePageInfo", havePageInfo)
                    .set("inserts",inserts);
            String content = this.engine.getTemplate(commonDtoTemplate).renderToString(data);
            this.writeToFile(targetOutputDir, targetReqFullName, content);
        }
        if (OptionEnum.SAVE == optionEnum) {
            genConverter(logicBean,targetReq,entityName,requestPackagePath+"."+targetReq);
        }
    }

    private void genResponseBody(LogicBean logicBean) {
        Class<?> entityClass = logicBean.getEntityClass();
        String entityName = entityClass.getSimpleName();
        String targetReq = "Query" + entityName + "ListResp";
        String targetReqFullName = targetReq + ".java";
        String responsePackagePath = logicBean.getResponsePackagePath();
        this.printMessageWithDate(String.format("正在生成 ResponseBoy:%s...", responsePackagePath));
        String targetOutputDir = this.getDirFromPackage(responsePackagePath);
        this.printMessageWithDate(String.format("ResponseBoy 生成路径:%s...", targetOutputDir));
        if (FileUtil.exist(targetOutputDir + File.separator + targetReqFullName)) {
            this.printMessageWithDate(String.format("ResponseBoy 文件[%s]已存在,忽略生成 ~~ ", targetReqFullName));
        } else {
            List<String> inserts = new ArrayList<>();
            Field[] declaredFields = entityClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                String fieldName = declaredField.getName();
                if ("createTime".equals(fieldName)) continue;
                if ("updateTime".equals(fieldName)) continue;
                if ("delFlag".equals(fieldName)) continue;
                Type genericType = declaredField.getGenericType();
                String[] genericArr = genericType.toString().split("\\.");
                String type = genericArr[genericArr.length - 1];
                inserts.add("private " + type + " " + fieldName + ";");
            }
            Kv<String,Object> data = Kv.by("author", (Object)logicBean.getAuthor())
                    .set("dtoPackagePath",responsePackagePath)
                    .set("DtoName", targetReq)
                    .set("inserts",inserts);
            String content = this.engine.getTemplate(queryDtoTemplate).renderToString(data);
            this.writeToFile(targetOutputDir, targetReqFullName, content);
        }
        genConverter(logicBean,targetReq,entityName,responsePackagePath+"."+targetReq);
    }

    private void genConverter(LogicBean logicBean,String A,String B,String AAllPath) {
        String converterName = A + "Converter";
        String converterFullName = converterName + ".java";
        String converterPackagePath = logicBean.getConverterPackagePath();
        this.printMessageWithDate(String.format("正在生成 Converter:%s...", converterPackagePath));
        String targetOutputDir = this.getDirFromPackage(converterPackagePath);
        this.printMessageWithDate(String.format("Converter 生成路径:%s...", targetOutputDir));
        if (FileUtil.exist(targetOutputDir + File.separator + converterFullName)) {
            this.printMessageWithDate(String.format("Converter 文件[%s]已存在,忽略生成 ~~ ", converterFullName));
        } else {
            Kv<String,Object> data = Kv.by("author", (Object)logicBean.getAuthor())
                    .set("converterPackagePath", converterPackagePath)
                    .set("Target", A)
                    .set("EntityName", B)
                    .set("TargetAllPath",AAllPath);
            String content = this.engine.getTemplate(converterTemplate).renderToString(data);
            this.writeToFile(targetOutputDir, converterFullName, content);
        }
    }

    private void writeToFile(String targetOutputDir, String fileName, String content) {
        if (!FileUtil.exist(targetOutputDir)) {
            FileUtil.mkdir(targetOutputDir);
        }

        String target = targetOutputDir + File.separator + fileName;
        if (!FileUtil.exist(target)) {
            FileUtil.writeUtf8String(content, target);
        }
    }

    /**
     * 初始化配置,供子类实现
     */
    public void initGenConfig() {
    }

    private String getDirFromPackage(String targetPackageName) {
        return PROJECT_PATH + "/src/main/java/" + targetPackageName.replace(".", "/");
    }

    public void printMessageWithDate(String message) {
        System.out.println("[Generate Log]:[" + DateUtil.format(new Date(), DateUtil.YMDHMS) + "]" + message);
    }

    public String toFirstLower(String var) {
        return var.substring(0, 1).toLowerCase() + var.substring(1);
    }

    public String toFirstUpper(String var) {
        return var.substring(0, 1).toUpperCase() + var.substring(1);
    }

    public void addGenBean(LogicBean logicBean) {
        this.logicBeans.add(logicBean);
    }

    private enum OptionEnum {
        SAVE,
        PAGINATE;
    }

    public static class Toggle {
        private String key;
        private String value;

        public Toggle() {
        }

        public Toggle(String key, String value) {
            this.key = key;
            this.value = value;
        }

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }

    public static class StringKit extends StrKit {
        private static final long serialVersionUID = -808251639784763326L;
    }

    /**
     * 生成对象类
     */
    public static class LogicBean {
        private Class<?> entityClass;
        private String author;
        private String projectPath;
        private String controllerPackagePath;
        private String servicePackagePath;
        private String serviceImplPackagePath;
        private String converterPackagePath;
        private String requestPackagePath;
        private String responsePackagePath;
        private String pathValue;
        private String keywordSelect;
        private boolean isCrudType;
        private boolean needPaginate;
        private boolean needSort;
        private String tableEnumDes;

        public LogicBean() {
        }

        public LogicBean(Class<?> entityClass,
                         String tableDes,
                         String author,
                         String pathValue,
                         String controllerPackagePath,
                         String servicePackagePath,
                         String serviceImplPackagePath,
                         String converterPackagePath,
                         String requestPackagePath,
                         String responsePackagePath,
                         String keywordSelect,
                         boolean needPaginate,
                         boolean isCrudType) {
            if (!entityClass.isAnnotationPresent(Table.class)) {
                throw new RuntimeException("Entity:" + entityClass.getName() + "必须有 @Table 注解");
            }
            this.entityClass = entityClass;
            this.tableEnumDes = tableDes;
            this.author = author;
            this.pathValue = pathValue;
            this.controllerPackagePath = controllerPackagePath;
            this.servicePackagePath = servicePackagePath;
            this.serviceImplPackagePath = serviceImplPackagePath;
            this.converterPackagePath = converterPackagePath;
            this.requestPackagePath = requestPackagePath;
            this.responsePackagePath = responsePackagePath;
            this.keywordSelect = keywordSelect;
            this.needPaginate = needPaginate;
            this.isCrudType = isCrudType;

        }

        public Class<?> getEntityClass() {
            return entityClass;
        }

        public void setEntityClass(Class<?> entityClass) {
            if (!entityClass.isAnnotationPresent(Table.class)) {
                throw new RuntimeException("Entity:" + entityClass.getName() + "必须有 @Table 注解");
            }
            this.entityClass = entityClass;
        }

        public String getAuthor() {
            return author;
        }

        public void setAuthor(String author) {
            this.author = author;
        }

        public String getProjectPath() {
            return projectPath;
        }

        public void setProjectPath(String projectPath) {
            this.projectPath = projectPath;
        }

        public String getControllerPackagePath() {
            return controllerPackagePath;
        }

        public void setControllerPackagePath(String controllerPackagePath) {
            this.controllerPackagePath = controllerPackagePath;
        }

        public String getServicePackagePath() {
            return servicePackagePath;
        }

        public void setServicePackagePath(String servicePackagePath) {
            this.servicePackagePath = servicePackagePath;
        }

        public String getServiceImplPackagePath() {
            return serviceImplPackagePath;
        }

        public void setServiceImplPackagePath(String serviceImplPackagePath) {
            this.serviceImplPackagePath = serviceImplPackagePath;
        }

        public String getTableEnumDes() {
            return tableEnumDes;
        }

        public void setTableEnumDes(String tableEnumDes) {
            this.tableEnumDes = tableEnumDes;
        }

        public boolean isCrudType() {
            return isCrudType;
        }

        public void setCrudType(boolean crudType) {
            isCrudType = crudType;
        }

        public boolean isNeedPaginate() {
            return needPaginate;
        }

        public void setNeedPaginate(boolean needPaginate) {
            this.needPaginate = needPaginate;
        }

        public String getPathValue() {
            return pathValue;
        }

        public void setPathValue(String pathValue) {
            this.pathValue = pathValue;
        }

        public boolean isNeedSort() {
            return needSort;
        }

        public void setNeedSort(boolean needSort) {
            this.needSort = needSort;
        }

        public String getConverterPackagePath() {
            return converterPackagePath;
        }

        public void setConverterPackagePath(String converterPackagePath) {
            this.converterPackagePath = converterPackagePath;
        }

        public String getRequestPackagePath() {
            return requestPackagePath;
        }

        public void setRequestPackagePath(String requestPackagePath) {
            this.requestPackagePath = requestPackagePath;
        }

        public String getResponsePackagePath() {
            return responsePackagePath;
        }

        public void setResponsePackagePath(String responsePackagePath) {
            this.responsePackagePath = responsePackagePath;
        }

        public String getKeywordSelect() {
            return keywordSelect;
        }

        public void setKeywordSelect(String keywordSelect) {
            this.keywordSelect = keywordSelect;
        }
    }

    public static class Kv<K,V> extends HashMap<K,V> {

        private static final long serialVersionUID = -808251639784763326L;

        public Kv() {
        }

        public Kv<K,V> set(K key, V value) {
            super.put(key, value);
            return this;
        }

        public Kv<K,V> set(Map<K,V> map) {
            super.putAll(map);
            return this;
        }

        public Kv<K,V> delete(K key) {
            super.remove(key);
            return this;
        }

        public static <K,V> Kv<K,V> by(K key, V value) {
            return (new Kv<K,V>()).set(key, value);
        }

        public static <K,V> Kv<K,V> create() {
            return new Kv<K,V>();
        }

        public Kv<K,V> setIfNotNull(K key, V value) {
            if (value != null) {
                this.set(key, value);
            }
            return this;
        }

    }
}

MainLogicGenerator.java 代码如下:

package com.duojiala.mikeboot.extend.gen;

import com.duojiala.mikeboot.domain.entity.Post;

/**
 * Controller Service 代码生成器
 */
public class MainLogicGenerator extends LogicGenerator {
    public static void main(String[] args) {
        new MainLogicGenerator().generate();
    }

    @Override
    public void initGenConfig() {
        // 以哪个实体类生成代码
        Class<?> clazz = Post.class;
        // 表描述
        String tableDes = "职位";  // TableEnum.java 类中的枚举,tableDes 为表的描述信息,如果不填默认为类名的全大写
        // 生成 java 代码的默认作者
        String author = "mike";
        // 映射路径
        String pathValue = ""; // 如果不填默认为类的全小写
        // Controller 类生成路径
        String controllerPackagePath = "com.duojiala.mikeboot.controller";
        // Service 类生成路径
        String servicePackagePath = "com.duojiala.mikeboot.service";
        // ServiceImpl 类生成路径
        String serviceImplPackagePath = "com.duojiala.mikeboot.service.imp";
        // Converter 转换器类生成路径
        String converterPackagePath = "com.duojiala.mikeboot.domain.converter.subclass";
        // Request 请求体类生成路径
        String requestPackagePath = "com.duojiala.mikeboot.domain.req";
        // Response 响应体类生成路径
        String responsePackagePath = "com.duojiala.mikeboot.domain.resp";
        // 是否需要分页查询
        boolean needPaginate = true;
        // 是否为普通增删操作
        boolean isCrudType = true;
        // 以那些字段进行模糊查询,多个用 "," 拼接,不设置默认为 name
        String keywordSelect = "";

        //创建主逻辑生成配置Bean
        LogicBean logicBean = new LogicBean(clazz, tableDes, author, pathValue,
                controllerPackagePath, servicePackagePath, serviceImplPackagePath,converterPackagePath,requestPackagePath,responsePackagePath,keywordSelect, needPaginate, isCrudType);

        //加入到生成队列中
        addGenBean(logicBean);
    }
}


4. 测试效果

我只需要在 MainLogicGenerator 启动类中配置相应的信息,比如要生成哪张表的增删改查,就配置对应的 .class,设置一些作者、路径、是否生成分页的方法等等条件,运行 mian 方法就行了。

在这里插入图片描述
例如上面所示,我要生成 post 表相关的一些基本操作,而且可以看到我这里是没有 Controller、Service、ServiceImpl 等文件的。

在这里插入图片描述

可以看到运行之后这些文件也都生成了。


三、项目源码

这就是我写的代码生成器,以下我也提供了项目的下载链接便于大家去玩:

链接:百度网盘
提取码:xb1k

其次我还要说明一点,如果你想要连那些实体类的文件和前端也全部生成,建议你还是看看 jfinal 是怎么实现的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/14588.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android音视频开发-OpenGL ES正交投影实现方法

本文实例为大家分享了OpenGL ES正交投影展示的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下 绘制正方形 在最开始绘制的六边形里面好像看起来挺容易的&#xff0c;也没有出现什么问题&#xff0c;接下来不妨忘记前面绘制六边形的代码&#xff0c;让我们按照自己的…

浏览器页面操作——实时监控网页变化,读取网页内容

浏览器页面操作功能介绍 浏览器页面操作是集简云的一款免费内置应用&#xff0c;它可以定时监控网页变化&#xff0c;精准捕捉所需信息。一键设置指定网页与元素&#xff0c;全自动监测并即时推送通知&#xff0c;助您在第一时间了解网页最新情况&#xff0c;让您更高效便捷地…

( “树” 之 Trie) 208. 实现 Trie (前缀树) ——【Leetcode每日一题】

知识点回顾 &#xff1a; Trie&#xff0c;又称前缀树或字典树&#xff0c;用于判断字符串是否存在或者是否具有某种字符串前缀。 ❓208. 实现 Trie (前缀树) 难度&#xff1a;中等 Trie&#xff08;发音类似 “try”&#xff09;或者说 前缀树 是一种树形数据结构&#xff…

冲实习 or 全力准备秋招?

作者&#xff1a;阿秀 校招八股文学习网站&#xff1a;https://interviewguide.cn 这是阿秀的第「261」篇原创 小伙伴们大家好&#xff0c;我是阿秀。 欢迎今年参加秋招的小伙伴加入阿秀的学习圈&#xff0c;目前已经超过 2300 小伙伴加入&#xff01;去年认真准备和走下来的基…

聚焦能源 | 赛宁网安亮相2023年中国能源网络安全大会

​​4月21日&#xff0c;2023年中国能源网络安全大会&#xff08;以下简称“大会”&#xff09;在江苏南京成功落幕&#xff01;为贯彻国家网络强国战略&#xff0c;加强能源网络安全技术创新、成果应用、人才培养与技术交流&#xff0c;大会推出主旨论坛、案例交流、展览展示等…

sql优化慢查询

1.慢查询设置 慢查询设置&#xff08;临时&#xff09; -- 查看是否开启了慢查询日志 show variables like slow%;-- 开启慢查询日志 set global slow_query_log on;-- 更改日志路径 set global slow_query_log_file /data/mydata/app1-slow.log;-- 查看慢查询时间临界值&…

大厂齐出海:字节忙种草,网易爱社交

配图来自Canva可画 随着国内移动互联网红利逐渐触顶&#xff0c;互联网市场日趋饱和&#xff0c;国内各互联网企业之间的竞争便愈发激烈起来。在此背景下&#xff0c;广阔的海外市场就成为了腾讯、阿里、字节、京东、拼多多、百度、网易、快手、B站等互联网公司关注和争夺的重…

第四章(1):词向量定义与意义

第四章&#xff08;1&#xff09;&#xff1a;词向量定义与意义 目录 第四章&#xff08;1&#xff09;&#xff1a;词向量定义与意义前言1. 词的表示1.1 离散表示1.1.1 One-Hot独热编码1.1.2 ngram特征表示 1.2 分布式表示 2. 意义 前言 在自然语言处理的领域中&#xff0c;每…

成功上岸北大!总分418分,数学150分,经验贴+方法论

Datawhale干货 作者&#xff1a;葛云阳&#xff0c;杭州电子科技大学&#xff0c;Datawhale成员 前 言 大家好&#xff0c;我是北海。2023年以总分418分的成绩上岸北京大学信息工程学院计算机应用技术专业&#xff0c;其中初试第三&#xff0c;复试第五&#xff0c;总成绩第三…

zookeepr 简介

简介&#xff1a; zookeeper是为分布式应用提供协调服务的高性能组件。zookeeper通过简单的接口暴露了一些公共服务(命名、配置管理、同步和分组服务), 因此你不需要从头开始写这些服务。你可以现成得使用zookeeper来实现共识、组管理、领导者选举和存在协议。你可以根据自己的…

iptables和firewalld防火墙

安全技术和防火墙概述 安全技术 入侵检测系统&#xff08;Intrusion Detection Systems&#xff09;&#xff1a;特点是不阻断任何网络访问&#xff0c;量化、定位来自内外网络的威胁情况&#xff0c;主要以提供报警和事后监督为主&#xff0c;提供有针对性的指导措施和安全决…

Java核心技术 卷1-总结-16

Java核心技术 卷1-总结-16 线程属性线程优先级守护线程未捕获异常处理器 同步竞争条件的一个例子竞争条件详解锁对象 线程属性 线程的各种属性包括&#xff1a;线程优先级、守护线程、线程组以及处理未捕获异常的处理器。 线程优先级 在Java程序设计语言中&#xff0c;每一个…

OpenGL入门教程之 纹理

引言 我们已经了解到&#xff0c;我们可以为每个顶点添加颜色来增加图形的细节&#xff0c;从而创建出有趣的图像。但是&#xff0c;如果想让图形看起来更真实&#xff0c;我们就必须有足够多的顶点&#xff0c;从而指定足够多的颜色。这将会产生很多额外开销&#xff0c;因为每…

ChatGPT国内可用版-国内chatGPT哪个软件好用

国内chatGPT哪个软件最好用 国内对接ChatGPT软件&#xff0c;让智能的对话变得更加简单便捷&#xff01;ChatGPT是由OpenAI公司开发的最新一代自然语言处理技术&#xff0c;为聊天机器人赋予了更加真实、流畅、智能的语言表达能力。 我们是国内一家专注于人工智能和自然语言处…

旧版VS安装 Visual Studio 2019/2017/2015官方安装教程

安装VisualStudio找不到官方版本&#xff1f;只能找到第三方&#xff1f;害怕中毒&#xff1f; 不要急&#xff0c;本文例举了VS 2019 2017 2015的官方位置&#xff0c;不用但心装成第三方Visual Studio 百度搜索 Visual Studio 2017&#xff0c;只有第三方的包&#xff0c;而…

大孔树脂型号,A-722,ADS500,ADS600,ADS750,ADS800

一、产品介绍 基于吸附功能的聚苯乙烯特种树脂 Tulsimer ADS-600 是一款没有离子官能基的&#xff0c;由交联聚苯乙烯合成的功能强大的吸附型树脂。 Tulsimer ADS-600 主要应用于水溶液中吸附酚及其化合物&#xff0c;氯代烃等含氯物质&#xff0c;表面活性剂&#xff0…

Three——二、加强对三维空间的认识

Three——二、加强对三维空间的认识 接上个例子我们接着往下看 辅助观察坐标系 THREE.AxesHelper()的参数表示坐标系坐标轴线段尺寸大小&#xff0c;你可以根据需要改变尺寸。 使用方法&#xff1a; // AxesHelper&#xff1a;辅助观察的坐标系 const axesHelper new THRE…

java的社区养老服务系统 ssm空巢老人

创新点&#xff1a; 1、根据时间、类型统计用户下单记录&#xff0c;形成可视化图形&#xff08;饼状图&#xff09; 2、根据用户爱好推荐项目 包含模块&#xff1a;关于我们、联系我们、外链信息、资讯类型、服务资讯、服务类型、服务项目、案例类型、服务案例、讨论类型、讨论…

【数据库】— 2NF、3NF、BCNF、最小函数依赖集例题

判断范式级别 设有关系模式W(C,P,S,G,T,R)&#xff0c;其中各属性的含义是&#xff1a;C课程&#xff0c;P教师&#xff0c;S学生&#xff0c;G成绩&#xff0c;T时间&#xff0c;R教室&#xff0c;根据定义有如下数据依赖集 D{ C→P&#xff0c;(S,C)→G&#xff0c;(T,R)→C&…

2023.04.23 学习周报

文章目录 摘要文献阅读1.题目2.摘要3.介绍4.模型4.1 研究区域4.2 自相关分析4.3 LSTM 5.实验与讨论5.1 高架道路不同位置空气污染物的变化5.2 高架道路不同位置空气污染物的相关性5.3 高架道路不同位置空气污染物预测 6.结论7.展望 度规张量1.曲率2.度量张量3.代码实现4.平行四…