MyBatis-Plus结合Swagger实现接口代码及文档自动生成工具(基础篇-基于配置文件)

              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> page = PageHelper.startPage(pageNum, pageSize);
        List 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表的方式获取对应的注解信息。

                                                                   表信息如下:

MyBatis-Plus结合Swagger实现接口代码及文档自动生成工具(基础篇-基于配置文件)_第1张图片

 

一: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> readCVS() throws FileNotFoundException, IOException {

		hssfWorkbook = new XSSFWorkbook(new FileInputStream(PropertiesUtil.getValue("FileParams.properties", "file.path")));
		// 2.获取要解析的表格(第一个表格)
		XSSFSheet sheet = hssfWorkbook.getSheetAt(0);
		Map> map = new HashMap>();
		
		// 获得最后一行的行号
		int lastRowNum = sheet.getLastRowNum();
		String stringCellValue0=null;
		String stringCellValue1=null;
		List wordList=null;
		// List beanNameList=new ArrayList();
		// List beanParamsList=new ArrayList();
		// List wordList=new ArrayList();
		// List rowsList=new ArrayList();
		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();
			}
			
			if (stringCellValue2==1 && i != 1) {
				map.put(stringCellValue0+"|"+stringCellValue1, wordList);
				wordList = new ArrayList();
				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> 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 sourceFiles = new ArrayList();
		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 list, String apiName) throws IOException {
		List 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 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 sourceFile, String targerPath) {
        JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager fileMgr = comp.getStandardFileManager(null, null, null);
		Iterable compilationUnits = fileMgr.getJavaFileObjectsFromFiles(sourceFile);
		List options = new ArrayList();
        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> page = PageHelper.startPage(pageNum, pageSize);
        List 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> 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> 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 sourceFiles = new ArrayList();
		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 list, String apiName) throws IOException {
		List 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 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 sourceFile, String targerPath) {
        JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager fileMgr = comp.getStandardFileManager(null, null, null);
		Iterable compilationUnits = fileMgr.getJavaFileObjectsFromFiles(sourceFile);
		List options = new ArrayList();
        options.add("-d");
        options.add(targerPath);
		return comp.getTask(null, fileMgr, null, options, null, compilationUnits).call();

	}

}

至此一个完整的自动代码生成工具构建完成,只需要几个简单配置,分分钟搭建开发工程。

 

插件定义源码

插件实现原源码

插件测试源码

 

 

你可能感兴趣的:(自动化代码)