代碼生成工具dgen使用說明

前言

項目組但願能有一個比較完善,能夠生成各種代碼的工具,由於以前寫過因此趁這兩天從新弄了個。java

代碼生成工具命名爲dgen -> dexcoder-generator,旨在提升開發人員效率,避免重複勞動。mysql

理論上能夠生成任何想要的代碼文件,包括實體類、dao、service及頁面文件等,另外能夠方便的實現擴展生成本身想要的東西。sql

爲了方便開發避免重複造輪子,依賴了一些開源的第三方組件,主要爲dom4jvelocity數據庫

dgen的使用步驟爲:編寫配置文件 -> 編寫模板文件 -> 運行。xcode

下面就按這個順序對dgen進行說明,並會在最後介紹如何對dgen進行自定義的擴展。dom

配置文件說明

dgen默認的配置文件名爲dgen.xml,固然能夠隨意改變它,只需在啓動時指定文件名便可。ide

dgen.xml文件是全部配置的入口,裏面包含了工具運行時須要的各種信息,當配置信息較多時能夠拆分紅多個文件,使用include標籤引入。工具

dgen.xml文件樣例:ui

 
 
 
  
  
           
  
  
<?xml version="1.0" encoding="UTF-8"?><config> <constants> <!--生成時文件已存在是否覆蓋 默認true--> <constant name="overwrite" value="true"/> <!--生成目標文件夾 不指定默認爲運行目錄--> <!--<constant name="targetDir" value="/Users/liyd/project"/>--> <!--是否運行在子模塊 爲true則生成代碼的文件夾會到上一層(父模塊)爲基準 默認false --> <constant name="runOnChildModule" value="false"/> </constants> <!--jdbc鏈接信息配置--> <jdbc> <property name="dialect" value="mysql"/> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/dexcoder?useUnicode=true&amp;characterEncoding=utf-8"/> <property name="username" value="root"/> <property name="password" value=""/> </jdbc> <!--數據類型轉換映射--> <converters> <convert dbType="number" javaType="java.lang.Long"/> <convert dbType="VARCHAR2" javaType="java.lang.String"/> <convert dbType="SYS.XMLTYPE" javaType="java.lang.String"/> <convert dbType="TIMESTAMP" javaType="java.util.Date"/> <convert dbType="datetime" javaType="java.util.Date"/> <convert dbType="BLOB" javaType="java.lang.Byte[]"/> </converters> <include file="demo/tableConfig.xml"/></config>
  • config根標籤,固定。
  • constants常量標籤,能夠包含任意數量的constant子標籤,每個constant子標籤都定義了一個常量。
    • jdbc數據庫鏈接配置信息。
    • converters數據類型轉換映射。dbType指定數據庫的類型,javaType指定你指望在生成Java類中的類型。
    • include標籤,引入其它的配置文件(固然也能夠不引入所有寫在當前配置文件當中)。

這裏把它拆分紅了2個文件,使用了include標籤,引入的tableConfig.xml文件樣例:this

 
 
 
  
  
           
  
  
<?xml version="1.0" encoding="UTF-8"?><config> <table name="USER" desc="用戶"> <task name="model"/> <task name="dao"/> </table> <tasks> <task name="model" class="com.dexcoder.dgen.generator.DefaultCodeGenerator"> <property name="template" value="demo/template/model.vm"/> <property name="beginFix" value=""/> <property name="endFix" value=""/> <property name="suffix" value=".java"/> <property name="moduleName" value="dexcoder-core"/> <property name="srcDir" value="src/main/java"/> <property name="package" value="com.dexcoder.model"/> </task> <task name="dao"> <property name="template" value="demo/template/dao.vm"/> <property name="beginFix" value=""/> <property name="endFix" value="Dao"/> <property name="suffix" value=".java"/> <property name="moduleName" value="dexcoder-core"/> <property name="srcDir" value="src/main/java"/> <property name="package" value="com.dexcoder.dao"/> </task> <task name="daoImpl"> <property name="template" value="demo/template/daoImpl.vm"/> <property name="beginFix" value=""/> <property name="endFix" value="DaoImpl"/> <property name="suffix" value=".java"/> <property name="moduleName" value="daoDir"/> <property name="srcDir" value="src/main/java"/> <property name="package" value="com.dexcoder.dao.impl"/> </task> <task name="service"> <property name="template" value="demo/template/service.vm"/> <property name="beginFix" value=""/> <property name="endFix" value="Service"/> <property name="suffix" value=".java"/> <property name="moduleName" value="serviceDir"/> <property name="srcDir" value="src/main/java"/> <property name="package" value="com.dexcoder.service"/> </task> </tasks></config>

