您的位置:首页 > 编程语言

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

2016-05-17 00:00 806 查看
前面,我们已经准备好了一切,配置信息、任务信息、数据库查询出来的表信息等等都已经拿到手了,接下来就是根据这些信息来进行代码生成了。
代码生成说白了就是弄个模板,字符串的替换而已,无非就是怎么样才能实现的比较优雅,在这里我们使用了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)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: