程式碼自動生成,避免重複性勞動,程式猿解放時間陪家人
阿新 • • 發佈:2019-01-10
前序
你有沒有覺得自己常常寫重複的程式碼,面試造輪船,上班擰螺絲?
你有沒有常覺得,自己明明可以更快地寫好功能模組,卻要不斷地 Ctrl + C 、Ctrl + V ?
一、java程式碼自動生成系統開發
1.1 開發環境
- java JDK 8或更新
- Tomcat 6及以上,tomcat8以上更佳,或者jetty7以上
- intellij idea 或者 eclipse
- maven
- mysql 5.7及以上(非必須)
- idea 需要安裝lombok外掛,一塊縮減程式碼的getter setter log等的外掛
1.2 已實現目標
- 通過配置實體模板自動生成MVC層,及mysql的指令碼
- 通過配置現有系統的開發模板(或者開發流程),進行全自動生成各層的程式碼
1.3 系統推介
系統模組結構圖,如下圖所示,圖中展示了,模板層,基礎層,程式碼自動生成層,以及各層的必備工具utils層。
生成的系統模組邏輯程式碼如下圖所示,這些程式碼完全是根據模板,通過IO流,配置規則,自動讀取包名進行切割完成IO輸出,註釋和配置分層都會自動生成,程式碼可以隨意更改,完全符合你需要更改成自己的模組生成的自由度。
二、系統亮點展示
2.1 亮點一、自動讀取實體,根據註釋生成js table所需的程式碼
如下圖,是js table節選程式碼塊,如果是做管理後臺的頁面,bootstrap 裡頭有js table控制元件 這是標配了。
而這一部分,程式碼的相似性,可以不誇張地說,所有的模組,所有目錄下的頁面都會包含,如果,每次都要去開發,都要去複製貼上修改,這是多麼痛苦的領悟!
舉例說明: 下面有這麼一個實體,實現的是機構資訊這麼個實體。
package com.xxx.cum.vo; import lombok.Data; import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; /** * 機構資訊 * @author XXX * @version Id; OrgInfo, 2018/8/9 16:17 XXX Exp $$ */ @Data public class OrgInfo implements Serializable { private static final long serialVersionUID = ${gETSerialVersionUID }; /** * ID */ private Long id; /** * 機構編號 */ private String orgCode; /** * 機構名稱 */ private String orgName; /** * 機構全稱 */ private String orgFullName; /** * 收款開戶行銀行編碼 */ private String bankCode; /** * 收款開戶行名稱 */ private String bankName; /** * 收款銀行卡賬號' */ private String bankCardNo; /** * 銀聯分潤比例 */ private BigDecimal unionpayBenefitRate; /** * 品牌服務費率 */ private BigDecimal brandRate; /** * 轉接清算費率 */ private BigDecimal transferRate; /** * 付款摘要 */ private String payoutSummary; /** * 是否集團結算 0否 1是 */ private String groupSettle; /** * 是否再付 0否 1是 */ private String payAgain; /** * 公私標識 0對公,1對私 */ private String perEntFlag; /** * 清結算模式 1自主清算 2代理清算 3自主清算+代理清算 */ private String settleType; /** * 協議有效期 */ private Date expiredAt; /** * 機構是否停用狀態碼(預設0,0未停用、1停用、其他待定) */ private String state; /** * 建立時間 */ private Date createdAt; /** * 建立人 */ private String createdBy; /** * 更新時間 */ private Date updatedAt; /** * 更新人 */ private String updatedBy; }
實現步驟:
1)、先讀取檔案
/**
* 通過類實體獲取DataTable
* @return
* @throws IOException
*/
public static String doSomeThing() throws IOException {
StringBuilder outputSb = new StringBuilder();
//讀檔案
File directory = new File("F:\\study\\project\\noUsage\\codeGenerator\\dedd\\com.william\\src\\main\\java\\com\\william\\auto\\temp_help\\template_oss_web");
for (File tempFile : directory.listFiles()) {
if (tempFile.getName().indexOf(".xml") != -1) {
FileReader fileReader = new FileReader(tempFile);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String s = null;
StringBuilder sb = new StringBuilder();
while(( s = bufferedReader.readLine() ) != null ) {
//System.out.println(s);
sb.append(s);
}
List<String> annoList = getRegexAnnotationExp("\\/\\*([^\\*^\\/]*|[\\*^\\/*]*|[^\\**\\/]*)*\\*\\/",sb);
List<String> fieldList = getRegexFieldExp("private\\s+([a-zA-Z]*)*\\s+([a-zA-Z]*)*",sb);
for (int i=0;i<fieldList.size();i++) {
if (fieldList.size() > i) {
if (fieldList.get(i).equalsIgnoreCase("final")) {
continue;
}
//{'title': '選擇', 'column': 'id'},
String item = "{'title':'"+annoList.get(i)+",'column':'"+fieldList.get(i)+"'},";
outputSb.append(item+"\n");
//System.out.println(item);
}
}
//輸出結果
System.out.println(outputSb.toString());
}
}
return outputSb.toString();
}
2)、讀取實體,以註釋作為js table的title,屬性作為值
/**
* 讀取註釋文字
* @param regexExp
* @param sb
*/
private static List<String> getRegexAnnotationExp(String regexExp, StringBuilder sb) {
List<String> resultList = new ArrayList<String>();
Pattern pattern = Pattern.compile(regexExp);
String searchPlainText = sb.toString();
Matcher matcher = pattern.matcher(searchPlainText);
while (matcher.find()) {
String groupExp = matcher.group(0).replaceAll("/\\*+\\s+\\*","").replaceAll("\\s+\\*/","");
resultList.add(groupExp);
//System.out.println("匹配到:"+groupExp+" 位置:(" + matcher.start()+","+matcher.end()+")");
}
return resultList;
}
/**
* 讀取屬性field
* @param regexExp
* @param sb
*/
private static List<String> getRegexFieldExp(String regexExp,StringBuilder sb) {
List<String> resultList = new ArrayList<String>();
Pattern pattern = Pattern.compile(regexExp);
String searchPlainText = sb.toString();
Matcher matcher = pattern.matcher(searchPlainText);
while (matcher.find()) {
String groupExp = matcher.group(0).replaceAll("private\\s+([a-zA-Z]*)*\\s+","");
resultList.add(groupExp);
//System.out.println("匹配到:"+groupExp+" 位置:(" + matcher.start()+","+matcher.end()+")");
}
return resultList;
}
生成的結果如下:
{'title':' 機構編號,'column':'orgCode'},
{'title':' 機構名稱,'column':'orgName'},
{'title':' 機構全稱,'column':'orgFullName'},
{'title':' 收款開戶行銀行編碼,'column':'bankCode'},
{'title':' 收款開戶行名稱,'column':'bankName'},
{'title':' 收款銀行卡賬號','column':'bankCardNo'},
{'title':' 銀聯分潤比例,'column':'unionpayBenefitRate'},
{'title':' 品牌服務費率,'column':'brandRate'},
{'title':' 轉接清算費率,'column':'transferRate'},
{'title':' 付款摘要,'column':'payoutSummary'},
{'title':' 是否集團結算 0否 1是,'column':'groupSettle'},
{'title':' 是否再付 0否 1是,'column':'payAgain'},
{'title':' 公私標識 0對公,1對私,'column':'perEntFlag'},
{'title':' 清結算模式 1自主清算 2代理清算 3自主清算+代理清算,'column':'settleType'},
{'title':' 協議有效期,'column':'expiredAt'},
{'title':' 機構是否停用狀態碼(預設0,0未停用、1停用、其他待定),'column':'state'},
{'title':' 建立時間,'column':'createdAt'},
{'title':' 建立人,'column':'createdBy'},
{'title':' 更新時間,'column':'updatedAt'},
{'title':' 更新人,'column':'updatedBy'},
2.2 自動替換程式碼模板的內容(程式碼為節選塊)
File templateDirectory = new File(mybatisPath);
if (templateDirectory.isDirectory()) {
File[] templateFiles = templateDirectory.listFiles();
for (File templateFileItem : templateFiles) {
String suffixGenerate = ".xml";
String template = "";
FileReader fr = new FileReader(templateFileItem);
BufferedReader br = new BufferedReader(fr);
String s,packagePath = "";
while ((s = br.readLine()) != null) {
if(s.trim().startsWith("package ")){
suffixGenerate = ".java";
packagePath = s;
packagePath = packagePath.replaceAll("package","").trim().toLowerCase().replaceAll(";","");
logger.info("package path: " + packagePath + "\n");
}
template += s + "\n";
}
fr.close();
// 替換內容
template = StringUtils.toReplaceTemplateKey(template, new String[]{
"Template",
"template",
"author",
"TemplateCN",
"DateTime",
"TemplateUpdateReturn",
"TemplateDeleteReturn",
"TemplateAddReturn",
"TemplateReturnUpdateType",
"TemplateReturnDeleteType",
"TemplateReturnAddType"
},
new String[]{
className,
StringUtils.toLower(className),
author,
classCNName,
new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date()),
TemplateUpdateReturn,
TemplateDeleteReturn,
TemplateAddReturn,
TemplateReturnUpdateType,
TemplateReturnDeleteType,
TemplateReturnAddType
});
String realProjectFilePath = "";
for (String tempPath:packagePath.split("\\.")) {
logger.info("tempPath:" + tempPath + "\tpackagePath:"+packagePath);
if (StringUtils.isNull(realProjectFilePath)) {
realProjectFilePath = realProjectFilePath + tempPath ;
} else {
realProjectFilePath = realProjectFilePath +File.separator+ tempPath ;
}
logger.info("切割後生成的目錄為:==> packagePath:"+realProjectFilePath);
}
String path = PROJECT_PATH + File.separator + PropertiesHelper.getValueByKey("rootPackage").replaceAll("\\.","\\\\") + File.separator + yyyyMMddHHmmss + File.separator+ realProjectFilePath + File.separator;
File file = new File(path);
if(!file.exists()){
file.mkdirs();
}
//File fileBiz = new File(path + "\\" + fileNameTemplate + suffixTemplate);
String fileItemName = templateFileItem.getName().replaceAll("_","").replaceAll("CustInfoConfig",className).replaceAll("Template",className).split("\\.")[0];
FileWriter fw = new FileWriter(path + File.separator + fileItemName + suffixGenerate);
BufferedWriter bw = new BufferedWriter(fw);
bw.write(template);
bw.close();
logger.info("生成測試類檔案:[" + fileItemName + suffixGenerate + "]成功" );
logger.info("檔案根目錄點選開啟:[" + "file:" + File.separator + File.separator+ File.separator + path + "]成功" );
}
如你所有的模板中,有個類註解
/**
* ${TemplateCN}查詢請求
* @author ${author}
* @version Id: ${Template}QueryRequest.java , ${DateTime} ${author} Exp $
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class ${Template}QueryRequest extends CumRequest {
模板中的${TemplateCN} ,${author} ,${Template},${DateTime}
可以通過全域性檔案的替換功能,實現替換
替換後的效果:
/**
* 商戶查詢請求
* @author zs
* @version Id: OssCustInstRateQueryRequest.java , 2018-09-03 17:08 zs Exp $
*/
2.3 自動生成各層的MVC程式碼,及資料庫
/**
* 開始執行自動生成程式碼
* @param className
*/
private static void autoGenerator(String className) {
try{
//生成實體物件
Class.forName(entityPackage + "." + className);
//生成實體檔案
writeEntity(className);
//生成dao
writeDao(className);
//生成service
writeService(className);
//生成service的實現類
writeServiceImpl(className);
//生成controller類
writeController(className);
//生成xml檔案
writeXml(className);
//生成ServiceTest的實現類
writeFileBeforeReplace(className,"TemplateManageServiceTest",className + "ManageServiceTest",".xml",".java");
//生成dubbo-service-provider.xml配置檔案
writeDubboProviderXml(className);
System.out.println("\n======生成的建表語句如下:======");
System.out.println(SqlGenerator.generateSql(entityPackage + "." + className,"id"));
}catch (Exception e){
logger.error(e.getStackTrace(),e);
}
}
如dao層、sql指令碼生成的程式碼節選:
/**
* 生成dao程式碼
* @param className
*/
private static void writeDao(String className) {
String daoPath = StringUtils.getJavaPath() + templatePackage;
try {
String targetDao = PROJECT_PATH + File.separator + PropertiesHelper.getValueByKey("rootPackage").replaceAll("\\.","/") + File.separator + "dao//";
File file = new File(targetDao);
if(!file.exists()){
file.mkdirs();
}
File fileAction = new File(targetDao + className + "Dao.java");
if (!fileAction.exists()) {
String template = "";
FileReader fr = new FileReader(daoPath + "TemplateDao.java");
BufferedReader br = new BufferedReader(fr);
String s;
boolean started = false;
while ((s = br.readLine()) != null) {
if(s.startsWith("package")){
//重新賦值
s = "package " + PropertiesHelper.getValueByKey("rootPackage") + ".dao;";
}
if(!started){
if(s.startsWith("import")){
String entityClass = PropertiesHelper.getValueByKey("rootPackage") + ".entity." + className + ";";
template += "import " + entityClass + "\n";
started = true;
}
}
template += s + "\n";
}
fr.close();
// 替換內容
template = template.replaceAll("Template", className).replaceAll("template", className);
FileWriter fw = new FileWriter(targetDao + className + "Dao.java");
BufferedWriter bw = new BufferedWriter(fw);
bw.write(template);
bw.close();
}
logger.info("生成dao類:[" + className + "Dao]成功" );
} catch (Exception e) {
e.printStackTrace();
}
}
sql指令碼根據配置的實體生成:
public class SqlGenerator {
static Logger logger = Logger.getLogger(SqlGenerator.class);
public static void main(String[] args) {
String className = "User";
System.out.println(generateSql(className,"id"));
}
/**
* 根據實體類生成建表語句
* @author
* @param className 全類名
*/
public static String generateSql(String className,String indexKey){
try {
Class<?> clz = Class.forName(className);
className = clz.getSimpleName();
Field[] fields = clz.getDeclaredFields();
StringBuffer column = new StringBuffer();
String varchar = " varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,";
for (Field f : fields) {
boolean isStatic = Modifier.isStatic(f.getModifiers());
if(!isStatic) {
if (indexKey.equals(f.getName())) {
column.append("\n\t`" + f.getName() + "` int(11) NOT NULL AUTO_INCREMENT,");
} else {
Class type = f.getType();
if (type == String.class) {
column.append("\n\t`" + f.getName() + "`" + varchar);
}
}
}
}
StringBuffer sql = new StringBuffer();
sql.append("DROP TABLE IF EXISTS `"+ RuleUtils.getTableNameByBean(className) + "`; ")
.append("\nCREATE TABLE `"+ RuleUtils.getTableNameByBean(className) + "`(")
.append(column)
.append(" \n\t PRIMARY KEY (`" + indexKey + "`) USING BTREE")
.append("\n) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci;");
return sql.toString();
} catch (ClassNotFoundException e) {
e.printStackTrace();
logger.debug("該類未找到!");
return null;
}
}
}
程式碼下載
對本程式碼感興趣的同學,可以下載下來使用,下載地址
對本程式碼感興趣的同學,可以下載下來使用,下載地址
對本程式碼感興趣的同學,可以下載下來使用,下載地址