裏面主要配置了各項任務的生成參數信息,及指定了哪張表須要生成哪些代碼任務。

  • config標籤,一樣固定。
  • table指定要生成代碼的表名,desc是表的備註。能夠配置多個多張表一塊兒生成。
    • task指定了該表須要運行哪幾個生成任務來生成相應的代碼。
  • tasks任務信息配置根標籤。
    • task具體的任務信息,name指定任務名。
    • property指定運行該任務所須要的屬性信息。如下幾個爲必須的固定屬性:
    • template指定模板文件。
    • beginFix生成代碼文件前綴。
    • endFix生成代碼文件後綴。
    • suffix生成代碼文件的擴展名。
    • moduleName生成代碼所在模塊的模塊名,多模塊項目時有用。
    • srcDir生成的源代碼文件夾。
    • package包名。

運行時參數說明

模板文件的編寫須要依賴於一些運行時的動態參數,因此先對運行時的參數做一下說明。

這裏的運行時參數,主要是爲模板文件服務的,也就是在模板文件中能夠直接使用的數據。主要有下面這些:

  • packageName生成的代碼文件所在包名。
  • importClasses須要導入的類,是一個List<String>集合。注意這裏的導入類只是生成過程當中產生的一些類型(如model中的變量類型),其它的須要在模板中自行書寫。
  • date當前日期信息。
  • table生成表的信息。
  • name表名。
  • desc表備註。
  • columns包含的列信息。
    • name列名。
    • camelName駱駝命名法名稱,首字母小寫。
    • firstUpperName駱駝命名法名稱,首字母大寫。
    • isPrimaryKey是否主鍵。
    • comment列備註。
    • dbType數據庫類型。
    • jdbcTypejdbc類型。
    • javaTypeJava類型,例:String。
    • javaClassJava類型class名,例:java.lang.String。
  • tasks當前表擁有的代碼生成任務。
  • task當前運行的任務信息。
  • name任務名。
  • clazz任務處理的class。
  • pluginMap任務包含的插件,Map<String,String>類型。
  • properties任務包含的屬性,Map<String,String>類型。

除了以上固定信息外,對於在任務運行時生成的一些類,能夠用如下方式訪問生成的類信息,這裏假設生成的類爲java.model.UserInfo

taskName請替換成實際的任務名,另外任務須要在運行以後才能用該方式訪問,對於一些有類依賴的生成,例如dao最好放在service以前生成以便在service中使用生成的dao類信息。

  • taskName.generatedLongClassName全類名,返回java.model.UserInfo
  • taskName.generatedShortClassName類名,返回UserInfo
  • taskName.firstLowerGeneratedClassName首字母小寫類名,返回userInfo
  • taskName.lineThroughClassName中劃線分隔小寫類名,返回user-info

編寫模板文件

模板採用了velocity組件來實現,根據上面介紹的動態參數能夠很容易的完成模板的編寫。下面是幾個模板的樣例。

model類模板:

 
 
 
  
  
           
  
  
package ${packageName};#foreach($im in ${importClasses})import ${im};#end/*** ${table.desc}** Author: Created by code generator* Date: ${date}*/public class ${model.generatedShortClassName} { /** serialVersionUID */ private static final long serialVersionUID = ${serialVersionUID}L;#foreach($column in ${table.columns})#if(${column.comment}) /** ${column.comment} */#end private ${column.javaType} ${column.camelName};#end#foreach($column in ${table.columns}) public ${column.javaType} get${column.firstUpperCamelName}() { return ${column.camelName}; } public void set${column.firstUpperCamelName}(${column.javaType} ${column.camelName}) { this.${column.camelName} = ${column.camelName}; }#end}

dao接口模板,這裏只生成了一個insert方法。

 
 
 
  
  
           
  
  
