码迷,mamicode.com
首页 > 其他好文 > 详细

代码生成工具dgen使用说明

时间:2016-04-12 11:15:27      阅读:615      评论:0      收藏:0      [点我收藏+]

标签:

前言

项目组希望能有一个比较完善,可以生成各类代码的工具,因为之前写过所以趁这两天重新弄了个。

代码生成工具命名为dgen -> dexcoder-generator,旨在提高开发人员效率,避免重复劳动。

理论上可以生成任何想要的代码文件,包括实体类、dao、service及页面文件等,另外可以方便的实现扩展生成自己想要的东西。

为了方便开发避免重复造轮子,依赖了一些开源的第三方组件,主要为dom4jvelocity

dgen的使用步骤为:编写配置文件 -> 编写模板文件 -> 运行。

下面就按这个顺序对dgen进行说明,并会在最后介绍如何对dgen进行自定义的扩展。

配置文件说明

dgen默认的配置文件名为dgen.xml,当然可以随意改变它,只需在启动时指定文件名即可。

dgen.xml文件是所有配置的入口,里面包含了工具运行时需要的各类信息,当配置信息较多时可以拆分成多个文件,使用include标签引入。

dgen.xml文件样例:

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <config>
    3. <constants>
    4. <!--生成时文件已存在是否覆盖 默认true-->
    5. <constant name="overwrite" value="true"/>
    6. <!--生成目标文件夹 不指定默认为运行目录-->
    7. <!--<constant name="targetDir" value="/Users/liyd/project"/>-->
    8. <!--是否运行在子模块 为true则生成代码的文件夹会到上一层(父模块)为基准 默认false -->
    9. <constant name="runOnChildModule" value="false"/>
    10. </constants>
    11. <!--jdbc连接信息配置-->
    12. <jdbc>
    13. <property name="dialect" value="mysql"/>
    14. <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    15. <property name="url" value="jdbc:mysql://localhost:3306/dexcoder?useUnicode=true&amp;characterEncoding=utf-8"/>
    16. <property name="username" value="root"/>
    17. <property name="password" value=""/>
    18. </jdbc>
    19. <!--数据类型转换映射-->
    20. <converters>
    21. <convert dbType="number" javaType="java.lang.Long"/>
    22. <convert dbType="VARCHAR2" javaType="java.lang.String"/>
    23. <convert dbType="SYS.XMLTYPE" javaType="java.lang.String"/>
    24. <convert dbType="TIMESTAMP" javaType="java.util.Date"/>
    25. <convert dbType="datetime" javaType="java.util.Date"/>
    26. <convert dbType="BLOB" javaType="java.lang.Byte[]"/>
    27. </converters>
    28. <include file="demo/tableConfig.xml"/>
    29. </config>
  • config根标签,固定。
  • constants常量标签,可以包含任意数量的constant子标签,每一个constant子标签都定义了一个常量。
    • jdbc数据库连接配置信息。
    • converters数据类型转换映射。dbType指定数据库的类型,javaType指定你期望在生成Java类中的类型。
    • include标签,引入其它的配置文件(当然也可以不引入全部写在当前配置文件当中)。

这里把它拆分成了2个文件,使用了include标签,引入的tableConfig.xml文件样例:

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <config>
    3. <table name="USER" desc="用户">
    4. <task name="model"/>
    5. <task name="dao"/>
    6. </table>
    7. <tasks>
    8. <task name="model" class="com.dexcoder.dgen.generator.DefaultCodeGenerator">
    9. <property name="template" value="demo/template/model.vm"/>
    10. <property name="beginFix" value=""/>
    11. <property name="endFix" value=""/>
    12. <property name="suffix" value=".java"/>
    13. <property name="moduleName" value="dexcoder-core"/>
    14. <property name="srcDir" value="src/main/java"/>
    15. <property name="package" value="com.dexcoder.model"/>
    16. </task>
    17. <task name="dao">
    18. <property name="template" value="demo/template/dao.vm"/>
    19. <property name="beginFix" value=""/>
    20. <property name="endFix" value="Dao"/>
    21. <property name="suffix" value=".java"/>
    22. <property name="moduleName" value="dexcoder-core"/>
    23. <property name="srcDir" value="src/main/java"/>
    24. <property name="package" value="com.dexcoder.dao"/>
    25. </task>
    26. <task name="daoImpl">
    27. <property name="template" value="demo/template/daoImpl.vm"/>
    28. <property name="beginFix" value=""/>
    29. <property name="endFix" value="DaoImpl"/>
    30. <property name="suffix" value=".java"/>
    31. <property name="moduleName" value="daoDir"/>
    32. <property name="srcDir" value="src/main/java"/>
    33. <property name="package" value="com.dexcoder.dao.impl"/>
    34. </task>
    35. <task name="service">
    36. <property name="template" value="demo/template/service.vm"/>
    37. <property name="beginFix" value=""/>
    38. <property name="endFix" value="Service"/>
    39. <property name="suffix" value=".java"/>
    40. <property name="moduleName" value="serviceDir"/>
    41. <property name="srcDir" value="src/main/java"/>
    42. <property name="package" value="com.dexcoder.service"/>
    43. </task>
    44. </tasks>
    45. </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类模板:

    1. package ${packageName};
    2. #foreach($im in ${importClasses})
    3. import ${im};
    4. #end
    5. /**
    6. * ${table.desc}
    7. *
    8. * Author: Created by code generator
    9. * Date: ${date}
    10. */
    11. public class ${model.generatedShortClassName} {
    12. /** serialVersionUID */
    13. private static final long serialVersionUID = ${serialVersionUID}L;
    14. #foreach($column in ${table.columns})
    15. #if(${column.comment})
    16. /** ${column.comment} */
    17. #end
    18. private ${column.javaType} ${column.camelName};
    19. #end
    20. #foreach($column in ${table.columns})
    21. public ${column.javaType} get${column.firstUpperCamelName}() {
    22. return ${column.camelName};
    23. }
    24. public void set${column.firstUpperCamelName}(${column.javaType} ${column.camelName}) {
    25. this.${column.camelName} = ${column.camelName};
    26. }
    27. #end
    28. }

