Appearance
Spring MVC Script Views:让前端模板在服务端飞起来 🚀
概述
在现代 Web 开发中,我们经常面临一个问题:如何在服务端使用流行的前端模板引擎? 比如你想在 Spring Boot 应用中使用 React、Handlebars 或 Mustache 模板,但这些模板引擎原本是为浏览器环境设计的。
Spring MVC 的 Script Views 功能就是为了解决这个痛点而生的!它让你能够在服务端运行各种基于 JavaScript 的模板引擎,实现真正的同构渲染。
TIP
同构渲染是指同一套模板代码既可以在服务端运行(用于 SEO 和首屏加载),也可以在客户端运行(用于交互和动态更新)。
核心原理:JSR-223 的魔法 ✨
Script Views 的核心原理基于 JSR-223 Java Scripting API。简单来说,它允许 Java 应用程序执行各种脚本语言的代码。
IMPORTANT
脚本引擎必须实现 ScriptEngine
和 Invocable
接口才能与 Spring 集成。
支持的模板引擎生态系统
Spring MVC Script Views 支持多种模板引擎和脚本引擎的组合:
模板库 | 脚本引擎 | 适用场景 |
---|---|---|
Handlebars | Nashorn | 逻辑简单的模板,语法清晰 |
Mustache | Nashorn | 无逻辑模板,适合简单展示 |
React | Nashorn | 组件化开发,同构渲染 |
EJS | Nashorn | 类似 JSP 的语法,易于上手 |
ERB | JRuby | Ruby 开发者的首选 |
String templates | Jython | Python 风格的模板 |
Kotlin Script | Kotlin | 类型安全的模板开发 |
环境准备
在开始使用之前,你需要准备相应的依赖:
kotlin
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
// Mustache 模板通过 WebJars 引入
implementation("org.webjars:mustache:4.2.0")
}
kotlin
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web")
// Kotlin 脚本支持
implementation("org.jetbrains.kotlin:kotlin-script-util")
}
WARNING
Java 8+ 自带 Nashorn JavaScript 引擎,但在 Java 15+ 中已被移除。如果使用较新版本的 Java,需要单独添加 Nashorn 依赖。
实战案例:Mustache 模板集成
让我们通过一个完整的例子来看看如何在 Spring Boot 中集成 Mustache 模板。
1. 配置 ScriptTemplateConfigurer
kotlin
@Configuration
@EnableWebMvc
class WebConfig : WebMvcConfigurer {
override fun configureViewResolvers(registry: ViewResolverRegistry) {
registry.scriptTemplate()
}
@Bean
fun scriptTemplateConfigurer() = ScriptTemplateConfigurer().apply {
engineName = "nashorn" // 指定脚本引擎
setScripts("mustache.js") // 加载模板库
renderObject = "Mustache" // 模板对象名称
renderFunction = "render" // 渲染函数名称
// 设置模板文件位置(可选)
resourceLoaderPath = "classpath:/templates/"
}
}
xml
<mvc:annotation-driven/>
<mvc:view-resolvers>
<mvc:script-template/>
</mvc:view-resolvers>
<mvc:script-template-configurer
engine-name="nashorn"
render-object="Mustache"
render-function="render">
<mvc:script location="mustache.js"/>
</mvc:script-template-configurer>
2. 创建控制器
kotlin
@RestController
class ProductController {
@GetMapping("/products/{id}")
fun getProduct(@PathVariable id: Long, model: Model): String {
// 模拟从数据库获取产品信息
val product = Product(
id = id,
name = "Spring Boot 实战指南",
price = 99.99,
description = "深入学习 Spring Boot 框架"
)
// 添加数据到模型中
model.addAttribute("product", product)
model.addAttribute("pageTitle", "产品详情")
// 返回模板名称(不需要扩展名)
return "product-detail"
}
}
data class Product(
val id: Long,
val name: String,
val price: Double,
val description: String
)
3. 创建 Mustache 模板
templates/product-detail.mustache
html
<!DOCTYPE html>
<html>
<head>
<title>{{pageTitle}}</title>
<meta charset="UTF-8">
<style>
.product-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
margin: 20px;
max-width: 500px;
}
.price {
color: #e74c3c;
font-size: 1.5em;
font-weight: bold;
}
</style>
</head>
<body>
<div class="product-card">
<h1>{{product.name}}</h1>
<p class="price">¥{{product.price}}</p>
<p>{{product.description}}</p>
<button onclick="addToCart({{product.id}})">
加入购物车
</button>
</div>
<script>
function addToCart(productId) {
alert('产品 ' + productId + ' 已加入购物车!');
}
</script>
</body>
</html>
高级用法:自定义渲染函数
对于一些复杂的模板引擎(如 Handlebars),你可能需要自定义渲染逻辑:
1. Handlebars 集成示例
kotlin
@Configuration
class HandlebarsConfig {
@Bean
fun scriptTemplateConfigurer() = ScriptTemplateConfigurer().apply {
engineName = "nashorn"
// 加载多个脚本文件
setScripts("polyfill.js", "handlebars.js", "render.js")
renderFunction = "render"
isSharedEngine = false // 非线程安全引擎需要设置为 false
}
}
2. 创建 polyfill.js
javascript
// polyfill.js - 为 Handlebars 提供浏览器环境模拟
var window = {};
3. 创建自定义渲染函数
javascript
// render.js - 自定义渲染逻辑
function render(template, model) {
// 编译模板(生产环境应该缓存编译结果)
var compiledTemplate = Handlebars.compile(template);
// 渲染模板
return compiledTemplate(model);
}
CAUTION
当使用非线程安全的脚本引擎时,必须将 sharedEngine
设置为 false
,这会为每个请求创建新的引擎实例,可能影响性能。
渲染函数参数详解
Spring 调用渲染函数时会传递三个参数:
javascript
function render(template, model, renderingContext) {
// template: 模板内容字符串
// model: 包含所有模型数据的 Map 对象
// renderingContext: 渲染上下文,包含应用上下文、区域设置等信息
console.log("模板内容:", template);
console.log("模型数据:", JSON.stringify(model));
console.log("应用上下文:", renderingContext.getApplicationContext());
// 执行实际的模板渲染
return Mustache.render(template, model);
}
性能优化建议
1. 模板缓存
kotlin
@Component
class TemplateCache {
private val compiledTemplates = ConcurrentHashMap<String, Any>()
fun getCompiledTemplate(templateName: String, compiler: (String) -> Any): Any {
return compiledTemplates.computeIfAbsent(templateName, compiler)
}
}
2. 脚本引擎池化
kotlin
@Configuration
class ScriptEngineConfig {
@Bean
fun scriptTemplateConfigurer() = ScriptTemplateConfigurer().apply {
engineName = "nashorn"
setScripts("mustache.js")
renderFunction = "render"
isSharedEngine = true // 启用引擎共享以提高性能
}
}
常见问题与解决方案
问题1:模板文件找不到
解决方案
确保模板文件放在正确的位置,默认是 src/main/resources/templates/
kotlin
@Bean
fun scriptTemplateConfigurer() = ScriptTemplateConfigurer().apply {
// 明确指定模板文件路径
resourceLoaderPath = "classpath:/templates/"
// 其他配置...
}
问题2:脚本引擎初始化失败
常见原因
- 缺少必要的依赖
- 脚本文件路径错误
- 脚本语法错误
kotlin
// 添加错误处理
@Bean
fun scriptTemplateConfigurer(): ScriptTemplateConfigurer {
return try {
ScriptTemplateConfigurer().apply {
engineName = "nashorn"
setScripts("mustache.js")
renderFunction = "render"
}
} catch (e: Exception) {
logger.error("脚本模板配置失败", e)
throw e
}
}
总结
Spring MVC Script Views 为我们提供了一个强大的工具来实现服务端模板渲染,特别适合以下场景:
✅ SEO 友好:服务端渲染确保搜索引擎能够正确索引内容
✅ 同构应用:同一套模板代码在服务端和客户端都能运行
✅ 性能优化:首屏渲染更快,用户体验更好
✅ 技术栈统一:前端开发者可以直接参与服务端模板开发
NOTE
虽然 Script Views 功能强大,但在选择时也要考虑性能影响。对于简单的服务端渲染需求,传统的 Thymeleaf 或 JSP 可能是更好的选择。
通过合理使用 Spring MVC Script Views,你可以构建出既现代又高效的 Web 应用程序! 🎉