MyBatis-Plus結合Swagger實現接口代碼及文檔自動生成工具(基礎篇-基於配置文件)
需求背景:
近日公司接到一個新項目,時間比較緊急,需求比較簡單無非是簡單的增刪改查分頁查詢等等,業務複雜度不高。唯一的要求就是快速正常交付,並且需要提供文檔給到客戶端進行開發聯調。
常規的需求分析步驟是:
1:根據需求設計數據庫表結構。
2:技術選型搭建開發工程。
3:根據表利用第三方插件自動生成bean及mapper等文件。
4:由於時間緊還需要和客戶端進行聯調,因此需要文檔先行,設計接口文檔。
5:代碼開發,編寫各個數據表的增刪改成方法。
我的分析:
雖然此次需求較簡單,業務邏輯不復雜,無非就是一些增刪改查的功能,但是設計功能模塊較多,初步估計需要設計50多張數據表。
1:關於數據表的設計,由於業務邏輯不復雜,因此在數據表的設計也要秉承簡單的原則,因此寧可多設計表,也不牽扯太多關聯。
2:技術選型肯定選擇springboot+maven快速集成發佈的模式。
3:對於第三點現在比較流行的就是mybatis,由於很大地方有分頁的需求,因此還需要集成分頁插件。
4:關於第四點需要文檔聯調,我第一反應就是Swagger,如果你對它還一無所知,建議你去了解一下,簡單來說就是可以在你寫好代碼的時候自動生成接口文檔,並且提供在線測試的功能。
5:對於第五點也是最需要思考的一點,最最常規的操作就是對着需求一個個接口來敲,可是如果是這樣就沒有本文存在的必要了。注意我前面對於需求的分析,重點圈起來(雖然此次需求較簡單,業務邏輯不復雜,無非就是一些增刪改查的功能,但是設計功能模塊較多,初步估計需要設計50多張數據表。)。
我在思考有沒有可能根據表結構像自動生成bean和mapper一樣可以自動生成增刪改查分頁等功能接口。答案是肯定的,因爲mybatis的插件能夠做到自動生成bean和mapper代碼,我們就肯定可以自動生成接口代碼。
既然答案是肯定的那麼下一步就需要思考如何去實現了。於是第一想法就是去度娘上搜索是否有現成的第三方jar包可以實現這個功能。可是搜索良久收穫全無。因此我下定決心自己來寫一個。你要問我怎麼樣去寫,我只能告訴你,我毫無頭緒,可是我會去一步步分析,問題都是一個個解決的,沒有誰能一蹴而就,那是大神的水準。
自動化代碼分析:
步驟一:既然要生成swagger,就需要觀察正常的swagger接口是個啥?
@ApiOperation(value = "查詢學員基本信息接口", notes = "")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "path", name = "pageNum", value = "頁碼", required = true, dataType = "Integer"),
@ApiImplicitParam(paramType = "path", name = "pageSize", value = "每頁條數", required = true, dataType = "Integer"),
@ApiImplicitParam(paramType = "query", name = "fbh", value = "編號", required = true, dataType = "String"),
@ApiImplicitParam(paramType = "query", name = "fsfz", value = "身份證", required = true, dataType = "String") })
@GetMapping(value = "list/{pageNum}/{pageSize}", produces = MediaType.APPLICATION_JSON_VALUE)
public JsonResult list(@PathVariable("pageNum") int pageNum, @PathVariable("pageSize") int pageSize, Gjda item) {
GjdaExample example = new GjdaExample();
// 補充查詢參數開始
if (item != null) {
GjdaExample.Criteria criteria = example.createCriteria();
if (StringUtils.isNotEmpty(item.getFbh())) {
criteria.andFbhLike("%" + item.getFbh() + "%");
}
if (StringUtils.isNotEmpty(item.getFsfz())) {
criteria.andFsfzLike("%" + item.getFsfz() + "%");
}
}
// 補充查詢參數結束
Page<List<Gjda>> page = PageHelper.startPage(pageNum, pageSize);
List<Gjda> list = gjdaMapper.selectByExample(example);
PageInfo info = new PageInfo(page.getPageNum(), page.getPageSize(), page.getTotal(), page.getPages(), list);
return JsonResult.getSuccess(info);
}
上面是一個正常的手寫的swagger風格的接口代碼,從上面可知,如果要生成這樣一個代碼我們需要知道bean對象自段的名稱、類型、以及說明。這幾點你會聯想到什麼?反射沒錯就是java反射,通過反射我們可以拿到bean對象的所有屬性。那麼我們就看看通過mybatis自動生成的bean到底長啥樣,能不能滿足我們的需求。
package com.zte.model;
import java.io.Serializable;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiModel;
public class TSysParameter implements Serializable {
/**
* This field was generated by MyBatis Generator. This field corresponds to the database column t_sys_parameter.parameter_id
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
private Long parameterId;
/**
* This field was generated by MyBatis Generator. This field corresponds to the database column t_sys_parameter.parameter_name
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
private String parameterName;
/**
* This field was generated by MyBatis Generator. This field corresponds to the database column t_sys_parameter.parameter_value
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
private String parameterValue;
/**
* This field was generated by MyBatis Generator. This field corresponds to the database column t_sys_parameter.remark
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
private String remark;
/**
* This field was generated by MyBatis Generator. This field corresponds to the database table t_sys_parameter
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
private static final long serialVersionUID = 1L;
/**
* This method was generated by MyBatis Generator. This method returns the value of the database column t_sys_parameter.parameter_id
* @return the value of t_sys_parameter.parameter_id
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
public Long getParameterId() {
return parameterId;
}
/**
* This method was generated by MyBatis Generator. This method sets the value of the database column t_sys_parameter.parameter_id
* @param parameterId the value for t_sys_parameter.parameter_id
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
public void setParameterId(Long parameterId) {
this.parameterId = parameterId;
}
/**
* This method was generated by MyBatis Generator. This method returns the value of the database column t_sys_parameter.parameter_name
* @return the value of t_sys_parameter.parameter_name
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
public String getParameterName() {
return parameterName;
}
/**
* This method was generated by MyBatis Generator. This method sets the value of the database column t_sys_parameter.parameter_name
* @param parameterName the value for t_sys_parameter.parameter_name
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
public void setParameterName(String parameterName) {
this.parameterName = parameterName == null ? null : parameterName.trim();
}
/**
* This method was generated by MyBatis Generator. This method returns the value of the database column t_sys_parameter.parameter_value
* @return the value of t_sys_parameter.parameter_value
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
public String getParameterValue() {
return parameterValue;
}
/**
* This method was generated by MyBatis Generator. This method sets the value of the database column t_sys_parameter.parameter_value
* @param parameterValue the value for t_sys_parameter.parameter_value
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
public void setParameterValue(String parameterValue) {
this.parameterValue = parameterValue == null ? null : parameterValue.trim();
}
/**
* This method was generated by MyBatis Generator. This method returns the value of the database column t_sys_parameter.remark
* @return the value of t_sys_parameter.remark
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
public String getRemark() {
return remark;
}
/**
* This method was generated by MyBatis Generator. This method sets the value of the database column t_sys_parameter.remark
* @param remark the value for t_sys_parameter.remark
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
public void setRemark(String remark) {
this.remark = remark == null ? null : remark.trim();
}
/**
* This method was generated by MyBatis Generator. This method corresponds to the database table t_sys_parameter
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (getClass() != that.getClass()) {
return false;
}
TSysParameter other = (TSysParameter) that;
return (this.getParameterId() == null ? other.getParameterId() == null
: this.getParameterId().equals(other.getParameterId()))
&& (this.getParameterName() == null ? other.getParameterName() == null
: this.getParameterName().equals(other.getParameterName()))
&& (this.getParameterValue() == null ? other.getParameterValue() == null
: this.getParameterValue().equals(other.getParameterValue()))
&& (this.getRemark() == null ? other.getRemark() == null : this.getRemark().equals(other.getRemark()));
}
/**
* This method was generated by MyBatis Generator. This method corresponds to the database table t_sys_parameter
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getParameterId() == null) ? 0 : getParameterId().hashCode());
result = prime * result + ((getParameterName() == null) ? 0 : getParameterName().hashCode());
result = prime * result + ((getParameterValue() == null) ? 0 : getParameterValue().hashCode());
result = prime * result + ((getRemark() == null) ? 0 : getRemark().hashCode());
return result;
}
/**
* This method was generated by MyBatis Generator. This method corresponds to the database table t_sys_parameter
* @mbg.generated Mon Sep 23 17:26:31 CST 2019
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
sb.append("Hash = ").append(hashCode());
sb.append(", parameterId=").append(parameterId);
sb.append(", parameterName=").append(parameterName);
sb.append(", parameterValue=").append(parameterValue);
sb.append(", remark=").append(remark);
sb.append(", serialVersionUID=").append(serialVersionUID);
sb.append("]");
return sb.toString();
}
}
生成的對象是上圖這樣的,除了註釋其它都能滿足。那麼擺在我們面前的問題就成了如何給生成的bean對象加上備註。通過集成swagger我們發現它提供了幾個註解的功能
@ApiModel("操作歷史表") @ApiModelProperty(value ="操作歷史ID")其中第一個註解是類說明,第二個註解是字段說明。所以現在問題就轉變爲了如何爲bean對象添加相應的對象。這裏又有兩種方案可以去解決。
方案一:重寫mybatis生成bean的源代碼,使它在生成bean的時候就生成相應註解。
方案二:自己動手重寫bean.
由於時間緊迫,故不想花太多精力去研究源碼因此我選擇方案二。
方案二我是這樣考慮的,首先我讀取事先生成的bean對象,然後根據其關鍵字添加相應的註解。再把生成的字符串寫入新的文件中。現在的問題進一步轉化爲如何知道相應類和字段的註解。這裏又有幾種方案。比如讀取數據庫配置信息,比如自己寫配置文件。都能實現,各有特點。而我由於已經在數據庫設計階段就形成了,數據表說明的excel表,因此我就利用讀取excel表的方式獲取對應的註解信息。
表信息如下:
一:POI方式讀取excel數據
package com.zte.common.util;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class ReadCVSUtil{
private static XSSFWorkbook hssfWorkbook;
public static Map<String, List<String>> readCVS() throws FileNotFoundException, IOException {
hssfWorkbook = new XSSFWorkbook(new FileInputStream(PropertiesUtil.getValue("FileParams.properties", "file.path")));
// 2.獲取要解析的表格(第一個表格)
XSSFSheet sheet = hssfWorkbook.getSheetAt(0);
Map<String, List<String>> map = new HashMap<String, List<String>>();
// 獲得最後一行的行號
int lastRowNum = sheet.getLastRowNum();
String stringCellValue0=null;
String stringCellValue1=null;
List<String> wordList=null;
// List<String> beanNameList=new ArrayList<String>();
// List<String> beanParamsList=new ArrayList<String>();
// List<String> wordList=new ArrayList<String>();
// List<Integer> rowsList=new ArrayList<Integer>();
for (int i = 1; i <= lastRowNum; i++) {// 遍歷每一行
// 3.獲得要解析的行
XSSFRow row = sheet.getRow(i);
double stringCellValue2 = row.getCell(2).getNumericCellValue();
// 4.獲得每個單元格中的內容(String)
String stringCellValue12 = row.getCell(12).getStringCellValue();
if(stringCellValue2==1 && i == 1) {
if( row.getCell(0)!=null) {
stringCellValue0 = row.getCell(0).getStringCellValue();
}
if( row.getCell(1)!=null) {
stringCellValue1 = row.getCell(1).getStringCellValue();
}
wordList = new ArrayList<String>();
}
if (stringCellValue2==1 && i != 1) {
map.put(stringCellValue0+"|"+stringCellValue1, wordList);
wordList = new ArrayList<String>();
if( row.getCell(0)!=null) {
stringCellValue0 = row.getCell(0).getStringCellValue();
}
if( row.getCell(1)!=null) {
stringCellValue1 = row.getCell(1).getStringCellValue();
}
}
wordList.add(stringCellValue12);
if(i==lastRowNum) {
map.put(stringCellValue0+"|"+stringCellValue1, wordList);
}
}
return map;
}
}
通過該方法將表信息直接映射成了一個key爲表名+表說明的map,value爲字段說明的list對象。
二:文件讀寫給bean對象生成註解信息
package com.zte.common.util;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class AutoCreateBeanUtil {
/**
* 指定目錄下的bean文件生成註解
* @throws IOException
*/
public static void autoCreateAnntation() throws IOException {
File file = new File(PropertiesUtil.getValue("FileParams.properties", "bean.path"));
File[] fileList = file.listFiles();
//通過數據庫字典excel讀取註解屬性
Map<String, List<String>> map = ReadCVSUtil.readCVS();
for (int i = 0; i < fileList.length; i++) {
if (fileList[i].isFile()) {
String fileName = fileList[i].getName();
String[] name = fileName.split("\\.");
if (name[0].endsWith("Example")) {
continue;
}
for (String key : map.keySet()) {
if (key.split("\\|")[0].replaceAll("_", "").toLowerCase().equals(name[0].toLowerCase())) {
readFile(fileList[i], map.get(key), key.split("\\|")[1]);
}
}
}
}
List<File> sourceFiles = new ArrayList<File>();
String targetpath=PropertiesUtil.getValue("FileParams.properties", "target.path");
//自動編譯bean文件,爲後續反射獲取bean屬性做準備
compiler(file,targetpath,sourceFiles);
compilerJavaFile(sourceFiles,targetpath);
}
/**
* bean文件添加swagge註解
* @param file 目標java文傑
* @param apiName 類註解
* @param list 字段註釋
* @throws IOException
*/
public static void readFile(File file, List<String> list, String apiName) throws IOException {
List<String> temp = new ArrayList<>();
FileInputStream inputStream = new FileInputStream(file);
int index = 0;
// 設置inputStreamReader的構造方法並創建對象設置編碼方式爲gbk
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
String str = null;
while ((str = bufferedReader.readLine()) != null) {
if (str.trim().startsWith("public class")) {
temp.add("import io.swagger.annotations.ApiModelProperty;");
temp.add("import io.swagger.annotations.ApiModel;");
temp.add(" ");
temp.add("@ApiModel(" +"\""+ apiName +"\""+")");
}
if (str.trim().startsWith("private") && !str.trim().startsWith("private static")) {
temp.add(" @ApiModelProperty(value =" +"\""+ list.get(index) +"\""+ ")");
index++;
}
temp.add(str);
}
// close
inputStream.close();
bufferedReader.close();
// 下面按行讀。我實現的其實就是變相的分行打印,如果有更好的方法請大家指教
OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
BufferedWriter bw = new BufferedWriter(os);
PrintWriter out = new PrintWriter(bw);
for (String aTemp : temp) {
out.println(aTemp);
}
bw.close();
os.close();
out.close();
}
/**
* 遞歸獲取java文件
*
* @param file
* 需要編譯的文件夾
* @param targetPath
* 編譯後class類文件存放目錄
*/
public static void compiler(File file, String targetPath, List<File> sourceFiles) {
File targetDir = new File(targetPath);
if (!targetDir.exists()) {
targetDir.mkdirs();
}
if (file != null && file.exists()) {
File[] listFiles = file.listFiles();
if (null == listFiles || listFiles.length == 0) {
return;
}
for (File file2 : listFiles) {
if (file2.isDirectory()) {
compiler(file2, targetPath, sourceFiles);
} else {
if (file2.getName().endsWith(".java")) {
// 將源文件目錄中的Java文件加入集合中
sourceFiles.add(file2);
}
}
}
} else {
System.out.println("傳入格式未知文件");
}
}
/**
* 編譯java文件
*
* @param sourcePath
* @param targerPath
* @return
*/
public static boolean compilerJavaFile(List<File> sourceFile, String targerPath) {
JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = comp.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileMgr.getJavaFileObjectsFromFiles(sourceFile);
List<String> options = new ArrayList<String>();
options.add("-d");
options.add(targerPath);
return comp.getTask(null, fileMgr, null, options, null, compilationUnits).call();
}
}
通過閱讀上述代碼,不光爲指定目錄下的所有bean對象添加了相應註釋外,還爲生成的bean對象字段編譯成.class文件。這樣做又是爲了什麼呢?
細心的讀者可能會發現,在我進行代碼分析的時候我說過生成最終的接口代碼時,由於要添加swagger接口註釋,因此我們需要通過反射獲取對象的屬性。而反射都是操作.class文件。這裏有兩種方案。
方案一:生成bean文件後手動編譯代碼生成class文件,再執行後續的自動生成rest風格的增刪改查接口代碼。
方案二:自動編譯class文件,馬上自動生成接口代碼。
本文既然是快速構建,那麼毫無疑問是要採用第二種方案的。既然選擇了遠方,便只顧往難的那條路去走,這樣你纔會發現時時刻刻都在提高。
因此上述代碼我自動將生成的代碼編譯到了指定的目錄下,爲後續的步驟做準備。
三:利用java反射自動生成接口代碼(包含文檔註釋)
@ApiOperation(value = "查詢列表接口", notes = "")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "path", name = "pageNum", value = "頁碼", required = true, dataType = "Integer"),
@ApiImplicitParam(paramType = "path", name = "pageSize", value = "每頁條數", required = true, dataType = "Integer"),
@ApiImplicitParam(paramType = "query", name = "functionId", value = "功能id", required = false, dataType = "Long"),
@ApiImplicitParam(paramType = "query", name = "functionRightsMapId", value = " ", required = false, dataType = "Long"),
@ApiImplicitParam(paramType = "query", name = "rightsGroupId", value = "權限組id", required = false, dataType = "Long"), })
@GetMapping(value = "list/{pageNum}/{pageSize}", produces = MediaType.APPLICATION_JSON_VALUE)
public JsonResult list(@PathVariable("pageNum") int pageNum, @PathVariable("pageSize") int pageSize,
TFunctionRightsMap item) {
TFunctionRightsMapExample example = null;
// 補充查詢參數開始
if (item != null) {
example = new TFunctionRightsMapExample();
TFunctionRightsMapExample.Criteria criteria = example.createCriteria();
// 功能id
if (item.getFunctionId() != null) {
criteria.andFunctionIdEqualTo(item.getFunctionId());
}
//
if (item.getFunctionRightsMapId() != null) {
criteria.andFunctionRightsMapIdEqualTo(item.getFunctionRightsMapId());
}
// 權限組id
if (item.getRightsGroupId() != null) {
criteria.andRightsGroupIdEqualTo(item.getRightsGroupId());
}
}
// 補充查詢參數結束
Page<List<TFunctionRightsMap>> page = PageHelper.startPage(pageNum, pageSize);
List<TFunctionRightsMap> list = tFunctionRightsMapMapper.selectByExample(example);
PageInfo info = new PageInfo(page.getPageNum(), page.getPageSize(), page.getTotal(), page.getPages(), list);
return JsonResult.getSuccess(info);
}
@ApiOperation(value = "查詢單個接口", notes = "")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "path", name = "id", value = "id", required = true, dataType = "Long") })
@GetMapping(value = "item/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public JsonResult item(@PathVariable("id") long id) {
return JsonResult.getSuccess(tFunctionRightsMapMapper.selectByPrimaryKey(id));
}
@ApiOperation(value = "修改接口", notes = "")
@PostMapping(value = "update/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public JsonResult update(@PathVariable("id") long id, @RequestBody TFunctionRightsMap item) {
item.setFunctionRightsMapId(id);
return JsonResult.getSuccess(tFunctionRightsMapMapper.updateByPrimaryKeySelective(item));
}
@ApiOperation(value = "刪除接口", notes = "")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "path", name = "id", value = "id", required = true, dataType = "Long"), })
@GetMapping(value = "delete/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public JsonResult delete(@PathVariable("id") long id) {
return JsonResult.getSuccess(tFunctionRightsMapMapper.deleteByPrimaryKey(id));
}
@ApiOperation(value = "新增接口", notes = "")
@PostMapping(value = "insert", produces = MediaType.APPLICATION_JSON_VALUE)
public JsonResult insert(@RequestBody TFunctionRightsMap item) {
return JsonResult.getSuccess(tFunctionRightsMapMapper.insertSelective(item));
}
從上面代碼可以發現,我們的增刪改查、分頁接口其實都大同小異,只是操作的類不一樣,對象不一樣。那麼我們可否用一個模板方式進行動態替換呢?答案是肯定的,通過以前前端的開發,我敏銳的發現freemarker簡直就是爲這個需求定製的。
編寫模板
package com.zte.rest;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zte.mapper.${mapper};
import com.zte.model.${model};
import com.zte.model.${example};
import com.zte.param.PageInfo;
import com.zte.common.util.JsonResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
@RestController
@RequestMapping("/${mapping}")
@Api(tags = "${tags}")
public class ${class} {
Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
${mapper} ${mapperclass1};
@ApiOperation(value = "查詢列表接口", notes = "")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "path", name = "pageNum", value = "頁碼", required = true, dataType = "Integer"),
@ApiImplicitParam(paramType = "path", name = "pageSize", value = "每頁條數", required = true, dataType = "Integer"),
${ApiImplicitParam}})
@GetMapping(value = "list/{pageNum}/{pageSize}", produces = MediaType.APPLICATION_JSON_VALUE)
public JsonResult list(@PathVariable("pageNum") int pageNum, @PathVariable("pageSize") int pageSize,
${model} item) {
${example} example = new ${example}();
// 補充查詢參數開始
// 補充查詢參數結束
Page<List< ${model}>> page = PageHelper.startPage(pageNum, pageSize);
List< ${model}> list = ${mapperclass1}.selectByExample(example);
PageInfo info = new PageInfo(page.getPageNum(), page.getPageSize(), page.getTotal(), page.getPages(), list);
return JsonResult.getSuccess(info);
}
@ApiOperation(value = "查詢單個接口", notes = "")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "path", name = "id", value = "id", required = true, dataType = "Long") })
@GetMapping(value = "item/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public JsonResult item(@PathVariable("id") long id) {
return JsonResult.getSuccess(${mapperclass1}.selectByPrimaryKey(id));
}
@ApiOperation(value = "修改接口", notes = "")
@PostMapping(value = "update", produces = MediaType.APPLICATION_JSON_VALUE)
public JsonResult update @RequestBody ${model} item) {
return JsonResult.getSuccess(${mapperclass1}.updateByPrimaryKeySelective(item));
}
@ApiOperation(value = "刪除接口", notes = "")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "path", name = "id", value = "id", required = true, dataType = "Long"), })
@GetMapping(value = "delete/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public JsonResult delete(@PathVariable("id") long id) {
return JsonResult.getSuccess(${mapperclass1}.deleteByPrimaryKey(id));
}
@ApiOperation(value = "新增接口", notes = "")
@PostMapping(value = "insert", produces = MediaType.APPLICATION_JSON_VALUE)
public JsonResult insert(@RequestBody ${model} item) {
return JsonResult.getSuccess(${mapperclass1}.insertSelective(item));
}
}
因此現在我們只需要替換掉模板中的變量,就能新生成一個接口類。
package com.zte.common.util;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
public class AutoCreateBeanUtil {
/**
* 指定目錄下的bean文件生成註解
* @throws IOException
*/
public static void autoCreateAnntation() throws IOException {
File file = new File(PropertiesUtil.getValue("FileParams.properties", "bean.path"));
File[] fileList = file.listFiles();
//通過數據庫字典excel讀取註解屬性
Map<String, List<String>> map = ReadCVSUtil.readCVS();
for (int i = 0; i < fileList.length; i++) {
if (fileList[i].isFile()) {
String fileName = fileList[i].getName();
String[] name = fileName.split("\\.");
if (name[0].endsWith("Example")) {
continue;
}
for (String key : map.keySet()) {
if (key.split("\\|")[0].replaceAll("_", "").toLowerCase().equals(name[0].toLowerCase())) {
readFile(fileList[i], map.get(key), key.split("\\|")[1]);
}
}
}
}
List<File> sourceFiles = new ArrayList<File>();
String targetpath=PropertiesUtil.getValue("FileParams.properties", "target.path");
//自動編譯bean文件,爲後續反射獲取bean屬性做準備
compiler(file,targetpath,sourceFiles);
compilerJavaFile(sourceFiles,targetpath);
}
/**
* bean文件添加swagge註解
* @param file 目標java文傑
* @param apiName 類註解
* @param list 字段註釋
* @throws IOException
*/
public static void readFile(File file, List<String> list, String apiName) throws IOException {
List<String> temp = new ArrayList<>();
FileInputStream inputStream = new FileInputStream(file);
int index = 0;
// 設置inputStreamReader的構造方法並創建對象設置編碼方式爲gbk
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
String str = null;
while ((str = bufferedReader.readLine()) != null) {
if (str.trim().startsWith("public class")) {
temp.add("import io.swagger.annotations.ApiModelProperty;");
temp.add("import io.swagger.annotations.ApiModel;");
temp.add(" ");
temp.add("@ApiModel(" +"\""+ apiName +"\""+")");
}
if (str.trim().startsWith("private") && !str.trim().startsWith("private static")) {
temp.add(" @ApiModelProperty(value =" +"\""+ list.get(index) +"\""+ ")");
index++;
}
temp.add(str);
}
// close
inputStream.close();
bufferedReader.close();
// 下面按行讀。我實現的其實就是變相的分行打印,如果有更好的方法請大家指教
OutputStreamWriter os = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
BufferedWriter bw = new BufferedWriter(os);
PrintWriter out = new PrintWriter(bw);
for (String aTemp : temp) {
out.println(aTemp);
}
bw.close();
os.close();
out.close();
}
/**
* 遞歸獲取java文件
*
* @param file
* 需要編譯的文件夾
* @param targetPath
* 編譯後class類文件存放目錄
*/
public static void compiler(File file, String targetPath, List<File> sourceFiles) {
File targetDir = new File(targetPath);
if (!targetDir.exists()) {
targetDir.mkdirs();
}
if (file != null && file.exists()) {
File[] listFiles = file.listFiles();
if (null == listFiles || listFiles.length == 0) {
return;
}
for (File file2 : listFiles) {
if (file2.isDirectory()) {
compiler(file2, targetPath, sourceFiles);
} else {
if (file2.getName().endsWith(".java")) {
// 將源文件目錄中的Java文件加入集合中
sourceFiles.add(file2);
}
}
}
} else {
System.out.println("傳入格式未知文件");
}
}
/**
* 編譯java文件
*
* @param sourcePath
* @param targerPath
* @return
*/
public static boolean compilerJavaFile(List<File> sourceFile, String targerPath) {
JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileMgr = comp.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileMgr.getJavaFileObjectsFromFiles(sourceFile);
List<String> options = new ArrayList<String>();
options.add("-d");
options.add(targerPath);
return comp.getTask(null, fileMgr, null, options, null, compilationUnits).call();
}
}
至此一個完整的自動代碼生成工具構建完成,只需要幾個簡單配置,分分鐘搭建開發工程。