package ${packageName};import ${model.generatedLongClassName};/*** ${table.desc} dao接口** Author: Created by code generator* Date: ${date}*/public interface ${dao.generatedShortClassName} { /** * 新增記錄 * * @param ${model.firstLowerGeneratedClassName} */ void insert(${model.generatedShortClassName} ${model.firstLowerGeneratedClassName});}

運行

代碼調用方式

只需一行代碼:

 
 
 
  
  
           
  
  
StartupGenerator.run();

指定配置文件名,文件路徑相對於當前運行項目目錄

 
 
 
  
  
           
  
  
StartupGenerator.run("demo/dgen.xml");

jar包執行方式

直接運行:

 
 
 
  
  
           
  
  
java -jar codeGenerator.jar

指定配置文件名,文件路徑相對於當前運行項目目錄

 
 
 
  
  
           
  
  
java -jar codeGenerator.jar demo/dgen.xml

擴展

有些時候可能默認的功能沒法知足項目的需求,你能夠經過編寫一些自定義擴展來加強dgen的功能。

插件實現

插件演示一

這裏以model類實現了序列化,生成須要serialVersionUID爲例,dgen默認並無提供,能夠經過簡單的插件方式來實現(該插件已內置實現)。

首先實現GeneratorPlugin接口,該接口只有一個方法:

 
 
 
  
  
           
  
  
/** * 插件執行方法 * * @param table 當前運行任務表配置信息 * @param task 當前運行任務配置信息 * @param configuration 全部的配置信息 * @param templateText 模板內容 * @param context 模板解析時的VelocityContext */void execute(Table table, Task task, Configuration configuration, StringBuilder templateText, VelocityContext context);

方法的參數中能夠拿到全部的想要的信息,包括表信息、任務信息、dgen配置信息、模板內容、模板解析時的VelocityContext等,按需使用便可。

下面是serialVersionUID插件的實現代碼:

 
 
 
  
  
           
  
  
public void execute(Table table, Task task, Configuration configuration, StringBuilder templateText, VelocityContext context) { long serialVersionUID = Math.abs(UUID.randomUUID().getLeastSignificantBits()); context.put("serialVersionUID", serialVersionUID);}

只是添加了serialVersionUID變量,這樣在model模板中就能夠使用了,添加下面代碼便可:

 
 
 
  
  
           
  
  
/** serialVersionUID */private static final long serialVersionUID = ${serialVersionUID}L;

固然別忘了把它添加到任務配置中:

 
 
 
  
  
           
  
  
<task name="model" class="com.dexcoder.dgen.generator.DefaultCodeGenerator"> <plugin name="serialVersionUIDPlugin" value="com.dexcoder.dgen.plugins.SerialVersionUIDPlugin"/> <property name="template" value="demo/template/model.vm"/> <property name="beginFix" value=""/> <property name="endFix" value=""/> <property name="suffix" value=".java"/> <property name="moduleName" value="dexcoder-core"/> <property name="srcDir" value="src/main/java"/> <property name="package" value="com.dexcoder.model"/></task>

插件演示二

咱們再來實現一個Excel表格生成插件,假設咱們在生成model類的同時想生成一份表結構的Excel文件。

實現的步驟上相同,只須要修改方法實現部分便可,如下是源代碼,這裏對Excel生成時行了封裝,不在本文討論範圍:

 
 
 
  
  
           
  
  
public void execute(Table table, Task task, Configuration configuration, StringBuilder templateText, VelocityContext context) { ExcelSheet excelSheet = new ExcelSheet(); excelSheet.setSheetName("表信息"); List<String> rowTitles = new ArrayList<String>(); rowTitles.add("列名"); rowTitles.add("列備註"); rowTitles.add("數據庫類型"); rowTitles.add("是否主鍵"); excelSheet.setRowTitles(rowTitles); List<ExcelRow> rowList = new ArrayList<ExcelRow>(); for (Column column : table.getColumns()) { ExcelRow excelRow = new ExcelRow(); excelRow.addCell(column.getName()); excelRow.addCell(column.getComment()); excelRow.addCell(column.getDbType()); excelRow.addCell(column.getIsPrimaryKey()); rowList.add(excelRow); } excelSheet.setRows(rowList); List<ExcelSheet> sheetList = new ArrayList<ExcelSheet>(); sheetList.add(excelSheet); ExcelWriteTools.write(sheetList, new File("/Users/liyd/Desktop/db.xls"));}

