Java-代码生成器的实现

article/2025/9/29 3:20:57

文章目录

  • 前言
  • 一、概述
  • 二、手写代码
    • 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();}@Overridepublic 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;// 以那些字段进行模糊查询,多个用 "," 拼接,不设置默认为 nameString keywordSelect = "";//创建主逻辑生成配置BeanLogicBean 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 是怎么实现的。


http://chatgpt.dhexx.cn/article/je0mr6oY.shtml

相关文章

MyBatis-Plus代码生成器(新)使用

目录 一&#xff0c;MyBatis-Plus基本简介。 二&#xff0c;特性 三&#xff0c;实现代码自动生成工具 3.1&#xff0c;准备一个初始项目&#xff0c;数据表&#xff0c;连接好数据库 3.2&#xff0c;导入Mybatis-Plus相关依赖 3.3&#xff0c;配置数据库配置文件applicat…

快速上手若依代码生成器(2022)

文章目录 前言一、启动若依框架二、使用代码生成器1 导入示例表2 使用自带的代码生成 生成zip文件2.1 Illegal mix of collations报错解决 三 把zip文件的内容粘贴到对应的模块中3.1 粘贴后台代码3.2 粘贴前台代码3.3 运行菜单sql语句 四 重新启动前端后端总结 前言 快速上手使…

若依代码生成器的使用

一、代码生成器的使用 1.新建maven模块 原则上&#xff0c;我们的业务代码和若依系统本身的系统代码是要做隔离的&#xff0c;一方面是易于之后随着若依系统升级而升级&#xff0c;另一方面则是纯粹的合理性考虑。 这里新建一个ruoyi-business模块作为业务代码模块&#xff0c;…

Java代码生成器

项目说明 本项目基于是基于 renren-generator 定制的代码生成器 文章目录 **项目说明**不同点&#xff1a;效果原理分析如何定制开发&#xff1f;更多可能存在的坑代码地址 不同点&#xff1a; 因为本人的公司使用的是 tkmyabtis swagger 构建 rest api,而 renren-generator…

手把手带你实现一个代码生成器

前言 不知各位看官在工作之中有没有陷入过疯狂CV代码、看着密密麻麻的类不想动手&#xff0c;或者把大把的时间花费在底层的情况。以笔者为例&#xff0c;会经常遇到以下两个问题&#xff1a; 隔一段时间就需要构建一个新应用&#xff0c;需要各种复制粘贴&#xff08;缺乏定…

代码生成器 ----一个独立的代码生成器

这里我就没有过多发废话了&#xff0c;直接给正文。下面是代码生成器的一些简单介绍。操作简单没啥难度。 下面是生成器的地址&#xff0c;谢谢您的使用↓ 代码生成器点这儿。 一、基于简单的数据库属性连接 二、浏览器缓存连接历史 1、可以选择历史连接 2、可以清空所有浏…

如何自定义代码生成器(上)

1 概述 1.1 介绍 ​ 在项目开发过程中&#xff0c;有很多业务模块的代码是具有一定规律性的&#xff0c;例如controller控制器、service接口、service实现类、mapper接口、model实体类等等&#xff0c;这部分代码可以使用代码生成器生成&#xff0c;我们就可以将更多的时间放…

Java快速开发之代码生成器

1、代码生成器原理分析 观察我们之前写的代码&#xff0c;会发现其中也会有很多重复内容&#xff0c;比如: 那我们就想&#xff0c;如果我想做一个Book模块的开发&#xff0c;是不是只需要将红色部分的内容全部更换成Book即可&#xff0c;如&#xff1a; 所以我们会发现&#…

代码生成器

目录 引言 1 第一种 1.0 pom依赖 1.1 第一步&#xff1a;配置文件 1.2 第二步&#xff1a;代码生成器 1.3 执行生成 引言 代码生成器&#xff0c;遇到过也使用过很多了&#xff0c;用的时候觉得很简单&#xff0c;但是这个玩意不是开个新项目几乎很少碰到&#xff0c;所…

