若依管理系統RuoYi-Vue(三):代碼生成器原理和實戰


歷史文章

若依管理系統RuoYi-Vue(一):項目啟動和菜單創建
若依管理系統RuoYi-Vue(二):權限系統設計詳解

本篇文章將會講解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表並導入,如下圖所示

image-20210225165412336

然后點擊生成代碼按鈕

image-20210225174111317

之后,就可以得到一個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都已經幫我們生成好了。

yanshi7

2.前端代碼

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

yanshi8

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 )的商品菜單

yanshi9

可以看到,前后端代碼只是復制粘貼,后端接口、前端菜單、權限等等一切都已經被代碼生成器幫我們做完了,可以說代碼生成器極大的簡化了我們的開發。

四、代碼生成器高級使用

在三的測試過程中,我們發現,商品是否上架這個字段對應的前端表單是個文本輸入框,但是實際上這里應該是個下拉列表框或者單選按鈕才對,因為它只有兩個值:0或者1,ruoyi-vue代碼生成器實際上是支持這種操作的。之前生成代碼的時候導入表之后直接點擊了下載按鈕生成了代碼,實際上跳過了一個重要的步驟,那就是編輯代碼生成選項。

image-20210226111535572

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

image-20210226111833327

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

提交之后重新生成的代碼樣式:

image-20210226131851918

image-20210226131953348

可以看到,商品是否字段變成了下拉列表和單選框的樣式。

另外,若依代碼生成器支持三種數據格式模板的代碼生成:單表、樹表、主子表,這里默認使用的是單表模板,也是最常使用的模板。

image-20210226153053315

五、代碼生成器原理

1.Velocity

Velocity是一個基於Java的模板引擎,其提供了一個Context容器,在java代碼里面我們可以往容器中存值,然后在vm文件中使用特定的語法獲取,這是velocity基本的用法,其與jsp、freemarker並稱為三大視圖展現技術。作為一個模塊引擎,除了作為前后端分離的MVC展現層,Velocity還有一些其他用途,比如源代碼生成。

在若依Vue系統中,正是使用了Velocity技術實現的源代碼生成。大體上,源代碼生成只需三步走:

  1. 創建模板文件
  2. 准備上下文(變量值)
  3. 替換模板文件中的變量

三步走完之后源代碼就生成了,說起來是很簡單的,但是實際上做起來會比較麻煩,特別是第一步創建模板文件是最復雜的,以下為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#importTableSavecom.ruoyi.generator.controller.GenController#batchGenCode ,生成源碼的步驟就要從這兩步下手。

首先看com.ruoyi.generator.controller.GenController#importTableSave 接口,它做了以下這些事情

  1. 從information_schema數據庫的tables表中查詢目標表的表明、標注釋、創建時間和更新時間,但是忽略掉定時任務的表和已經生成過的表。
  2. 初始化表數據並將數據插入ruoyi數據庫的gen_table表
  3. 從information_schema數據庫的columns表中查詢目標表的列信息,包含字段名、字段注釋、字段類型、是否允許為null等詳細信息
  4. 初始化列信息並將數據插入ruoyi數據庫的gen_table_column表

接下來看下 com.ruoyi.generator.controller.GenController#batchGenCode 接口,它做了以下這些事情

  1. 從ruoyi數據庫的gen_table、gen_table_column表查詢出生成代碼需要的表和列信息。
  2. 初始化Velocity
  3. 准備Velocity上下文信息(變量值信息)
  4. 讀取模板、渲染模板,然后將渲染后的模板內容添加進如壓縮流,之后前端就可以下載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 ~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM