Foxnic-Web 代码生成 (1) —— 开始生成代码

基本原理

  应用 Foxnic-Web 以及 Foxnic-SQL 进行利用开发时,都能够反对代码生成。他们的区别是,基于 Foxnic-SQL 的疾速 main 函数启动的利用,只须要生成 Model 和 Service 即可。基于 Foxnic-Web 开发 Web 利用时,除了生成 Model 和 Service 以外,还要生成 Proxy、Controller、UI界面等。

  Foxnic 的代码生成是基于数据表的,所以当表构造变更,甚至只是正文的调整,咱们也是倡议从新生成必要的代码。在 Foxnic 的体系中,咱们认为最后的表结构设计、ER图设计,就是这个零碎设计的终点。后续的程序设计或数据结构设计都是表结构设计的连续。

  Foxnic 的代码生成体系心愿开发者能够有一个较高的开发终点,能够基于生成的代码间接开发利用,甚至是代码生成后无需批改就能够间接应用了。另一方面,咱们又不敞开二次开发的开放性,毕竟自在的批改代码才是软件系统能够按需定制的终极门路。这也是 Foxnic 体系没有走无代码或低代码平台的起因。

  本文中的示例代码均可在 https://gitee.com/LeeFJ/foxni... 我的项目中找到。

生成表构造元数据

  代码生成的第一步是生成表构造元数据。咱们晓得,数据表、列、索引等信息都存储在数据库内,咱们在 java 程序内无奈间接援用。但有些时候,咱们又须要用到这些货色,特地是表名、字段等。Foxnic-Web 在代码生成、以及其它业务逻辑编写时都有可能用到这些元数据。

  所以,咱们要通过一种形式将数据库元数据转换成 java 构造,这种构造是通过代码生成的。Java 生成的元数据它是动态的,不会随着变构造的扭转而扭转,Foxnic-Web 要求表构造变动后,须要从新生成元数据类。

  示例我的项目的 webfull 我的项目下的 WebFullDBMetaGenerator 类用于生成数据库元数据类:

package org.github.foxnic.web.generator.constants;import com.github.foxnic.dao.spec.DAO;import com.github.foxnic.generator.builder.constants.DBMetaClassFile;import org.github.foxnic.web.generator.config.WebFullConfigs;public class WebFullDBMetaGenerator {    /**    * 运行main函数生成代码    * */    public static void main(String[] args) throws Exception {        WebFullDBMetaGenerator g = new WebFullDBMetaGenerator();        g.buildDBMeta();    }    /**    * 生成DBMeta数据    * */    private void buildDBMeta() {        WebFullConfigs configs=new WebFullConfigs("webfull-service-example");        DAO dao=configs.getDAO();        DBMetaClassFile dbMetaBuilder=new DBMetaClassFile(dao,configs.getDomainProject(),configs.getProjectConfigs().getDomainConstantsPackage(),"WebFullTables");        dbMetaBuilder.setTableFilter(table->{            table=table.toLowerCase();            // 仅生成以 example_ 结尾的表            if(table.startsWith("webfull_")) return true;            return false;        });        dbMetaBuilder.save(true);    }}

  执行 main 函数后,在 domain 模块内生成 WebFullTables 类,前面生成其它代码,咱们会用到这个类中数据库表构造定义的常量。

非 Web 环境的代码生成

  事实上,非 Web 环境的开发是很少的,然而咱们在解说 Foxnic-SQL 局部的时候,也用到了非 Web 环境的代码生成,大家参考 Foxnic-SQL 相干的文档即可。

  在 https://gitee.com/LeeFJ/foxni... 我的项目的 com/leefj/foxnic/sql/demo/generator 目录的 ExampleCodeGenerator.java 中有示例,大家看代码联合咱们的文档,很容易了解。前面的篇幅咱们着重介绍 Web 环境下的代码生成,所有 Web 环境下代码生成的原理一样实用于非 Web 环境。

  为了不便了解,咱们还是贴一下代码:

package com.leefj.foxnic.sql.demo.generator;import com.github.foxnic.commons.project.maven.MavenProject;import com.github.foxnic.dao.spec.DAO;import com.github.foxnic.generator.builder.model.PoClassFile;import com.github.foxnic.generator.config.ModuleContext;import com.github.foxnic.sql.meta.DBTable;import com.leefj.foxnic.sql.demo.app.domain.example.Address;import com.leefj.foxnic.sql.demo.app.domain.example.Goods;import com.leefj.foxnic.sql.demo.app.domain.example.Order;import com.leefj.foxnic.sql.demo.app.domain.example.OrderItem;import com.leefj.foxnic.sql.demo.config.DBInstance;import com.leefj.foxnic.sql.demo.config.db.ExampleTables;/*** 代码生成器* */public class ExampleCodeGenerator {    public static interface  Config  {        void config(PoClassFile poType);    }    private static final String BASE_PACKAGE = "com.leefj.foxnic.sql.demo.app";    /**    * 须要首先运行 ExampleDBMetaGenerator 生成 ExampleTables 类    * */    public static void main(String[] args) {        ExampleCodeGenerator generator = new ExampleCodeGenerator();        // 生成商品实体类        generator.generate(ExampleTables.EXAMPLE_GOODS.$TABLE, poType -> {            // Goods 对象 通过 orderList 属性持有 Order            poType.addListProperty(Goods.class,"orderList","订单明细商品","订单明细商品");            // Goods 对象 通过 addressList 属性持有 Address            poType.addListProperty(Address.class,"addressList","收件地址","收件地址,包含收件人以及手机号码");            // Goods 对象 通过 itemList 属性持有 OrderItem            poType.addListProperty(OrderItem.class,"itemList","订单明细","订单明细");        });        // 生成订单实体类        generator.generate(ExampleTables.EXAMPLE_ORDER.$TABLE , poType -> {            // Order 对象 通过 goodsList 属性持有 Goods            poType.addListProperty(Goods.class,"goodsList","订单明细商品","订单明细商品");            // Order 对象 通过 address 属性持有 Address            poType.addSimpleProperty(Address.class,"address","收件地址","收件地址,包含收件人以及手机号码");            // Order 对象 通过 itemList 属性持有 OrderItem            poType.addListProperty(OrderItem.class,"itemList","订单明细","订单明细");        });        // 生成订单明细实体类        generator.generate(ExampleTables.EXAMPLE_ORDER_ITEM.$TABLE, poType -> {            // OrderItem 对象 通过 goodsList 属性持有 Goods            poType.addSimpleProperty(Goods.class,"goods","订单明细商品","订单明细商品");            // OrderItem 对象 通过 address 属性持有 Address            poType.addSimpleProperty(Address.class,"address","收件地址","收件地址,包含收件人以及手机号码");            // OrderItem 对象 通过 order 属性持有 Order            poType.addListProperty(Order.class,"order","订单","订单");        });        // 生成地址实体类        generator.generate(ExampleTables.EXAMPLE_ADDRESS.$TABLE, poType -> {            // Address 对象 通过 goodsList属性 持有 Goods            poType.addListProperty(Goods.class,"goodsList","订单明细商品","订单明细商品");            // Address 对象 通过 orderList 持有 Order            poType.addListProperty(Address.class,"orderList","收件地址","收件地址,包含收件人以及手机号码");            // Address 对象 通过 itemList 持有 OrderItem            poType.addListProperty(OrderItem.class,"itemList","订单明细","订单明细");        });    }    /**    * 按表生成    * */    public void generate(DBTable table) {        generate(table,null);    }    /**    * 按表生成    * */    public void generate(DBTable table,Config config) {        DAO dao = DBInstance.DEFAULT.dao();        MavenProject project = GeneratorUtil.getProject();        String pkg = table.name().split("_")[0];        String prefix = pkg + "_";        ModuleContext context = new ModuleContext(GeneratorUtil.initGlobalSettings(),table,prefix,BASE_PACKAGE + "." + pkg);        context.setDomainProject(project);        context.setServiceProject(project);        context.setDAO(dao);        if(config!=null) {            config.config(context.getPoClassFile());        }        context.buildPo();        context.buildVo();        context.buildService();    }                    }

Web 环境的代码生成

  Web 环境的代码生成本文以 webfull 我的项目为示例, 它以 WebFullCodeStarter 开始,初始化代码生成器,各模块配置,最初按生成用户指定的模块代码。代码生成的模块配置是按表配置的,一个表对应一套代码。咱们先来看一下 WebFullCodeStarter 的代码:

package org.github.foxnic.web.generator.module;import com.github.foxnic.generator.util.ModuleCodeGenerator;import org.github.foxnic.web.generator.module.bpm.ExampleReimbursementConfig;import org.github.foxnic.web.generator.module.mall.ExampleAddressConfig;import org.github.foxnic.web.generator.module.mall.ExampleGoodsConfig;import org.github.foxnic.web.generator.module.mall.ExampleOrderConfig;import org.github.foxnic.web.generator.module.mall.ExampleOrderItemConfig;/*** 代码生成启动类* */public class WebFullCodeStarter extends ModuleCodeGenerator {        public static void main(String[] args) {        // 新建启动类对象        WebFullCodeStarter g=new WebFullCodeStarter();        // 初始化本次须要生成代码的模块        g.initModules();        // 启动        g.start();    }    /**    * 初始化本次须要生成代码的模块    * */    public void initModules() {        initExampleModules();        initBPMModules();    }    /**    * 初始化 BPM 示例模块    * */    private void initBPMModules() {        this.addConfig(new ExampleReimbursementConfig());    }    /**    * 初始化订单示例模块    * */    private void initExampleModules() {        this.addConfig(new ExampleGoodsConfig());        this.addConfig(new ExampleAddressConfig());        this.addConfig(new ExampleOrderConfig());        this.addConfig(new ExampleOrderItemConfig());    }}

  启动 WebFullCodeStarter 的 main 函数,输入如下:

输出 ALL 生成全副

或输出需模块序号: (1) webfull_example_goods (2) webfull_example_address (3) webfull_example_order (4) webfull_example_order_item (5) webfull_example_reimbursement

|

  此时,输出 all 或 a 生成列表中全副模块的代码,如果输出指定模块的序号,则社能成指定模块的代码。

  从这个动图中,咱们不难发现,代码生成器不用重启能够重复生成。针对一些界面的生成,能够边批改代码生成的配置,边生成边测试,咱们把它叫做迭代式的代码生成。

  为了可能重复进行代码生成,咱们针对模块代码的构造做了一些非凡的设计,尽量把业务逻辑的代码剥离到某个独自的文件,其它文件则能够重复生成,这种代码生成的形式极大的进步了开发效率。

代码生成的后果

  某个模块的代码生成后,在我的项目的各个 Maven 子模块下生成相应的 Java 类、Html 文件、js 文件。上面对各个文件阐明如下:

文件类型示例地位阐明
POOrder.javadomain与数据表对应的实体类。
POMetaOrderMeta.javadomain针对 PO 类的元数据形容,蕴含一个 PO Proxy 外部类。
VOOrderVO.javadomain用于 API 接口传参、返回等。
VOMetaOrderVOMeta.javadomain针对 VO 类的元数据形容,蕴含一个 VO Proxy 外部类。
自定义模型CustomModeldomain代码生成时自定义的模型类。
ProxyOrderServiceProxy.javaproxy接口代理、Feign代理接口、接口门路定义。
服务接口IOrderService.javaservice服务接口
服务实现OrderServiceImpl.javaservice服务接口实现
流程反对OrderBpmEventAdaptor.javaservice可选;开启流程审批时会生成,解决流程逻辑。
API控制器OrderControllerserviceRest API 接口控制器
页面控制器OrderPageControllerview页面控制器
列表页order_list.htmlview列表页,表格展现数据。
列表页JSorder_list.jsview列表页对应的JS文件
表单页order_form.htmlview表单页,表单形式展现、编辑数据。
表单页JSorder_form.jsview表单页JS
逻辑扩大JSorder_ext.jsview剥离表单、表格业务逻辑,使页面代码可重复生成。

  从下面的表格咱们能够看到,一个数据表对用的简略模块,就蕴含可诸多文件。其中,彩色加粗的几项不倡议手动批改,如要批改能够通过代码生成的形式从新生成。

  页面如果有业务逻辑,尽量到 xxx_ext.js 文件批改,因为一旦批改也列表页或表单也的代码就无奈再次从新生成代码。

我的项目依赖

  Foxnic-Web 代码生成的形式是迭代式的,所以被生成的代码须要依赖到代码生成的我的项目中。以 webfull 我的项目的 generator 我的项目为例,在它的 pom 文件中须要退出 domain、proxy、service 和 view 的依赖。

<dependencies>  <!-- 通用根底模块 -->  <dependency>    <groupId>com.github.foxnic.web</groupId>    <artifactId>framework-boot</artifactId>    <version>${foxnic.web.version}</version>  </dependency>  <dependency>    <groupId>com.github.foxnic.web</groupId>    <artifactId>framework-cloud</artifactId>    <version>${foxnic.web.version}</version>  </dependency>  <dependency>    <groupId>com.github.foxnic</groupId>    <artifactId>foxnic-generator</artifactId>    <version>${foxnic.version}</version>  </dependency>  <!-- 以后我的项目根底模块 -->  <dependency>    <groupId>com.github.foxnic.example.webfull</groupId>    <artifactId>webfull-proxy</artifactId>    <version>${project.version}</version>  </dependency>  <dependency>    <groupId>com.github.foxnic.example.webfull</groupId>    <artifactId>webfull-domain</artifactId>    <version>${project.version}</version>  </dependency>  <dependency>    <groupId>com.github.foxnic.example.webfull</groupId>    <artifactId>webfull-framework</artifactId>    <version>${project.version}</version>  </dependency>  <!-- 以后我的项目业务模块 -->  <dependency>    <groupId>com.github.foxnic.example.webfull</groupId>    <artifactId>webfull-service-example</artifactId>    <version>${project.version}</version>  </dependency>  <dependency>    <groupId>com.github.foxnic.example.webfull</groupId>    <artifactId>webfull-view-example</artifactId>    <version>${project.version}</version>  </dependency></dependencies>

小结

  本节次要介绍了在 Foxnic-SQL 和 oxnic-Web 代码生成的根本步骤,以及代码生成的文件散布,具体作用等。尤其要留神是代码生成我的项目的依赖问题,可能导致异样而无奈正确生成代码。

  前面的章节中,咱们将进一步介绍代码生成的细节。

相干我的项目

  https://gitee.com/LeeFJ/foxnic

  https://gitee.com/LeeFJ/foxni...

  https://gitee.com/lank/eam

  https://gitee.com/LeeFJ/foxni...

官网文档

  http://foxnicweb.com/docs/doc...