推荐几个代码自动生成器,神器!!!

20个代码生成框架 老的代码生成器的地址&#xff1a;https://www.cnblogs.com/skyme/archive/2011/12/22/2297592.html 以下是大家推荐的最近很火爆的代码生成器神器。如果有更好的希望大家多多留言&#xff0c;我会及时补充上去。 -------------------------更新补充------…

代码生成器AutoGenerator

简介&#xff1a;AutoGenerator 是 MyBatis-Plus 的代码生成器&#xff0c;通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码&#xff0c;极大的提升了开发效率。 简介 | MyBatis-Plus 一、如何使用&#xff1a; 1&#x…

代码生成器-mybatis-plus-generator

我们平时在开发的过程中&#xff0c;对于新建的一张表难免会有对其进行增删改查的操作&#xff0c;而且还要写Controller、service、Mapper、Mapper.xml、PO、VO等等。如果每次都要去写这些跟业务毫不相干但是却又耗时耗力的重复代码这不仅是让开发人员不能专注于业务逻辑甚至可…

HM2022ssm-mp5【MP代码生成器】

1. 代码生成器原理分析 1.1 造句: 我们可以往空白内容进行填词造句&#xff0c;比如: 在比如: 再有&#xff1a; 1.2 观察我们之前写的代码&#xff0c;会发现其中也会有很多重复内容&#xff0c;比如: 那我们就想&#xff0c;如果我想做一个Book模块的开发&#xff0c;是不是只…

MyBatis-Plus——代码生成器的使用

MyBatis-Plus——代码生成器的使用 AutoGenerator 是 MyBatis-Plus 的代码生成器&#xff0c;通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码&#xff0c;极大的提升了开发效率。 导入依赖 <dependency><groupI…

推荐几个代码自动生成器,神器

20个代码生成框架 老的代码生成器的地址&#xff1a;https://www.cnblogs.com/skyme/archive/2011/12/22/2297592.html 以下是大家推荐的最近很火爆的代码生成器神器。如果有更好的希望大家多多留言&#xff0c;我会及时补充上去。 2.0 WebFirst .NET Core代码生成器 全新的.…

Mybatis-Plus自动生成代码,自定义Controller

MP网址&#xff1a;https://baomidou.com/pages/779a6e/#%E4%BD%BF%E7%94%A8 直接copy官网代码修改成自己的&#xff1a; private void generate() {FastAutoGenerator.create("jdbc:mysql://localhost:3306/test?serverTimezoneGMT%2b8", "root", "…

Oracle数据库的下载与安装教程详解

前言 由于新公司的开发需要用到Oracle数据库&#xff0c;但是自己之前很少接触Oracle&#xff0c;自然也就没有安装Oracle数据库&#xff0c;所以在自己亲自下载安装的时候&#xff0c;就顺便整理成一篇笔记。 一、Oracle的下载 首先&#xff0c;任何软件的下载都推荐自己去官…

【详细】Oracle数据库安装教程--Oracle DataBase 19c

目录 前言/背景1、下载Oracle 19c数据库1. Oracle官网下载2. 网盘下载3. 安装及使用 2、Oracle管理工具 前言/背景 Oracle Database 19c 是最新的长期版本&#xff0c;支持期限最长&#xff1b; 19.3 - 企业版&#xff08;也包括标准版 2&#xff09; 适用于WINDOWS X64位系统。…

Oracle数据库的学习

初始第一步&#xff1a;打开计算机管理---找到服务---打开Oracle实例和Oracle监听器。 1.在命令提示窗口中输入sqlplus&#xff0c;然后进入数据库设置页面大小&#xff1a; 2.切换用户&#xff1a; 3. run相当于/ &#xff0c;D:\999.sql 这是运行脚本的指令&#xff1b; 4. …