共计 9202 个字符,预计需要花费 24 分钟才能阅读完成。
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 文件。上面对各个文件阐明如下:
文件类型 | 示例 | 地位 | 阐明 |
---|---|---|---|
PO | Order.java | domain | 与数据表对应的实体类。 |
POMeta | OrderMeta.java | domain | 针对 PO 类的元数据形容,蕴含一个 PO Proxy 外部类。 |
VO | OrderVO.java | domain | 用于 API 接口传参、返回等。 |
VOMeta | OrderVOMeta.java | domain | 针对 VO 类的元数据形容,蕴含一个 VO Proxy 外部类。 |
自定义模型 | CustomModel | domain | 代码生成时自定义的模型类。 |
Proxy | OrderServiceProxy.java | proxy | 接口代理、Feign 代理接口、接口门路定义。 |
服务接口 | IOrderService.java | service | 服务接口 |
服务实现 | OrderServiceImpl.java | service | 服务接口实现 |
流程反对 | OrderBpmEventAdaptor.java | service | 可选;开启流程审批时会生成,解决流程逻辑。 |
API 控制器 | OrderController | service | Rest API 接口控制器 |
页面控制器 | OrderPageController | view | 页面控制器 |
列表页 | order_list.html | view | 列表页,表格展现数据。 |
列表页 JS | order_list.js | view | 列表页对应的 JS 文件 |
表单页 | order_form.html | view | 表单页,表单形式展现、编辑数据。 |
表单页 JS | order_form.js | view | 表单页 JS |
逻辑扩大 JS | order_ext.js | view | 剥离表单、表格业务逻辑,使页面代码可重复生成。 |
从下面的表格咱们能够看到,一个数据表对用的简略模块,就蕴含可诸多文件。其中,彩色加粗的几项不倡议手动批改,如要批改能够通过代码生成的形式从新生成。
页面如果有业务逻辑,尽量到 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…