dao接口模板,这里只生成了一个insert方法。

    1. package ${packageName};
    2. import ${model.generatedLongClassName};
    3. /**
    4. * ${table.desc} dao接口
    5. *
    6. * Author: Created by code generator
    7. * Date: ${date}
    8. */
    9. public interface ${dao.generatedShortClassName} {
    10. /**
    11. * 新增记录
    12. *
    13. * @param ${model.firstLowerGeneratedClassName}
    14. */
    15. void insert(${model.generatedShortClassName} ${model.firstLowerGeneratedClassName});
    16. }

运行

代码调用方式

只需一行代码:

    1. StartupGenerator.run();

指定配置文件名,文件路径相对于当前运行项目目录

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

jar包执行方式

直接运行:

    1. java -jar codeGenerator.jar

指定配置文件名,文件路径相对于当前运行项目目录

    1. java -jar codeGenerator.jar demo/dgen.xml

扩展

有些时候可能默认的功能无法满足项目的需求,你可以通过编写一些自定义扩展来增强dgen的功能。

插件实现

插件演示一

这里以model类实现了序列化,生成需要serialVersionUID为例,dgen默认并没有提供,可以通过简单的插件方式来实现(该插件已内置实现)。

首先实现GeneratorPlugin接口,该接口只有一个方法:

    1. /**
    2. * 插件执行方法
    3. *
    4. * @param table 当前运行任务表配置信息
    5. * @param task 当前运行任务配置信息
    6. * @param configuration 所有的配置信息
    7. * @param templateText 模板内容
    8. * @param context 模板解析时的VelocityContext
    9. */
    10. void execute(Table table, Task task, Configuration configuration, StringBuilder templateText,
    11. VelocityContext context);

方法的参数中可以拿到所有的想要的信息,包括表信息、任务信息、dgen配置信息、模板内容、模板解析时的VelocityContext等,按需使用即可。

下面是serialVersionUID插件的实现代码:

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

只是添加了serialVersionUID变量,这样在model模板中就可以使用了,添加下面代码即可:

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

当然别忘了把它添加到任务配置中:

    1. <task name="model" class="com.dexcoder.dgen.generator.DefaultCodeGenerator">
    2. <plugin name="serialVersionUIDPlugin" value="com.dexcoder.dgen.plugins.SerialVersionUIDPlugin"/>
    3. <property name="template" value="demo/template/model.vm"/>
    4. <property name="beginFix" value=""/>
    5. <property name="endFix" value=""/>
    6. <property name="suffix" value=".java"/>
    7. <property name="moduleName" value="dexcoder-core"/>
    8. <property name="srcDir" value="src/main/java"/>
    9. <property name="package" value="com.dexcoder.model"/>
    10. </task>

插件演示二

我们再来实现一个Excel表格生成插件,假设我们在生成model类的同时想生成一份表结构的Excel文件。

实现的步骤上相同,只需要修改方法实现部分即可,以下是源代码,这里对Excel生成时行了封装,不在本文讨论范围:

    1. public void execute(Table table, Task task, Configuration configuration, StringBuilder templateText,
    2. VelocityContext context) {
    3. ExcelSheet excelSheet = new ExcelSheet();
    4. excelSheet.setSheetName("表信息");
    5. List<String> rowTitles = new ArrayList<String>();
    6. rowTitles.add("列名");
    7. rowTitles.add("列备注");
    8. rowTitles.add("数据库类型");
    9. rowTitles.add("是否主键");
    10. excelSheet.setRowTitles(rowTitles);
    11. List<ExcelRow> rowList = new ArrayList<ExcelRow>();
    12. for (Column column : table.getColumns()) {
    13. ExcelRow excelRow = new ExcelRow();
    14. excelRow.addCell(column.getName());
    15. excelRow.addCell(column.getComment());
    16. excelRow.addCell(column.getDbType());
    17. excelRow.addCell(column.getIsPrimaryKey());
    18. rowList.add(excelRow);
    19. }
    20. excelSheet.setRows(rowList);
    21. List<ExcelSheet> sheetList = new ArrayList<ExcelSheet>();
    22. sheetList.add(excelSheet);
    23. ExcelWriteTools.write(sheetList, new File("/Users/liyd/Desktop/db.xls"));
    24. }

