代码生成器(二)代码生成器的开发

代码生成器开发(核心)

一、概述

  • 整体架构图

在这里插入图片描述

  • 思路
  1. 用户填写数据库信息,工程代建信息,把这些信息构造到实体类对象中
  2. 数据库表信息,数据库字段信息构造到实体类中
  3. 构造FreeMarker数据模型,把数据库表对象和基本配置存入到数据模型中
  4. 构造自定义公共代码模板
  5. 借助FreeMarker完成代码生成

二、搭建环境

1. 构造Maven工程,引入第三方依赖

  1. FreeMarker

  2. mysql驱动

2. 配置工具类

1)工具类的实现方式灵活且个性化十足,可以通过导入第三方工具类,也可以自己编写工具类

2)工具类清单:

  1. 字符串处理工具类
  2. 配置文件加载工具类
  3. 文件处理工具类
  4. 数据库处理工具类

3. 配置实体类

1)数据库配置

  1. JDBC配置实体类
  2. 数据表实体类
  3. 数据列实体类

2)工程配置

3)总结

这部分的任务就是把前台获取的配置信息封装到实体类中,方便后续加载数据表和构造数据模型

三、代码生成

1、流程

1)通过数据库处理工具类来获取数据库表实体对象

2)通过自定义配置数据表元数据生成工程配置构造数据模型

3)代码生成:a. 通过文件操作工具类扫描模板路径下的所有文件 b. 对每个模板使用数据模型进行插值

  1. 处理模板路径

    (1) 说明

    构造的模板文件名字可能存在不同,但都有很强的的规律性,一般都是类名+一般命名,故可以采用 FreeMarker的字符串模板插值的方式进行插值,最后把插值结果以流的形式在转换为字符串输出,赋值给生成模板文件作为文件名。

    (2)思路

    1、通过StringReader读取模板路径
    2、配合数据模型进行插值,把结果写入StringWriter
    3、把StringWriter转换为字符串

  2. 处理模板文件

    (1)思路
    1、通过FreeMarker配置器获取模板文件
    2、设定模板输出为utf-8编码方式
    3、通过文件工具类提前生成模板文件所在父目录
    4、配合数据模型生成模板文件
    5、关闭文件写入流

四、核心代码

1、调用代码生成器的入口

	//UI调用程序入口
	/**
	 *
	 * @param templetPath		模板所在路径
	 * @param outpath			选择代码生成路径
	 * @param settings			工程配置对象
	 * @param db				数据库信息
	 */
	private void generator(String templetPath,String outpath,Settings settings,DataBase db) throws Exception {
		GeneratorFacade gf = new GeneratorFacade(templetPath,outpath,settings,db);
		gf.generatorByDataBase();
	}

2、代码生成器数据模型整合类(核心)

/**
 * 代码生成器的核心入口类
 */
 public class GeneratorFacade {
    private String templatePath;
    private String outPath;
    private Settings settings;
    private DataBase db;
    private Generator generator;

    public GeneratorFacade(String templatePath, String outPath, Settings settings, DataBase db) throws Exception {
        this.templatePath = templatePath;
        this.outPath = outPath;
        this.settings = settings;
        this.db = db;
        this.generator = new Generator(templatePath,outPath);
    }

    /**
     * 1.准备数据模型
     * 2.调用核心处理类完成代码生成工作
     */
    public void generatorByDataBase() throws Exception {
        List<Table> tables = DataBaseUtils.getDbInfo(db);
        for (Table table : tables) {
            Map<String,Object> dataModel = getDataModel(table);
            generator.scanAndGenerator(dataModel);
        }
    }


    /**
     * 根据table对象获取数据模型
     */
    private Map<String,Object> getDataModel(Table table){
        Map<String,Object> dataModel = new HashMap<>();
        // 1.自定义配置
        dataModel.putAll(PropertiesUtils.customMap);
        // 2.元数据
        dataModel.put("table",table);
        // 3.setting
        dataModel.putAll(this.settings.getSettingMap());
        // 4.类型
        dataModel.put("ClassName",table.getName2());
        return dataModel;
    }
}

3、代码生成类(核心)

/**
 * 代码生成器的核心处理类
 */
public class Generator {
    private String templatePath; //模板路径
    private String outPath; //代码生成路径
    private Configuration cfg;

    public Generator(String templatePath, String outPath) throws Exception {
        this.templatePath = templatePath;
        this.outPath = outPath;
        //实例化Configuration
        cfg = new Configuration();
        // 指定模板加载器
        FileTemplateLoader fil = new FileTemplateLoader(new File(templatePath));
        cfg.setTemplateLoader(fil);
    }

