View Starter
受众:开发者 摘要:Thymeleaf 视图 + 本地前端资源(Vue 3 / Pico CSS WebJar)+
public/挂载 + SPA 入口 + 根路径智能路由。
何时使用
| 场景 | 建议 |
|---|---|
| Payment mock 支付调试页 | View starter |
| Storage 上传 / Notification / Queue / Scheduler 轻量运维页 | View starter |
| Starter 样板预览页 | View starter |
| 大型后台管理前端 | 独立 Vue / React 工程,不用 View |
| 重交互 SPA 主应用 / 用户侧正式门户 | 独立前端工程,不用 View |
| 安装向导临时入口 | View starter + ViewRootRouteTargetProvider 接管 / |
主线仍是前后端分离。View 只解决「启动后立刻能访问一个小页面」的开发体验问题。
快速开始
<dependency>
<groupId>com.springopen</groupId>
<artifactId>spring-open-starter-view</artifactId>
</dependency>模板:
src/main/resources/templates/sample/index.html<!doctype html>
<html lang="zh-CN" xmlns:th="https://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<title>Sample</title>
<link rel="stylesheet" th:href="@{/webjars/picocss__pico/2.1.1/css/pico.min.css}">
</head>
<body>
<main id="app" class="container">
<h1>{{ title }}</h1>
</main>
<script th:src="@{/webjars/vue/3.5.17/dist/vue.global.prod.js}"></script>
<script>
const { createApp } = Vue;
createApp({
data() { return { title: 'Hello View' }; }
}).mount('#app');
</script>
</body>
</html>Controller:
@Controller
@PublicApi
@IgnoreResponseWrapper
public class SamplePageController {
@GetMapping("/sample.html")
public String sample() {
return "sample/index";
}
}访问 GET /sample.html。
Facade / 编程入口
View 不提供 Facade。能力通过:
- Spring MVC
@Controller+ Thymeleaf 模板 ViewRootRouteTargetProviderSPI:业务模块在特定状态接管/ViewVendorResourceHandler:自定义 vendor 资源映射
业务模块按需注册 ViewRootRouteTargetProvider Bean 即可参与根路径路由。
配置项
spring:
open:
view:
enabled: true
root:
enabled: true
mode: auto
target: /index.html
desktop-target: /pc
mobile-target: /app
preserve-query-string: true
database-enabled: true
external-redirect-enabled: false
allowed-redirect-hosts: []
spa:
enabled: true
paths: [admin, app, pc]
vendor:
enabled: false
vue: true
pico: true| 配置 | 默认值 | 说明 |
|---|---|---|
enabled | true | 启用开关 |
root.enabled | true | 根路径智能入口 |
root.mode | auto | auto / file / forward / redirect / disabled |
root.target | /index.html | 根路径目标 |
root.desktop-target | /pc | root.target 为空或 / 时的电脑端跳转 |
root.mobile-target | /app | root.target 为空或 / 时的手机 / 平板跳转 |
root.preserve-query-string | true | 保留原始 query |
root.database-enabled | true | 数据库配置覆盖根路径 |
root.external-redirect-enabled | false | 外部 URL 重定向 |
root.allowed-redirect-hosts | [] | 外部重定向白名单 |
spa.enabled | true | SPA 目录入口转发到 index.html |
spa.paths | [admin, app, pc] | 参与转发的目录名 |
vendor.enabled | false | /vendor/** 自定义资源映射 |
vendor.vue | true | /vendor/vue/** |
vendor.pico | true | /vendor/pico/css/** |
环境变量:
SPRING_OPEN_VIEW_ENABLED=true
SPRING_OPEN_VIEW_ROOT_DESKTOP_TARGET=/pc
SPRING_OPEN_VIEW_ROOT_MOBILE_TARGET=/app
SPRING_OPEN_VIEW_VENDOR_ENABLED=false
SPRING_OPEN_VIEW_VENDOR_VUE=true
SPRING_OPEN_VIEW_VENDOR_PICO=trueThymeleaf 配置仍用 Spring Boot 标准:
spring:
thymeleaf:
enabled: true
cache: false
check-template-location: false根路径智能路由
根路径用低优先级映射:
- 业务项目已提供
GET /Controller → 业务优先 - 否则用
public/index.html或配置的forward/redirect目标 target为空或/→ 按设备跳转(电脑/pc,手机 / 平板/app),保留 query- 外部重定向默认关闭,开启需配
allowed-redirect-hosts防开放重定向
开启首页设备分流:
spring:
open:
view:
root:
target: /
desktop-target: /pc
mobile-target: /app业务模块通过 ViewRootRouteTargetProvider 在特定状态接管根路径。例如 install.md 在未安装且未锁定时把 / 转入 /install/,安装完成后自动失效。
机制说明
View starter 的路由机制分成三层,避免把轻量页面、三端 SPA 和安装向导入口混在一个 Controller 中:
| 层级 | 组件 | 说明 |
|---|---|---|
| 根路径解析 | ViewRootRouteResolver | 读取本地配置和数据库配置,先让 ViewRootRouteTargetProvider 接管,再生成 file / forward / redirect 目标 |
| 根路径执行 | ViewRootRouteHandlerMapping + ViewRootRouteHandler | 使用低优先级映射处理 GET / 和 HEAD /,用户项目自定义根路由优先;目标禁用时返回 404 |
| SPA 入口 | ViewSpaForwardingConfigurer + ViewSpaRouteHandlerMapping | /admin、/app、/pc 转发到各自 index.html;深层无扩展名路由兜底到 index.html,静态资源请求不兜底 |
根路径安全边界:
- 站内目标必须以
/开头,拒绝协议、协议相对 URL、反斜杠、路径穿越、控制字符和脚本 URL。 - 外部重定向默认关闭;开启后必须命中
allowed-redirect-hosts,只允许http/https。 target为空或/时按 User-Agent 跳转到desktop-target/mobile-target,并按配置保留 query string。ViewRootRouteTargetProvider只负责在特定业务状态下提供目标,例如安装模块未安装时接管/;正常上线后的 PC / App 入口仍由 View 根路由配置控制。
SPA 目录入口
spa.paths 列出的目录入口会转发到各自的 index.html,支持前端 history 路由直接刷新子路径:
/admin → /admin/index.html
/app → /app/index.html
/pc → /pc/index.html默认 [admin, app, pc],覆盖三端 SPA 静态产物挂载目录。/admin 和 /admin/ 两种写法都会处理。需要新增前端入口(如 /h5)时追加到 spa.paths;完全自管路由时设 spa.enabled: false。
Provider
View 不提供 Provider 切换。扩展点:
| 扩展点 | 用途 |
|---|---|
ViewRootRouteTargetProvider SPI | 业务模块接管根路径 |
ViewVendorResourceHandler | 自定义 vendor 资源映射 |
| Thymeleaf 标准扩展(dialect / processor / template resolver) | 模板增强 |
本地前端资源
内置 WebJar:
| 资源 | WebJar | 默认路径 |
|---|---|---|
| Vue 3 | org.webjars.npm:vue | /webjars/vue/3.5.17/dist/vue.global.prod.js |
| Pico CSS | org.webjars.npm:picocss__pico | /webjars/picocss__pico/2.1.1/css/pico.min.css |
默认优先用 Spring Boot / WebJars 原生路径,离线、内网也能打开样板页。
不手工复制 CDN 文件到项目。升级 Vue / Pico 时同步更新 Maven 依赖版本、模板引用、ViewVendorResourceHandler 常量和测试断言。
可选 /vendor/**
默认关闭,避免两套静态资源路径。需要隐藏版本号或统一前缀时开启:
spring:
open:
view:
vendor:
enabled: true/vendor/vue/vue.global.prod.js
/vendor/pico/css/pico.min.css<link rel="stylesheet" th:href="@{/vendor/pico/css/pico.min.css}">
<script th:src="@{/vendor/vue/vue.global.prod.js}"></script>新页面默认用 /webjars/**;除非迁移旧页面或有 vendor 前缀需求,不同时混用两套路径。
CDN 处理
样板页默认不能依赖 CDN。可保留注释方便临时调试:
<!-- CDN fallback: <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script> -->
<script th:src="@{/webjars/vue/3.5.17/dist/vue.global.prod.js}"></script>生效的 <script> / <link> 不指向公网 CDN,确保离线开发、内网部署和安全扫描稳定。
页面 Controller 规范
页面用 @Controller,不用 @RestController:
@Controller
@PublicApi
@IgnoreResponseWrapper
public class SamplePageController {
@GetMapping("/samples/preview.html")
public String page(Model model) {
model.addAttribute("title", "Preview");
return "samples/preview";
}
}| 规范 | 说明 |
|---|---|
| 返回模板名 | 不返回 Result<T> |
| 不写业务逻辑 | 只组装页面 model |
| JSON API 分开 | 仍放 @RestController |
| 公开访问 | 标 @PublicApi |
| 避免响应封装 | 标 @IgnoreResponseWrapper |
验证命令
./mvnw -pl spring-open-starters/spring-open-starter-view -am test -DskipITs相关
- 启动后立刻可访问的页面:本文档
- WebSocket 实时通道:websocket.md
- 安装向导接管根路径:install.md
- 前后端分离主线方案:architecture.md
- Provider 扩展模型:provider.md