代码生成简单自定义重写

如果使用插件的方式无法满足你的需求,可以自己实现代码生成的方法,只需要继承AbstractCodeGenerator类实现generate方法即可。

默认的生成类为DefaultCodeGenerator

    1. /**
    2. * 默认代码生成实现类
    3. *
    4. * User: liyd
    5. * Date: 13-12-16
    6. * Time: 下午4:28
    7. */
    8. public class DefaultCodeGenerator extends AbstractCodeGenerator {
    9. /**
    10. * 代码生成方法
    11. *
    12. * @param table
    13. * @param task
    14. * @param context
    15. * @param template
    16. */
    17. @Override
    18. public void generate(Table table, Task task, VelocityContext context, StringBuilder template) {
    19. Set<String> importClasses = this.getColumnsImportClass(table.getColumns());
    20. context.put("importClasses", importClasses);
    21. context.put("date", new Date());
    22. context.put("table", table);
    23. context.put("task", task);
    24. context.put("packageName", task.getPackageName());
    25. //任务生成的类信息
    26. String generatedLongClassName = task.getGeneratedReferenceClassName(table.getName());
    27. String generatedShotClassName = task.getGeneratedShotClassName(table.getName());
    28. String firstLowerGeneratedClassName = NameUtils.getFirstLowerName(generatedShotClassName);
    29. String lineThroughClassName = NameUtils.getLineThroughName(generatedShotClassName);
    30. Map<String, String> map = new HashMap<String, String>();
    31. map.put("generatedLongClassName", generatedLongClassName);
    32. map.put("generatedShortClassName", generatedShotClassName);
    33. map.put("firstLowerGeneratedClassName", firstLowerGeneratedClassName);
    34. map.put("lineThroughClassName", lineThroughClassName);
    35. context.put(task.getName(), map);
    36. }
    37. }

可以看到只是往VelocityContext中添加了一些数据而已,在这里完全可以根据你自己的想法来实现一些特别的功能,至于如何读取模板、生成文件则不用去关心,默认都会帮你完成。

重写代码生成接口实现重写

假设上面的简单重写还无法满足需求,例如你想要对模板读取、插件运行、文件生成等方式做一些改变,那么可以直接实现接口CodeGenerator,只需要实现一个方法:

    1. /**
    2. * 代码生成方法
    3. * @param table 当前运行表信息
    4. * @param task 当前运行任务信息
    5. * @param configuration 所有的配置信息
    6. */
    7. void doGenerate(Table table, Task task, Configuration configuration);

参数中可以获取到运行的表、任务及所有的配置信息,可以根据自己的需要完全改写dgen的代码生成行为。

可以参考AbstractCodeGeneratorDefaultCodeGenerator中的实现。

当然,最后仍然是别忘了在任务配置中指定实现类:

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

扩展xml配置文件

有时候可能会需要在dgen.xml配置文件中添加一些新的标签以使增加的扩展功能实现的更加优雅,这个同样可以很好的支持。

只需要实现XmlElementParser接口:

    1. public interface XmlElementParser {
    2. /**
    3. * 解析的元素名称
    4. *
    5. * @return
    6. */
    7. String getParseElementName();
    8. /**
    9. * 解析元素
    10. *
    11. * @param element
    12. * @param configuration
    13. */
    14. void parseElement(Element element, Configuration configuration);
    15. }

getParseElementName方法只需要返回该解析类解析的xml元素标签名称,当dgen运行时解析到该元素标签时便会调用parseElement方法,以完成你想要的操作。

以下是jdbc标签的解析类实现,供参考:

    1. public class JdbcParser extends AbstractXmlElementParser {
    2. public String getParseElementName() {
    3. return "jdbc";
    4. }
    5. @Override
    6. public void doParseElement(Element element, Configuration configuration) {
    7. List<Element> elements = element.elements("property");
    8. if (CollectionUtils.isEmpty(elements)) {
    9. return;
    10. }
    11. for (Element el : elements) {
    12. String name = el.attributeValue("name");
    13. String value = el.attributeValue("value");
    14. configuration.addJdbcConfig(name, value);
    15. }
    16. }
    17. }

最后,用以下代码运行:

    1. ConfigParser configParser = new ConfigParser();
    2. //这里注册实现的xml标签解析器 JdbcParser是内置解析器默认已注册
    3. //configParser.registerParser(new JdbcParser());
    4. Configuration configuration = configParser.parseConfig(configFile);
    5. GenerationManager generationManager = new GenerationManager(configuration);
    6. generationManager.doGenerate();

代码生成工具dgen使用说明

标签:

原文地址:http://blog.csdn.net/hj7jay/article/details/51130376

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!