    /**
     * 代码生成
     *      1. 扫描模板路径下的所有模板
     *      2. 对每个模板进行代码填充生成(数据模型)
     */
    public void scanAndGenerator(Map<String,Object> dataModel) throws Exception {
        //1. 根据模板路径找到此路径下的所有模板文件
        List<File> fileList = FileUtils.searchAllFile(new File(templatePath));// 提供一个相对路径

        //2. 每个模板文件生成
        for (File file: fileList){
            executeGenertor(dataModel,file);
        }

    }

    /**
     * 对模板进行文件生成
     * @param dataModel
     * @param file
     *      模板文件:c: com.example.abc.java
     */
    private void executeGenertor(Map<String,Object> dataModel, File file) throws Exception {
        //1. 文件路径的处理  (E:\模板\${path1}\${path2}\${path3}\${ClassName}.java})
        System.out.println("file = " + file);
        String templateFileName = file.getAbsolutePath().replace(this.templatePath,""); // 得到绝对路径
        System.out.println("absoultePathFile = " + templateFileName);
        String outFileName = processTemplateString(templateFileName,dataModel);
        //2. 读取文件模板
        Template template = cfg.getTemplate(templateFileName);
        template.setOutputEncoding("utf-8");
        //3. 创建输出的模板文件
        File file1 = FileUtils.mkdir(outPath,outFileName);
        //4. 模板处理(文件生成)
        FileWriter fw = new FileWriter(file1);
        template.process(dataModel,fw);
        fw.close();
    }

    public String processTemplateString(String templateString, Map dataModel) throws Exception {
        StringWriter out = new StringWriter();
        Template template = new Template("name1",new StringReader(templateString),cfg);
        System.out.println("templateString = " + templateString + ", dataModel = " + dataModel);
        template.process(dataModel,out);
        System.out.println(out.toString());
        return out.toString();
    }
}

五、总结

