告別硬編碼!用 SpringBoot 玩轉(zhuǎn)動(dòng)態(tài)表單與問卷生成神器
在企業(yè)級(jí)項(xiàng)目中,表單幾乎無處不在。從內(nèi)部管理后臺(tái),到客戶信息采集,再到問卷調(diào)查、審批流程配置,開發(fā)者常常陷入“表單地獄”: 字段多、規(guī)則復(fù)雜、需求變更頻繁。每次新增字段、調(diào)整驗(yàn)證規(guī)則,都意味著前后端同時(shí)改代碼、發(fā)版本、聯(lián)調(diào)測(cè)試——工作量翻倍,幸福感歸零。
有沒有可能,讓表單脫離硬編碼,讓前端和后端都只需遵守一份“契約”就能協(xié)同工作?
答案是:JSON Schema。 結(jié)合 Spring Boot 的靈活性和 JSON Schema 的結(jié)構(gòu)描述能力,我們可以輕松構(gòu)建出一個(gè)通用動(dòng)態(tài)表單系統(tǒng)——無需修改 Java 類,也無需改前端模板,所有表單結(jié)構(gòu)都由數(shù)據(jù)庫(kù)中的 JSON 控制,實(shí)時(shí)生效。
本文將帶你從原理到實(shí)現(xiàn),完整走通一條路徑:
/project
├── /src/main/java/com/icoderoad/form
│ ├── controller/FormController.java
│ ├── entity/DynForm.java
│ └── repository/DynFormRepository.java
├── /src/main/resources/application.yml
└── /frontend/vue/
└── FormPage.vue認(rèn)識(shí) JSON Schema:讓結(jié)構(gòu)成為“契約”
在傳統(tǒng)開發(fā)中,我們通常用 Java Bean 或 DTO 來定義數(shù)據(jù)結(jié)構(gòu)。但這意味著一旦字段變化,就必須重新編譯部署。 JSON Schema 的目標(biāo),就是讓數(shù)據(jù)結(jié)構(gòu)以 JSON 格式定義、校驗(yàn)、共享。
它的設(shè)計(jì)理念很簡(jiǎn)單:
用 JSON 描述 JSON,讓數(shù)據(jù)自解釋,讓契約可傳遞。
下面是一個(gè)簡(jiǎn)化示例:
{
"type": "object",
"properties": {
"name": { "type": "string", "title": "姓名" },
"age": { "type": "number", "title": "年齡", "minimum": 0 },
"city": { "type": "string", "title": "城市", "enum": ["北京", "上海"] }
},
"required": ["name"]
}它的強(qiáng)大之處在于:
場(chǎng)景 | 作用 |
接口校驗(yàn) | 后端可直接基于 Schema 驗(yàn)證請(qǐng)求合法性 |
前端渲染 | 低代碼或自動(dòng)化前端可根據(jù) Schema 自動(dòng)生成表單 |
IDE 提示 | VSCode 可識(shí)別 Schema,智能提示 JSON 字段與規(guī)則 |
也就是說,一旦定義好 Schema,前后端都可以對(duì)著這份 JSON 契約工作,再也不用為了字段變動(dòng)反復(fù)修改代碼。
動(dòng)態(tài)表單的真正挑戰(zhàn)是什么?
動(dòng)態(tài)表單的“難”,其實(shí)不在 UI,而在動(dòng)態(tài)性。
挑戰(zhàn)點(diǎn) | 說明 |
① 結(jié)構(gòu)未知 | 字段、類型、校驗(yàn)規(guī)則都由運(yùn)行時(shí)決定,傳統(tǒng) DTO 完全不適用 |
② 數(shù)據(jù)綁定復(fù)雜 | 嵌套字段、數(shù)組項(xiàng)動(dòng)態(tài)變動(dòng),容易引發(fā)前端渲染異常 |
③ 校驗(yàn)不同步 | 前后端兩套驗(yàn)證規(guī)則不一致,導(dǎo)致線上錯(cuò)誤數(shù)據(jù) |
而 JSON Schema 正好從根上解決了這三個(gè)問題。接下來,我們就用它來構(gòu)建一個(gè)最小可運(yùn)行閉環(huán)。
后端實(shí)戰(zhàn):Spring Boot + JSON Schema 校驗(yàn)
技術(shù)選型
模塊 | 技術(shù) | 職責(zé) |
后端 | Spring Boot + json-schema-validator | 存儲(chǔ)與校驗(yàn) Schema |
前端 | Vue3 + @jsonforms/vue | 動(dòng)態(tài)渲染表單 |
數(shù)據(jù) | MySQL | 存儲(chǔ)表單定義 |
通信 | JSON | 無額外協(xié)議依賴 |
實(shí)體類定義:/src/main/java/com/icoderoad/form/entity/DynForm.java
package com.icoderoad.form.entity;
import jakarta.persistence.*;
import lombok.Data;
/**
* 動(dòng)態(tài)表單實(shí)體類
* 用于存儲(chǔ)每個(gè)表單的結(jié)構(gòu)定義(JSON Schema)
*/
@Entity
@Data
@Table(name = "dyn_form")
public class DynForm {
@Id
private String id;
private String name;
@Column(columnDefinition = "TEXT")
private String schema; // 存儲(chǔ) JSON Schema 定義
}控制層:/src/main/java/com/icoderoad/form/controller/FormController.java
package com.icoderoad.form.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.icoderoad.form.entity.DynForm;
import com.icoderoad.form.repository.DynFormRepository;
import com.networknt.schema.*;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import java.util.Set;
/**
* 動(dòng)態(tài)表單控制器
* 提供表單 Schema 查詢與數(shù)據(jù)提交接口
*/
@RestController
@RequestMapping("/form")
@RequiredArgsConstructor
public class FormController {
private final DynFormRepository repo;
private final ObjectMapper mapper = new ObjectMapper();
/** 獲取表單 Schema */
@GetMapping("/{id}/schema")
public Map<String, Object> getSchema(@PathVariable String id) throws Exception {
DynForm form = repo.findById(id)
.orElseThrow(() -> new RuntimeException("表單不存在"));
return mapper.readValue(form.getSchema(), Map.class);
}
/** 提交并校驗(yàn)表單數(shù)據(jù) */
@PostMapping("/{id}/submit")
public String submit(@PathVariable String id, @RequestBody Map<String, Object> data) throws Exception {
DynForm form = repo.findById(id)
.orElseThrow(() -> new RuntimeException("表單不存在"));
JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V201909);
JsonSchema schema = factory.getSchema(form.getSchema());
Set<ValidationMessage> errors = schema.validate(mapper.valueToTree(data));
if (!errors.isEmpty()) {
throw new RuntimeException("數(shù)據(jù)校驗(yàn)失敗:" + errors);
}
// 保存數(shù)據(jù)邏輯(省略)
// repo.saveData(id, data);
return "ok";
}
}依賴配置:pom.xml
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JSON Schema Validator -->
<dependency>
<groupId>com.networknt</groupId>
<artifactId>json-schema-validator</artifactId>
<version>1.4.0</version>
</dependency>
<!-- JSON 處理 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>前端實(shí)現(xiàn):Vue3 + @jsonforms/vue 自動(dòng)渲染
安裝依賴
npm install @jsonforms/vue @jsonforms/vue-vanilla axios頁(yè)面文件:/frontend/vue/FormPage.vue
<template>
<div class="container mt-4">
<json-forms
:data="formData"
:schema="schema"
:uischema="uiSchema"
@change="formData = $event.data" />
<button class="btn btn-primary mt-3" @click="submitForm">提交</button>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { JsonForms } from '@jsonforms/vue';
import axios from 'axios';
const schema = ref({});
const uiSchema = ref({});
const formData = ref({});
onMounted(async () => {
const res = await axios.get('/form/1/schema');
schema.value = res.data;
});
const submitForm = async () => {
await axios.post('/form/1/submit', formData.value);
alert('提交成功!');
};
</script>亮點(diǎn):
- 后端修改 JSON Schema → 前端自動(dòng)更新表單;
- 新增字段、修改枚舉、調(diào)整驗(yàn)證,全程無需改動(dòng)前端代碼;
- 支持多端(H5 / 小程序 / PC)共用一份 Schema。
進(jìn)階玩法:從動(dòng)態(tài)到“低代碼”
功能方向 | 實(shí)現(xiàn)方式 |
可視化搭建 | 接入拖拽式表單設(shè)計(jì)器(如 FormMaking、amis),讓運(yùn)營(yíng)直接生成 Schema |
版本控制 | Schema 加 version 字段,實(shí)現(xiàn)灰度表單與歷史回溯 |
多端共用 | 統(tǒng)一 Schema 驅(qū)動(dòng) H5、小程序、Web 端渲染 |
流程集成 | 結(jié)合規(guī)則引擎與數(shù)據(jù)流轉(zhuǎn),升級(jí)為企業(yè)級(jí)低代碼平臺(tái) |
總結(jié):Schema 是契約,更是自由
JSON Schema 的出現(xiàn),讓“表單開發(fā)”這件事從繁瑣變?yōu)閮?yōu)雅。 后端不必再編寫冗長(zhǎng)的 DTO,前端也無需反復(fù)手寫輸入框和校驗(yàn)邏輯。 只需一份 Schema,就能讓前后端共享同一個(gè)“真相”。
JSON Schema 不是什么銀彈,但它足夠?qū)嵱谩⑤p量、靈活。 對(duì)于中小企業(yè)、SaaS 平臺(tái)、問卷系統(tǒng)、信息采集工具而言,它幾乎是最具性價(jià)比的解決方案。
未來的表單開發(fā),不再是手動(dòng)堆疊組件,而是通過 Schema 去“聲明”它的存在。 這,才是告別硬編碼后的真正自由。































