前言
最近看了一個開源的項目,jfinal-layui
,然后這個項目里面有一個 代碼生成器
的功能
之前雖然有用過代碼生成器,但是從來沒有看過相關(guān)的源碼,所以就研究了一下,個人感覺這個項目的代碼生成器還是蠻好的,能夠根據(jù)指定的數(shù)據(jù)庫和表生成前后端的代碼,不過這個項目的框架是 jfinal
,直接把這個代碼生成器相關(guān)的代碼拷到自己的項目當中是運行不起來,而且每個項目的結(jié)構(gòu)都存在一些特有的性質(zhì),很難找到一個拿來就能用的代碼生成器,介于這一點,我就根據(jù)自己項目的架構(gòu),利用 jfinal
框架造的輪子,寫了一個較為簡單代碼生成器,同時分享一下我是怎樣實現(xiàn)的。
倘如各位想研究下 jfinal-layui
這個項目,可以點擊 https://gitee.com/QinHaiSenLin/Jfinal-layui 這個鏈接去拉取這個項目的源碼。
一、概述
代碼生成器顧名思義就是一個生成代碼的軟件,在日常開發(fā)中我們可以使用代碼生成器來減少一些重復(fù)的開發(fā)工作,提高開放效率,像我們經(jīng)常使用的 mybatis
就有一個逆向工程,它能自動幫我們生產(chǎn)數(shù)據(jù)庫對應(yīng)的實體類,mapper 和 xml 文件等,其實就是一個代碼生成器。
那代碼生成器的原理是什么呢?
其實很簡單,我們要生成的文件肯定是得有一定的規(guī)律,然后我們根據(jù)這個規(guī)律去編寫一個統(tǒng)一的模板
,按照這個模板去生成相應(yīng)的代碼。
二、手寫代碼
1. 簡要說明
以下內(nèi)容僅供參考,我提供的只是一種實現(xiàn)方案,可能并不是適用于你的項目,但是只要你理解了我是怎么實現(xiàn)的,你也可以編寫一個適合你自己項目的代碼生成器。
脫離實際場景的業(yè)務(wù)沒什么意義,前面我也說過,任何項目都有它的獨特性,我是根據(jù)自己的項目來編寫一個符合我要求的代碼生成器,所以我先說下我自己的這個項目簡單的一個架構(gòu)。
比如說我的這個項目,要實現(xiàn)一個完整的增刪改查的一套的接口,需要有以下幾個類的存在,其中紅色框
框選的部分(實體類
User.java、mapper 文件
UserMapper.java 和 xml 文件
UserMapper.xml)都可以通過 mybatis 的逆向工程生成,而綠色框
框選的部分是需要我手動去編寫的,所以我需要寫的代碼生成器是能夠幫我去生成綠色框框選部分的文件,比如: Controller
、Service
、ServiceImp
、請求體 dto
、相應(yīng)體 dto
以及 subclass 包下的轉(zhuǎn)換器
。
關(guān)于我這個項目具體的搭建可查看這篇博客:SpringBoot - 快速搭建
那我該如何去實現(xiàn)了,首先我建議你自己寫一個標準的增刪改查,然后對照你寫的東西,去寫一個模板,比如我寫了一個崗位的 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.*;
/**
* 頂部導(dǎo)航 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
}
這是通過自己項目中常寫的代碼,然后將公用的地方保留起來,結(jié)合一些模板引擎的語法將有差異的地方替換掉就行了,這里暫時看不懂也沒關(guān)系(模板引擎的語法在寫這個代碼生成器時我也不懂,不過看別人寫的對比下還是能寫出來的)
最終效果
2. 代碼編寫
因為是借了 jfinal
的輪子,所以這里我有引用到 jfinal
的一個依賴
<!--JFianl-->
<dependency>
<groupId>com.jfinal</groupId>
<artifactId>jfinal</artifactId>
<version>5.0.4</version>
</dependency>
還有就是一些工具類(這些根據(jù)你自己項目來選擇)
<!-- 轉(zhuǎn)化器自動轉(zhuǎn)換 -->
<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
層的方法是如何實現(xiàn)的吧
/**
* 生成 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 文件中的內(nèi)容,比如在 #(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 去生成相應(yīng)的內(nèi)容
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";
// 轉(zhuǎn)換器模板
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());
}
/**
* 根據(jù)生成類隊列里面的信息生成代碼
*/
public void generate() {
this.printMessageWithDate("開始執(zhí)行生成......");
this.logicBeans.forEach(this::generateOne);
this.printMessageWithDate("全部生成 結(jié)束 ...");
this.printMessageWithDate("==請刷新項目目錄,檢查生成結(jié)果==");
}
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);
// 生成 相應(yīng)的 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 + "文件內(nèi)容異常");
}
}
}
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)) {
// 有關(guān)鍵字,則拼裝查詢條件
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);
}
}
/**
* 初始化配置,供子類實現(xiàn)
*/
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 轉(zhuǎn)換器類生成路徑
String converterPackagePath = "com.duojiala.mikeboot.domain.converter.subclass";
// Request 請求體類生成路徑
String requestPackagePath = "com.duojiala.mikeboot.domain.req";
// Response 響應(yīng)體類生成路徑
String responsePackagePath = "com.duojiala.mikeboot.domain.resp";
// 是否需要分頁查詢
boolean needPaginate = true;
// 是否為普通增刪操作
boolean isCrudType = true;
// 以那些字段進行模糊查詢,多個用 "," 拼接,不設(shè)置默認為 name
String keywordSelect = "";
//創(chuàng)建主邏輯生成配置Bean
LogicBean logicBean = new LogicBean(clazz, tableDes, author, pathValue,
controllerPackagePath, servicePackagePath, serviceImplPackagePath,converterPackagePath,requestPackagePath,responsePackagePath,keywordSelect, needPaginate, isCrudType);
//加入到生成隊列中
addGenBean(logicBean);
}
}
4. 測試效果
我只需要在 MainLogicGenerator
啟動類中配置相應(yīng)的信息,比如要生成哪張表的增刪改查,就配置對應(yīng)的 .class
,設(shè)置一些作者、路徑、是否生成分頁的方法等等條件,運行 mian
方法就行了。
例如上面所示,我要生成 post
表相關(guān)的一些基本操作,而且可以看到我這里是沒有 Controller、Service、ServiceImpl
等文件的。
可以看到運行之后這些文件也都生成了。
三、項目源碼
這就是我寫的代碼生成器,以下我也提供了項目的下載鏈接便于大家去玩:
鏈接:百度網(wǎng)盤
提取碼:xb1k文章來源:http://www.zghlxwxcb.cn/news/detail-431697.html
其次我還要說明一點,如果你想要連那些實體類的文件和前端也全部生成,建議你還是看看 jfinal
是怎么實現(xiàn)的。文章來源地址http://www.zghlxwxcb.cn/news/detail-431697.html
到了這里,關(guān)于Java-代碼生成器的實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!