歷史文章
本篇文章將會講解ruoyi-vue系統下代碼生成器的使用、原理分析以及將這部分代碼抽離出來形成獨立版的代碼生成器。
一、代碼生成器的使用
1.新建maven模塊
原則上,我們的業務代碼和若依系統本身的系統代碼是要做隔離的,一方面是易於之后隨着若依系統升級而升級,另一方面則是純粹的合理性考慮。
這里新建一個ruoyi-business模塊作為業務代碼模塊,新建完ruoyi-business模塊之后添加ruoyi-framework依賴,pom文件如下所示
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ruoyi</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.4.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.kdyzm</groupId>
<artifactId>ruoyi-business</artifactId>
<version>1.0.0-SNAPSHOT</version>
<dependencies>
<!-- 核心模塊-->
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-framework</artifactId>
</dependency>
</dependencies>
</project>
之后在ruoyi-admin添加ruoyi-business模塊的依賴
<dependency>
<groupId>com.kdyzm</groupId>
<artifactId>ruoyi-business</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
2.准備代碼生成器配置
ruoyi-vue系統中代碼生成器相代碼都在ruoyi-generator模塊中,代碼生成的配置在resources/generator.yml文件中,由於要在新的模塊ruoyi-business中做開發,要有個新包名,包名取作com.kdyzm.business,所以generator.yml配置文件內容如下:
# 代碼生成
gen:
# 作者
author: kdyzm
# 默認生成包路徑 system 需改成自己的模塊名稱 如 system monitor tool
packageName: com.kdyzm.business
# 自動去除表前綴,默認是false
autoRemovePre: false
# 表前綴(生成類名不會包含表前綴,多個用逗號分隔)
tablePrefix: sys_
另外,這里要使用自定義包名com.kdyzm.business,所以若依系統中mybatis也要做相應的修改
-
修改mybatis別名配置,增加對com.kdyzm包名的識別
# MyBatis配置 mybatis: # 搜索指定包別名 typeAliasesPackage: com.ruoyi.**.domain,com.kdyzm.**.domain -
修改mybatis的mapper掃描包路徑
修改com.ruoyi.framework.config.ApplicationConfig類的MapperScan注解,增加對com.kdyzm包的掃描
@MapperScan({"com.ruoyi.**.mapper","com.kdyzm.**.mapper"}) public class ApplicationConfig{ ... }
最后,在ruoyi-admin新增一個Config類,掃描com.kdyzm包,以將ruoyi-business模塊中的所有組件納入spring管理。
package com.ruoyi.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @author kdyzm
*/
@Configuration
@ComponentScan(basePackages = "com.kdyzm")
public class Config {
}
3.准備表
這里新建一張商品表作為示例,注意,這里的字段和表都要加上注釋
CREATE TABLE `goods` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`GOODS_NAME` varchar(255) DEFAULT NULL COMMENT '商品名字',
`put_way_flag` tinyint(1) DEFAULT NULL COMMENT '商品是否上架,0:下架,1:上架',
`create_time` datetime DEFAULT NULL COMMENT '創建時間',
`create_by` varchar(64) DEFAULT NULL COMMENT '創建人',
`update_time` datetime DEFAULT NULL COMMENT '更新時間',
`update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表'
4.生成代碼
進入系統工具-代碼生成頁面,點擊導入按鈕,找到goods表並導入,如下圖所示

然后點擊生成代碼按鈕

之后,就可以得到一個ruoyi.zip壓縮文件,壓縮文件中包含生成的前后端代碼以及sql語句文件,生成的代碼目錄結構如下所示
├── goodsMenu.sql
├── main
│ ├── java
│ │ └── com
│ │ └── kdyzm
│ │ └── business
│ │ ├── controller
│ │ │ └── GoodsController.java
│ │ ├── domain
│ │ │ └── Goods.java
│ │ ├── mapper
│ │ │ └── GoodsMapper.java
│ │ └── service
│ │ ├── IGoodsService.java
│ │ └── impl
│ │ └── GoodsServiceImpl.java
│ └── resources
│ └── mapper
│ └── business
│ └── GoodsMapper.xml
└── vue
├── api
│ └── business
│ └── goods.js
└── views
└── business
└── goods
└── index.vue
二、將生成的代碼應用到項目
1.后端代碼
將生成代碼中的main目錄直接拷貝到ruoyi-business模塊下的src目錄,可以看到生成的代碼是典型的三層架構,從controller到mapper都已經幫我們生成好了。

2.前端代碼
前端代碼對應着生成目錄中的vue目錄,這里將vue/api目錄中的內容拷貝到ruoyi-ui/src/api目錄中,將vue/views中的內容拷貝到ruoyi-ui/src/views目錄,操作上,直接將生成的api和views目錄拷貝到src目錄即可。

3.sql代碼
生成的sql代碼是創建菜單和按鈕權限使用的,直接在ruoyi數據庫下執行 goodsMenu.sql 文件中的內容即可。
-- 菜單 SQL
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('商品', '3', '1', 'goods', 'business/goods/index', 1, 0, 'C', '0', '0', 'business:goods:list', '#', 'admin', sysdate(), '', null, '商品菜單');
-- 按鈕父菜單ID
SELECT @parentId := LAST_INSERT_ID();
-- 按鈕 SQL
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('商品查詢', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', 'business:goods:query', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('商品新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', 'business:goods:add', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('商品修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', 'business:goods:edit', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('商品刪除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', 'business:goods:remove', '#', 'admin', sysdate(), '', null, '');
insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
values('商品導出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', 'business:goods:export', '#', 'admin', sysdate(), '', null, '');
三、測試
重新運行前后端代碼,打開系統工具菜單(為何在這里?這是若依vue的bug,已經提到gitee,bug鏈接: I3915P )的商品菜單

可以看到,前后端代碼只是復制粘貼,后端接口、前端菜單、權限等等一切都已經被代碼生成器幫我們做完了,可以說代碼生成器極大的簡化了我們的開發。
四、代碼生成器高級使用
在三的測試過程中,我們發現,商品是否上架這個字段對應的前端表單是個文本輸入框,但是實際上這里應該是個下拉列表框或者單選按鈕才對,因為它只有兩個值:0或者1,ruoyi-vue代碼生成器實際上是支持這種操作的。之前生成代碼的時候導入表之后直接點擊了下載按鈕生成了代碼,實際上跳過了一個重要的步驟,那就是編輯代碼生成選項。

點擊編輯按鈕之后,跳轉修改生成配置頁面。

在這個頁面中,可以修改字段在前端的顯示類型以及字典類型,比如,要想“商品是否上架”在前端展示為單選框,就可以修改顯示類型為“單選框”,字典類型設置為“業務是否(需要新增數據字典,0表示下架,1表示上架)”。
提交之后重新生成的代碼樣式:


可以看到,商品是否字段變成了下拉列表和單選框的樣式。
另外,若依代碼生成器支持三種數據格式模板的代碼生成:單表、樹表、主子表,這里默認使用的是單表模板,也是最常使用的模板。

五、代碼生成器原理
1.Velocity
Velocity是一個基於Java的模板引擎,其提供了一個Context容器,在java代碼里面我們可以往容器中存值,然后在vm文件中使用特定的語法獲取,這是velocity基本的用法,其與jsp、freemarker並稱為三大視圖展現技術。作為一個模塊引擎,除了作為前后端分離的MVC展現層,Velocity還有一些其他用途,比如源代碼生成。
在若依Vue系統中,正是使用了Velocity技術實現的源代碼生成。大體上,源代碼生成只需三步走:
- 創建模板文件
- 准備上下文(變量值)
- 替換模板文件中的變量
三步走完之后源代碼就生成了,說起來是很簡單的,但是實際上做起來會比較麻煩,特別是第一步創建模板文件是最復雜的,以下為index.vue模板文件部分源代碼:
#foreach($column in $columns)
#if($column.query)
#set($dictType=$column.dictType)
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($column.htmlType == "input")
...
可以看到,該vue模板文件中充斥着大量Velocity的if-else語法,嵌套在一起更是顯得無比復雜。
總之,整體上來看,java代碼的模板比較簡單,vue和mybatis mapper的模板文件比較復雜。
2.information_schema 數據庫
mysql數據庫中有一個information_schema數據庫,它是mysql的系統數據庫之一,它里面存儲着兩個表TABLES以及COLUMNS,這兩個表分別存儲着所有的表信息以及所有表中的列信息,代碼生成器正是以兩張表的信息為核心實現的。
3.ruoyi-vue代碼生成器源碼分析
ruoyi-vue代碼生成器相關代碼均位於ruoyi-generator模塊中,根據之前的實際操作體驗上來看,最簡單的情況,前端頁面只需要兩步即可完成代碼生成
- 導入表結構
- 生成代碼
實際上這兩步對應着后端的兩個接口:com.ruoyi.generator.controller.GenController#importTableSave 和 com.ruoyi.generator.controller.GenController#batchGenCode ,生成源碼的步驟就要從這兩步下手。
首先看com.ruoyi.generator.controller.GenController#importTableSave 接口,它做了以下這些事情
- 從information_schema數據庫的tables表中查詢目標表的表明、標注釋、創建時間和更新時間,但是忽略掉定時任務的表和已經生成過的表。
- 初始化表數據並將數據插入ruoyi數據庫的gen_table表
- 從information_schema數據庫的columns表中查詢目標表的列信息,包含字段名、字段注釋、字段類型、是否允許為null等詳細信息
- 初始化列信息並將數據插入ruoyi數據庫的gen_table_column表
接下來看下 com.ruoyi.generator.controller.GenController#batchGenCode 接口,它做了以下這些事情
- 從ruoyi數據庫的gen_table、gen_table_column表查詢出生成代碼需要的表和列信息。
- 初始化Velocity
- 准備Velocity上下文信息(變量值信息)
- 讀取模板、渲染模板,然后將渲染后的模板內容添加進如壓縮流,之后前端就可以下載zip壓縮文件了。
完畢。
六、擴展篇:封裝獨立版ruoyi-vue代碼生成器
1.為什么要做這個
作為一個后端開發,我最經常做的事情不是搞啥系統架構,而是最簡單的CRUD。。。若是能有一個代碼一鍵生成工具自動根據已經創建的表信息生成CRUD后端代碼,那豈不是能節省老鼻子功夫了——若依系統已經實現了這個代碼生成器的工具,但是它依賴於前端頁面,必須有權限訪問“系統工具-代碼生成”菜單才行,而這在企業中像我這種普通研發往往是是沒有權限訪問的。但是我有權限訪問表,查看表結構。作為一個有追求的開發,既不肯開口問別人要權限,還想要實現代碼生成器,該怎么做?
自己搞一個唄。
在這里封裝的代碼生成器不考慮前端頁面調整功能,其實現的功能更加注重於后端代碼,其作用和“系統工具-代碼生成”頁面中最簡單的生成代碼的兩步(導入表和下載代碼,無編輯)結果等效。
2.抽離ruoyi-vue代碼生成器邏輯
ruoyi-vue中的ruoyi-generator模塊有着完整的代碼生成邏輯,但是它依托於ruoyi-admin的spring-boot框架才能運行,現在我要將ruoyi-generator模塊的功能獨立於spring-boot,讓其作為一個普通的spring的程序,只有一個普通的main方法,實現和原來等效的功能。
這里的做法是直接修改ruoyi-generator模塊,刪除spring-boot的相關功能,但是保留spring、mybatis、druid等基礎組件的依賴,然后將這些組件手動重新納入spring容器中進行管理,最后通過main方法調用到相關模塊。
具體就不展開講了,有興趣的可以看看我的源代碼:https://gitee.com/kdyzm/ruoyi-vue-gen
3.獨立版代碼生成器使用方法
3.1 配置application.properties配置文件
該配置文件的內容如下:
mysql.username=${dbUserName}
mysql.password=${dbPassword}
mysql.connectionUrl=${dbUrl}
mysql.driverClass=${dbDriverClassName}
gen.author=kdyzm
gen.packageName=com.kdyzm.business
gen.autoRemovePre=false
gen.tableName=news
gen.tablePrefix=sys_
上半部分是數據庫配置,連接的是ruoyi-vue數據庫,正常配置即可;下半部分是生成配置,除了gen.tableName,其它配置和原ruoyi-vue代碼生成器的配置相同。
要注意,代碼生成結果僅使用用ruoyi-vue項目,如需自定義模板,需要修改源代碼。
3.2 打包和運行
在項目根目錄運行命令mvn clean package,打包完成之后切換到target目錄,使用命令java -jar ruoyi-vue-gen-1.0-SNAPSHOT.jar運行jar包得到ruoyi.zip壓縮文件
七、項目源代碼
獨立版代碼生成器源代碼:https://gitee.com/kdyzm/ruoyi-vue-gen
好了,ruoyi-vue代碼生成器篇就到此結束了,歡迎關注我的博客 https://blog.kdyzm.cn ~