代碼生成簡單自定義重寫

若是使用插件的方式沒法知足你的需求,能夠本身實現代碼生成的方法,只須要繼承AbstractCodeGenerator類實現generate方法便可。

默認的生成類爲DefaultCodeGenerator

 
 
 
  
  
           
  
  
/*** 默認代碼生成實現類** User: liyd* Date: 13-12-16* Time: 下午4:28*/public class DefaultCodeGenerator extends AbstractCodeGenerator { /** * 代碼生成方法 * * @param table * @param task * @param context * @param template */ @Override public void generate(Table table, Task task, VelocityContext context, StringBuilder template) { Set<String> importClasses = this.getColumnsImportClass(table.getColumns()); context.put("importClasses", importClasses); context.put("date", new Date()); context.put("table", table); context.put("task", task); context.put("packageName", task.getPackageName()); //任務生成的類信息 String generatedLongClassName = task.getGeneratedReferenceClassName(table.getName()); String generatedShotClassName = task.getGeneratedShotClassName(table.getName()); String firstLowerGeneratedClassName = NameUtils.getFirstLowerName(generatedShotClassName); String lineThroughClassName = NameUtils.getLineThroughName(generatedShotClassName); Map<String, String> map = new HashMap<String, String>(); map.put("generatedLongClassName", generatedLongClassName); map.put("generatedShortClassName", generatedShotClassName); map.put("firstLowerGeneratedClassName", firstLowerGeneratedClassName); map.put("lineThroughClassName", lineThroughClassName); context.put(task.getName(), map); }}

能夠看到只是往VelocityContext中添加了一些數據而已,在這裏徹底能夠根據你本身的想法來實現一些特別的功能,至於如何讀取模板、生成文件則不用去關心,默認都會幫你完成。

重寫代碼生成接口實現重寫

假設上面的簡單重寫還沒法知足需求,例如你想要對模板讀取、插件運行、文件生成等方式作一些改變,那麼能夠直接實現接口CodeGenerator,只須要實現一個方法:

 
 
 
  
  
           
  
  
/** * 代碼生成方法 * @param table 當前運行表信息 * @param task 當前運行任務信息 * @param configuration 全部的配置信息 */void doGenerate(Table table, Task task, Configuration configuration);

參數中能夠獲取到運行的表、任務及全部的配置信息,能夠根據本身的須要徹底改寫dgen的代碼生成行爲。

能夠參考AbstractCodeGeneratorDefaultCodeGenerator中的實現。

固然,最後仍然是別忘了在任務配置中指定實現類:

 
 
 
  
  
           
  
  
<task name="model" class="com.dexcoder.dgen.test.CustomCodeGenerator"> ......</task>

擴展xml配置文件

有時候可能會須要在dgen.xml配置文件中添加一些新的標籤以使增長的擴展功能實現的更加優雅,這個一樣能夠很好的支持。

只須要實現XmlElementParser接口:

 
 
 
  
  
           
  
  
public interface XmlElementParser { /** * 解析的元素名稱 * * @return */ String getParseElementName(); /** * 解析元素 * * @param element * @param configuration */ void parseElement(Element element, Configuration configuration);}

getParseElementName方法只須要返回該解析類解析的xml元素標籤名稱,當dgen運行時解析到該元素標籤時便會調用parseElement方法,以完成你想要的操做。

如下是jdbc標籤的解析類實現,供參考:

 
 
 
  
  
           
  
  
public class JdbcParser extends AbstractXmlElementParser { public String getParseElementName() { return "jdbc"; } @Override public void doParseElement(Element element, Configuration configuration) { List<Element> elements = element.elements("property"); if (CollectionUtils.isEmpty(elements)) { return; } for (Element el : elements) { String name = el.attributeValue("name"); String value = el.attributeValue("value"); configuration.addJdbcConfig(name, value); } }}

最後,用如下代碼運行:

 
 
 
  
  
           
  
  
ConfigParser configParser = new ConfigParser();//這裏註冊實現的xml標籤解析器 JdbcParser是內置解析器默認已註冊//configParser.registerParser(new JdbcParser());Configuration configuration = configParser.parseConfig(configFile);GenerationManager generationManager = new GenerationManager(configuration);generationManager.doGenerate();
相關文章
相關標籤/搜索