以上便是代码生成的核心代码,通过以上代码可以实现一个简单的SpringBoot框架核心代码的自动生成。从此,面对数量庞大的数据表,不用再陷入无聊且易出错的代码复制粘贴困局。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一个不怎么智能,半手工方式的,但最适合实际开发java代码生成器 核心理念: 为你生成一切,再根据所需手工copy回来工作区 用最精简的代码完成最核心的功能 特性 基于FreeMarker模板语言,并且模板易于修改 基于数据库,内建好数据库的model,并支持多种数据库(mysql,sql server,oracle测试通过) 半手工方式,生成的代码放在某个目录,再手工拷贝回来工作区 易于做开发,整个生成器本身就是java源代码,源代码核心十分精简,并且鼓励你修改代码,也可以作为任何语言的代码生成器 配置简单,只有一个配置文件generator.properties 生成器的运行 在eclipse中运行 配置classpath,将generator/lib中的rapid-generator.jar及其它数据库驱动加入classpath 修改generator.xml的数据库连接属性及其它属性 以application的方式运行GeneratorMain类,要生成不同的table,直接修改代码即可 public class GeneratoMain { public static void main(String[] args) throws Exception { GeneratorFacade g = new GeneratorFacade(); //删除生成器的输出目录 g.deleteOutRootDir(); //通过数据库表生成文件,template为模板的根目录 // g.generateByTable("table_name","template"); //自动搜索数据库中的所有表并生成文件,template为模板的根目录 g.generateByAllTable("template"); // g.generateByClass(Blog.class,"template_clazz"); //删除生成的文件 // g.deleteByTable("table_name", "template"); } } 以application方式运行cn.org.rapid_framework.generator.ext.CommandLine 独立版运行 下载standandalone-rapid-generator.zip,解压并运行rapid-gen.bat 生成器讲解 1.生成器模板路径可以引用相关变量 示例:dao/${basepackage_dir}/${className}.java,根据该变量生成输出文件 如果basepackage_dir = com/company/rapid, className=UserInfo 那么完整路径则为:dao/com/company/rapid/UserInfo.java 2.自动搜索某个目录所有模板文件,无需配置 代码生成器模板可以引用的相关变量 1. g.generateByTable("table_name") 方法可以引用的变量 table : cn.org.rapid_framework.generator.provider.db.table.model.Table 2. g.generateByClass("class") 方法可以引用的变量 clazz : cn.org.rapid_framework.generator.provider.java.model.JavaClass 3. g.generateBySql(Sql) 方法可以引用的变量 sql : cn.org.rapid_framework.generator.provider.db.sql.model.Sql 4.公共变量 env : 系统环境变量 System.getProperties() : 直接引用,没有前缀 generator.properties 文件中的所有属性,直接引用,没有前缀 gg : 模板控制变量, cn.org.rapid_framework.generator.GeneratorControl 每个模板有gg变量可以控制自身的自定义配置 (每一个模板都会创建新的gg实例) 如是否生成,是否覆盖目标文件,甚至是生成其它文件 示例: ${gg.setIgnoreOutput(true)}, 参考: rapid_generator_gg 支持生成(gen)及删除操作(del),即生成的代码也可以很方便的删除 3.自动拷贝进制文件至输出目录 如模板目录下的 zip,rar,doc文件将会自动拷贝至输出目录,不会破坏文件格式 (通过扩展名自动识别) 4.自动删除模板扩展名:.ftl,.vm 举例: 如你有一个模板 SqlMap.xml.ftl 将变为 SqlMap.xml 所以你要生成ftl扩展名的文件,应该将文件名从 list.ftl => list.ftl.ftl 5.模板自动include同级目录:macro.include文件 示例: 如你的模板为 java_src/com/project/UserDao.java, 将自动include: java_src/com/project/macro.include 及 根目录的macro.include 6.generator.xml (或者generator.properties)配置文件 类似ant可以变量引用,引用环境变量使用${env.JAVA_HOME}, 引用System.getProperties()直接引用 2.自动替换generator.properties中的句号(.)为反斜杠,设置key为key+"dir"后缀 示例: pkg=com.company => pkg_dir=com/company 7. 数据库表配置,用于自定义生成器模板引用的table变量 配置文件必须存放在classpath: generator_config/table/table_name.xml (该文件生成器可以生成,自己再自定义修改) <!-- <table sqlName="数据库表名" className="类名称" tableAlias="表的别名"> <column sqlName="数据库列名" columnAlias="列的别名" javaType="自定义javaType" unique="是否唯一性约束" nullable="是否可以为空" pk="是否主键,在表没有主键的情况下,可以指定一个代理主键" updatable="是否可以更新" insertable="是否插入" enumString="枚举值,以分号分隔,示例值:M(1,男);F(0,女) 或者是:M(男);F(女)" enumClassName="如果枚举有值,生成的类名称将是这个,没有枚举值,该配置无用.示例值:Sex" /> </table> --> <table sqlName="user_info" className="UserInfo" tableAlias="UserInfo"> <column sqlName="username" columnAlias="用户名" javaType="String" unique="false" nullable="true" pk="false" updatable="true" insertable="true" enumString="F(1,Female);M(0,Male)" enumClassName="用户枚举" /> <column sqlName="password" columnAlias="password" javaType="String" unique="false" nullable="true" pk="false" updatable="true" insertable="true" enumString="" enumClassName="PasswordEnum" /> </table> 生成的代码插入文档的某个部位 如模板输出生成的地方已经有该 同名的文件 存在,并且该文件中有包含"generator-insert-location"标记,则模板生成的内容会插入在该标记之后.该特性对如生成的spring配置内容插入spring配置文件十分有用 创建一个生成器模板文件 在temppate目录创建一个文件,如${className}SpringControler.java代码生成器会自动将该模板文件加载并生成该文件 充分利用各种文件的注释 如在.xml中我们可以使用 在.properties文件中我们可以使用 #generator-insert-location 具体请查看template/insert_demo目录的内容 生成器参数配置 通过设置GeneratorProperties.setProperty(key,value)设置相关参数值. 完整的配置参数请查看: rapid-generator/rapid-generator/src/main/java/cn/org/rapid_framework/generator/GeneratorConstants.java 生成器核心类图 Generator为生成器引擎 GeneratorFacade为生成器入口调用类 GeneratorProperties 生成器的相关配置,用于读取generator.properties(或者是generator.xml) TableFactory用于创建Table.java对象,用于GeneratorFacade.generateByTable()使用 SqlFactory用于创建Sql.java对象,用于GeneratorFacade.generateBySql()使用 JavaClass模板变量,用于GeneratorFacade.generateByClass()使用 重复生成代码 现rapid自带的模板都不支持重复生成代码的,如果你需要重复生成代码,可以采用继承机制技巧重复生成。 Java代码的重复生成,善用 "继承机制" , 示例如下: UserInfoBaseDao : 自动生成的代码, 不能手工修改,用于重复生成 UserInfoDao extends UserInfoBaseDao : 存放手工的代码,不能重复生成 页面的重复生成还没有啥好办法。 语法参考: http://freemarker.sourceforge.net/docs/index.html 具体使用请看 在线文档说明 本项目内置模板文件属于 rapid-framework 的一部分 rapid-framework简单介绍: rapid-framework是一个以spring为核心的项目脚手架(或者称为胶水框架),框架将各个零散的框架(struts,strust2,springmvc,hibernate,ibatis,spring_jdbc,flex)搭建好,并内置一个代码生成器,辅助项目开发,可以生成java的hibernat model,dao,manager,struts+struts2 action类,可以生成jsp的增删改查及列表页面 通常代码生成器存在的问题 开发困难,没有源码可以修改模板文件的model对象等 过于智能,自动插入我们的项目中,程序员还需考虑旧的代码会不会被覆盖的问题 生成文件的时候让你选要生成那些文件, 而rapid只负责生成代码,这样生成器核心一分精简,没有将存放模板的目录名称及文件名称利用起来,导致还需配置每个模板文件生成的文件名,目录结构

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值