编写自己的代码生成工具五:代码生成实现

前面,我们已经准备好了一切,配置信息、任务信息、数据库查询出来的表信息等等都已经拿到手了,接下来就是根据这些信息来进行代码生成了。

代码生成说白了就是弄个模板,字符串的替换而已,无非就是怎么样才能实现的比较优雅,在这里我们使用了velocity,可以省下大量繁琐的事情。不多说了,看源码,源码的实现方式和数据库查询类似,首先也是定义一个接口:


  
/** * 代码生成接口 * * User: liyd * Date: 13-11-28 * Time: 下午5:35 */ public interface EasyCodeGenerator { /** * 代码生成方法 * @param table the table * @param task the task * @param context the context */ public void doGenerate(Table table, Task task, VelocityContext context); }

实现接口的抽象类:


  
/** * 代码生成接口抽象实现 * * User: liyd * Date: 13-12-6 * Time: 下午4:56 */ public abstract class AbstractEasyCodeGenerator implements EasyCodeGenerator { /** * 代码生成方法 * * @param table the table * @param task the task * @param context the context */ @Override public void doGenerate(Table table, Task task, VelocityContext context) { context.put("serialVersionUID", getSerialVersionUID() + "L"); StringBuilder sbTemp = new StringBuilder(FileUtils.getTemplate(task.getTemplate())); //运行插件 this.executePlugin(table, task, context, sbTemp); this.generate(table, task, context, sbTemp); String template = VelocityUtils.parseString(sbTemp.toString(), context); String targetDir = EasyCodeContext.getConstant("targetDir"); targetDir = StringUtils.isBlank(targetDir) ? "" : targetDir + "/"; String moduleDir = task.getModuleDir(); moduleDir = StringUtils.isBlank(moduleDir) ? "" : moduleDir + "/"; String srcDir = task.getSrcDir(); srcDir = StringUtils.isBlank(srcDir) ? "" : srcDir + "/"; String packageFileDir = task.getGeneratedFileName(table.getName()); String filePath = targetDir + moduleDir + srcDir + packageFileDir; FileUtils.writeFile(filePath, template); } /** * 运行插件 * * @param table * @param task * @param context * @param sbTemp */ private void executePlugin(Table table, Task task, VelocityContext context, StringBuilder sbTemp) { Map<String, EasyCodePlugin> pluginMap = task.getPluginMap(); if (pluginMap == null || pluginMap.size() == 0) { return; } for (EasyCodePlugin easyCodePlugin : pluginMap.values()) { easyCodePlugin.execute(table, task, sbTemp, context); } } /** * 添加字段类型需要导入的包 * * @param columns the columns * @return the columns import class */ protected Set<String> getColumnsImportClass(List<Column> columns) { Set<String> importSet = new HashSet<String>(); for (Column column : columns) { if (EasyCodeContext.getDataConvertType(column.getDbType()) != null) { addImportClass(importSet, EasyCodeContext.getDataConvertType(column.getDbType()) .getJavaClass()); } else { addImportClass(importSet, column.getJavaClass()); } } return importSet; } /** * 生成serialVersionUID * * @return */ protected String getSerialVersionUID() { return String.valueOf(Math.abs(UUID.randomUUID().getMostSignificantBits())); } /** * 添加引用的类 * * @param importSet * @param className */ private void addImportClass(Set<String> importSet, String className) { if (StringUtils.startsWith(className, "java.lang")) { return; } importSet.add(className); } /** * 代码生成方法 * * @param table the table * @param task the task * @param context the context * @param template the template */ public abstract void generate(Table table, Task task, VelocityContext context, StringBuilder template); }

在这个抽象实现类里,我们做了一些基本都要用到的操作:生成并放入serialVersionUID,调用插件,及最后的代码文件生成。并主要提供了一个getColumnsImportClass方法供子类调用,该方法的主要作用是引入生成类中属性的包,例如对应数据库的Date类型属性,则会引入java.util.Date。至于具体的代码内容则由子类去实现。

具体的代码构建子类:


  
/** * 默认代码生成实现类 * * User: liyd * Date: 13-12-16 * Time: 下午4:28 */ public class DefaultCodeGenerator extends AbstractEasyCodeGenerator { /** * 代码生成方法 * * @param table * @param task * @param context * @param template */ @Override public void generate(Table table, Task task, VelocityContext context, StringBuilder template) { context.put("date", new Date()); context.put("table", table); context.put("task", task); context.put("packageName", task.getPackageName()); String longClassKey = task.getName() + "GeneratedLongClassName"; String shortClassKey = task.getName() + "GeneratedShotClassName"; String firstLowerClassKey = task.getName() + "FirstLowerGeneratedClassName"; String generatedShotClassName = task.getGeneratedShotClassName(table.getName()); context.put(longClassKey, task.getGeneratedReferenceClassName(table.getName())); context.put(shortClassKey, generatedShotClassName); context.put(firstLowerClassKey, NameUtils.getFirstLowerName(generatedShotClassName)); Set<String> importSet = new HashSet<String>(); String tmp = template.toString(); if (StringUtils.indexOf(tmp, "List<") != -1 && StringUtils.indexOf(tmp, "java.util.List") == -1) { importSet.add("java.util.List"); } if (StringUtils.indexOf(tmp, "Map<") != -1 && StringUtils.indexOf(tmp, "java.util.Map") == -1) { importSet.add("java.util.Map"); } if (StringUtils.indexOf(tmp, "@Repository") != -1 && StringUtils.indexOf(tmp, "org.springframework.stereotype.Repository") == -1) { importSet.add("org.springframework.stereotype.Repository"); } if (StringUtils.indexOf(tmp, "@Autowired") != -1 && StringUtils.indexOf(tmp, "org.springframework.beans.factory.annotation.Autowired") == -1) { importSet.add("org.springframework.beans.factory.annotation.Autowired"); } if (StringUtils.indexOf(tmp, "@Component") != -1 && StringUtils.indexOf(tmp, "org.springframework.stereotype.Component") == -1) { importSet.add("org.springframework.stereotype.Component"); } if (StringUtils.indexOf(tmp, "${modelGeneratedShotClassName}") != -1 && StringUtils.indexOf(tmp, "${modelGeneratedLongClassName}") == -1 && !StringUtils.equals(task.getName(), "model")) { Object modelGeneratedLongClassName = context.get("modelGeneratedLongClassName"); importSet.add(modelGeneratedLongClassName == null ? "" : modelGeneratedLongClassName .toString()); } if (StringUtils.indexOf(tmp, "${modelVoGeneratedShotClassName}") != -1 && StringUtils.indexOf(tmp, "${modelVoGeneratedLongClassName}") == -1 && !StringUtils.equals(task.getName(), "modelVo")) { Object modelGeneratedLongClassName = context.get("modelVoGeneratedLongClassName"); importSet.add(modelGeneratedLongClassName == null ? "" : modelGeneratedLongClassName .toString()); } if (StringUtils.indexOf(tmp, "${javaMapperGeneratedShotClassName}") != -1 && StringUtils.indexOf(tmp, "${javaMapperGeneratedLongClassName}") == -1 && !StringUtils.equals(task.getName(), "javaMapper")) { Object mapperGeneratedLongClassName = context.get("javaMapperGeneratedLongClassName"); importSet.add(mapperGeneratedLongClassName == null ? "" : mapperGeneratedLongClassName .toString()); } if (StringUtils.indexOf(tmp, "${daoGeneratedShotClassName}") != -1 && StringUtils.indexOf(tmp, "${daoGeneratedLongClassName}") == -1 && !StringUtils.equals(task.getName(), "dao")) { Object daoGeneratedLongClassName = context.get("daoGeneratedLongClassName"); importSet.add(daoGeneratedLongClassName == null ? "" : daoGeneratedLongClassName .toString()); } if (StringUtils.indexOf(tmp, "${serviceGeneratedShotClassName}") != -1 && StringUtils.indexOf(tmp, "${serviceGeneratedLongClassName}") == -1 && !StringUtils.equals(task.getName(), "service")) { Object daoGeneratedLongClassName = context.get("serviceGeneratedLongClassName"); importSet.add(daoGeneratedLongClassName == null ? "" : daoGeneratedLongClassName .toString()); } if (StringUtils.equalsIgnoreCase("model", task.getName()) || StringUtils.equalsIgnoreCase("modelVo", task.getName())) { importSet.addAll(super.getColumnsImportClass(table.getColumns())); } context.put("importList", importSet); } }

这里构建内容,其实主要就是把一些代码需要的信息放入到VelocityContext中,代Velocity在解析模板时使用。需要一提的是,这里把当前任务生成的代码信息也放了进去,可以供后面的生成任务使用,这样做的缘由是你可能会需要用到前面生成的Java类。比如在生成dao和service时,必然要依赖用到生成的model实体类,这样你就可以方便的取用。

下面的代码对常用的一些类,List,Map,spring的注解和前面任务生成的一些类做了一下特别处理,使得当包含这些对象时,包可以自动导入。这是我在实际生成时,发现每次都要在模板里面写多有不更而临时加上去的,可以去掉。

另外附上实体类生成的模板,其它模板类似,依样画葫芦编写即可:


  
package ${packageName}; import com.mincoder.kit.page.PagingOrder; #foreach($im in ${importList}) import ${im}; #end /** * ${table.desc} * * User: ${constant_creator} * Date: ${date} */ public class ${modelGeneratedShotClassName} extends PagingOrder{ /** serialVersionUID */ private static final long serialVersionUID = ${serialVersionUID}; #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.firstUpperName}() { return ${column.camelName}; } public void set${column.firstUpperName}(${column.javaType} ${column.camelName}) { this.${column.camelName} = ${column.camelName}; } #end }

到这里,代码生成的基本操作算是全部完成了,接下去将讲如何实现自定义的生成(例如在生成代码的同时生成一份表信息的Excel)。

转载于:https://my.oschina.net/u/2532228/blog/676384

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值