Spring MVC 之视图技术
# Spring MVC 之视图技术
Spring MVC 中视图技术的使用是可插拔的。无论决定使用 Thymeleaf、Groovy 等模板引擎、JSP 还是其他技术,都可以通过配置来更改。
Spring MVC 的视图位于该应用程序的内部信任边界内。 视图可以访问应用程序上下文的所有 bean。 因此,不建议在模板可由外部源编辑的应用程序中使用 Spring MVC 的模板支持,因为这可能会产生安全隐患。
# Thymeleaf
Thymeleaf (opens new window) 是一个现代服务器端 Java 模板引擎,它强调自然的 HTML 模板,可以通过双击在浏览器中预览,而无需运行服务器,这对于 UI 模板的独立工作(例如,由设计师)非常有帮助。
Thymeleaf 与 Spring MVC 的集成由 Thymeleaf 项目管理。 配置涉及一些 bean 声明,例如 ServletContextTemplateResolver
、SpringTemplateEngine
和 ThymeleafViewResolver
。 有关详细信息,请参阅 Thymeleaf+Spring (opens new window)。
# FreeMarker
Apache FreeMarker (opens new window) 是一个模板引擎,用于生成从 HTML 到电子邮件等任何类型的文本内容。 Spring 框架内置了 Spring MVC 与 FreeMarker 模板结合使用的集成。
# 视图配置
以下示例显示了如何将 FreeMarker 配置为视图技术:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.freeMarker();
}
// Configure FreeMarker...
@Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("/WEB-INF/freemarker");
return configurer;
}
}
以下示例显示了如何在 XML 中配置相同的内容:
<mvc:annotation-driven/>
<mvc:view-resolvers>
<mvc:freemarker/>
</mvc:view-resolvers>
<!-- Configure FreeMarker... -->
<mvc:freemarker-configurer>
<mvc:template-loader-path location="/WEB-INF/freemarker"/>
</mvc:freemarker-configurer>
或者,您也可以声明 FreeMarkerConfigurer
以完全控制所有属性,如以下示例所示:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
</bean>
您的模板需要存储在前面示例中所示的 FreeMarkerConfigurer
指定的目录中。鉴于前面的配置,如果您的控制器返回视图名称 welcome
,解析器将查找 /WEB-INF/freemarker/welcome.ftl
模板。
# FreeMarker 配置
可以通过在 FreeMarkerConfigurer
上设置适当的 bean 属性,将 FreeMarker 'Settings' 和 'SharedVariables' 直接传递给 FreeMarker Configuration
对象(由 Spring 管理)。 freemarkerSettings
属性需要一个 java.util.Properties
对象,freemarkerVariables
属性需要一个 java.util.Map
。 以下示例显示了如何使用 FreeMarkerConfigurer
:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
<property name="freemarkerVariables">
<map>
<entry key="xml_escape" value-ref="fmXmlEscape"/>
</map>
</property>
</bean>
<bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/>
有关应用于 Configuration
对象的设置和变量的详细信息,请参阅 FreeMarker 文档。
# 表单处理
Spring 提供了一个用于 JSP 的标记库,其中包含一个 <spring:bind/>
元素。 此元素主要让表单显示来自表单支持对象的值,并显示来自 Web 或业务层中的“验证器”的验证失败的结果。 Spring 还支持 FreeMarker 中的相同功能,以及用于生成表单输入元素的额外便利宏。
# 绑定宏
在 FreeMarker 的 spring-webmvc.jar
文件中维护了一组标准宏,因此它们始终可用于适当配置的应用程序。
Spring 模板库中定义的一些宏被认为是内部的(私有的),但宏定义中不存在这样的范围,这使得所有宏对调用代码和用户模板都是可见的。以下部分仅关注您需要从模板中直接调用的宏。如果您想直接查看宏代码,该文件名为 spring.ftl
,位于 org.springframework.web.servlet.view.freemarker
包中。
# 简单绑定
在基于充当 Spring MVC 控制器表单视图的 FreeMarker 模板的 HTML 表单中,您可以使用类似于下一个示例的代码来绑定到字段值,并以类似于 JSP 等价物的方式为每个输入字段显示错误消息。以下示例显示了一个 personForm 视图:
<!-- FreeMarker macros have to be imported into a namespace.
We strongly recommend sticking to 'spring'. -->
<#import "/spring.ftl" as spring/>
<html>
...
<form action="" method="POST">
Name:
<@spring.bind "personForm.name"/>
<input type="text"
name="${spring.status.expression}"
value="${spring.status.value?html}"/><br />
<#list spring.status.errorMessages as error> <b>${error}</b> <br /> </#list>
<br />
...
<input type="submit" value="submit"/>
</form>
...
</html>
<@spring.bind>
需要一个 'path' 参数,它由命令对象的名称(它是 'command',除非您在控制器配置中更改它)组成,在您希望绑定的命令对象后跟一个句点和字段名称。 您还可以使用嵌套字段,例如 command.address.street
。 bind
宏采用 web.xml
中的 ServletContext
参数 defaultHtmlEscape
指定的默认 HTML 转义行为。
称为 <@spring.bindEscaped>
的宏的另一种形式采用第二个参数,该参数明确指定是否应在状态错误消息或值中使用 HTML 转义。 您可以根据需要将其设置为 true
或 false
。 附加的表单处理宏简化了 HTML 转义的使用,您应该尽可能使用这些宏。
# 输入宏
FreeMarker 的附加便利宏简化了绑定和表单生成(包括验证错误显示)。 永远不需要使用这些宏来生成表单输入字段,您可以将它们与简单的 HTML 混合搭配,或者直接调用我们之前强调的 Spring 绑定宏。
下表中的可用宏显示了 FreeMarker 模板 (FTL) 定义和每个采用的参数列表:
macro | FTL definition |
---|---|
message (output a string from a resource bundle based on the code parameter) | <@spring.message code/> |
messageText (output a string from a resource bundle based on the code parameter, falling back to the value of the default parameter) | <@spring.messageText code, text/> |
url (prefix a relative URL with the application’s context root) | <@spring.url relativeUrl/> |
formInput (standard input field for gathering user input) | <@spring.formInput path, attributes, fieldType/> |
formHiddenInput (hidden input field for submitting non-user input) | <@spring.formHiddenInput path, attributes/> |
formPasswordInput (standard input field for gathering passwords. Note that no value is ever populated in fields of this type.) | <@spring.formPasswordInput path, attributes/> |
formTextarea (large text field for gathering long, freeform text input) | <@spring.formTextarea path, attributes/> |
formSingleSelect (drop down box of options that let a single required value be selected) | <@spring.formSingleSelect path, options, attributes/> |
formMultiSelect (a list box of options that let the user select 0 or more values) | <@spring.formMultiSelect path, options, attributes/> |
formRadioButtons (a set of radio buttons that let a single selection be made from the available choices) | <@spring.formRadioButtons path, options separator, attributes/> |
formCheckboxes (a set of checkboxes that let 0 or more values be selected) | <@spring.formCheckboxes path, options, separator, attributes/> |
formCheckbox (a single checkbox) | <@spring.formCheckbox path, attributes/> |
showErrors (simplify display of validation errors for the bound field) | <@spring.showErrors separator, classOrStyle/> |
上述任何宏的参数具有一致的含义:
path
: 要绑定到的字段的名称(例如,“command.name”)options
: 可在输入字段中选择的所有可用值的Map
。映射的键表示从表单回传并绑定到命令对象的值。针对键存储的 map 对象是在表单上显示给用户的标签,可能与表单回传的相应值不同。通常,这样的地图由控制器提供作为参考数据。您可以使用任何Map
实现,具体取决于所需的行为。对于严格排序的映射,您可以使用带有合适的“比较器”的SortedMap
(例如TreeMap
),对于应按插入顺序返回值的任意映射,使用“LinkedHashMap”或“LinkedMap”公共收藏
。separator
: 在多个选项可用作离散元素(单选按钮或复选框)的情况下,用于分隔列表中每个选项的字符序列(例如<br>
)。attributes
: 要包含在 HTML 标记本身中的任意标记或文本的附加字符串。该字符串按字面意思由宏回显。例如,在textarea
字段中,您可以提供属性(例如“rows="5" cols="60"'),或者您可以传递样式信息,例如 'style="border:1px solid silver"'。classOrStyle
: 对于showErrors
宏,包装每个错误的span
元素使用的 CSS 类的名称。如果未提供任何信息(或值为空),错误将包含在<b></b>
标签中。
以下部分概述了宏的示例。
输入字段
formInput
宏采用 path
参数 (command.name
) 和一个额外的 attributes
参数(在接下来的示例中为空)。该宏与所有其他表单生成宏一起对路径参数执行隐式 Spring 绑定。绑定在新绑定发生之前一直有效,因此 showErrors
宏不需要再次传递路径参数——它对上次创建绑定的字段进行操作。
showErrors
宏接受一个分隔符参数(用于分隔给定字段上的多个错误的字符),还接受第二个参数——这次是类名或样式属性。请注意,FreeMarker 可以为 attributes 参数指定默认值。以下示例显示了如何使用 formInput
和 showErrors
宏:
<@spring.formInput "command.name"/>
<@spring.showErrors "<br>"/>
下一个示例显示表单片段的输出,生成名称字段并在表单提交后显示验证错误,该字段中没有任何值。验证通过 Spring 的验证框架进行。
生成的 HTML 类似于以下示例:
Name:
<input type="text" name="name" value="">
<br>
<b>required</b>
<br>
<br>
formTextarea
宏的工作方式与 formInput
宏相同,并且接受相同的参数列表。通常,第二个参数 (attributes
) 用于传递样式信息或 textarea
的 rows
和 cols
属性。
选中字段
您可以使用四个选择字段宏在 HTML 表单中生成常见的 UI 值选择输入:
formSingleSelect
formMultiSelect
formRadioButtons
formCheckboxes
四个宏中的每一个都接受一个“Map”选项,其中包含表单字段的值和与该值对应的标签。值和标签可以相同。
下一个例子是 FTL 中的单选按钮。表单支持对象为此字段指定默认值“伦敦”,因此无需验证。渲染表单时,整个可供选择的城市列表作为参考数据提供在模型中,名称为 cityMap
。以下清单显示了示例:
...
Town:
<@spring.formRadioButtons "command.address.town", cityMap, ""/><br><br>
前面的清单呈现一行单选按钮,一个用于 cityMap
中的每个值,并使用分隔符 ""
。没有提供额外的属性(缺少宏的最后一个参数)。 cityMap
对地图中的每个键值对使用相同的 String
。地图的键是表单实际作为 POST 请求参数提交的内容。地图值是用户看到的标签。在前面的示例中,给定三个知名城市的列表和表单支持对象中的默认值,HTML 类似于以下内容:
Town:
<input type="radio" name="address.town" value="London">London</input>
<input type="radio" name="address.town" value="Paris" checked="checked">Paris</input>
<input type="radio" name="address.town" value="New York">New York</input>
如果您的应用程序希望通过内部代码处理城市(例如),您可以使用合适的键创建代码映射,如以下示例所示:
protected Map<String, ?> referenceData(HttpServletRequest request) throws Exception {
Map<String, String> cityMap = new LinkedHashMap<>();
cityMap.put("LDN", "London");
cityMap.put("PRS", "Paris");
cityMap.put("NYC", "New York");
Map<String, Object> model = new HashMap<>();
model.put("cityMap", cityMap);
return model;
}
代码现在生成输出,其中无线电值是相关代码,但用户仍然看到更用户友好的城市名称,如下所示:
Town:
<input type="radio" name="address.town" value="LDN">London</input>
<input type="radio" name="address.town" value="PRS" checked="checked">Paris</input>
<input type="radio" name="address.town" value="NYC">New York</input>
# HTML 转义
前面描述的表单宏的默认使用导致 HTML 元素符合 HTML 4.01,并且使用 web.xml
文件中定义的 HTML 转义的默认值,如 Spring 的绑定支持所使用的那样。 要使元素符合 XHTML 或覆盖默认的 HTML 转义值,您可以在模板中指定两个变量(或在模型中,它们对模板可见)。 在模板中指定它们的好处是它们可以在稍后的模板处理中更改为不同的值,以便为表单中的不同字段提供不同的行为。
要为您的标签切换到 XHTML 合规性,请为名为 xhtmlCompliant
的模型或上下文变量指定 true
值,如以下示例所示:
<#-- for FreeMarker -->
<#assign xhtmlCompliant = true>
处理此指令后,Spring 宏生成的任何元素现在都符合 XHTML。
以类似的方式,您可以为每个字段指定 HTML 转义,如以下示例所示:
<#-- until this point, default HTML escaping is used -->
<#assign htmlEscape = true>
<#-- next field will use HTML escaping -->
<@spring.formInput "command.name"/>
<#assign htmlEscape = false in spring>
<#-- all future fields will be bound with HTML escaping off -->
# Groovy
Groovy 标记模板引擎 (opens new window) 主要用于生成类似 XML 的标记(XML、XHTML、HTML5 等),但可以使用它来生成任何基于文本的内容。 Spring Framework 具有将 Spring MVC 与 Groovy 标记结合使用的内置集成。
# 配置
以下示例显示如何配置 Groovy 标记模板引擎:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.groovy();
}
// Configure the Groovy Markup Template Engine...
@Bean
public GroovyMarkupConfigurer groovyMarkupConfigurer() {
GroovyMarkupConfigurer configurer = new GroovyMarkupConfigurer();
configurer.setResourceLoaderPath("/WEB-INF/");
return configurer;
}
}
以下示例显示了如何在 XML 中配置相同的内容:
<mvc:annotation-driven/>
<mvc:view-resolvers>
<mvc:groovy/>
</mvc:view-resolvers>
<!-- Configure the Groovy Markup Template Engine... -->
<mvc:groovy-configurer resource-loader-path="/WEB-INF/"/>
# 示例
与传统的模板引擎不同,Groovy 标记依赖于使用构建器语法的 DSL。以下示例显示了 HTML 页面的示例模板:
yieldUnescaped '<!DOCTYPE html>'
html(lang:'en') {
head {
meta('http-equiv':'"Content-Type" content="text/html; charset=utf-8"')
title('My page')
}
body {
p('This is an example of HTML contents')
}
}
# 脚本视图
Spring 有一个内置的集成,可以将 Spring MVC 与任何可以在 JSR-223 (opens new window) 之上运行的模板库一起使用 Java 脚本引擎。 我们在不同的脚本引擎上测试了以下模板库:
# 要求
需要在类路径中包含脚本引擎,具体细节因脚本引擎而异:
- The Nashorn (opens new window) Java 8+ 提供了 JavaScript 引擎。强烈建议使用可用的最新更新版本。
- JRuby (opens new window) 应该作为 Ruby 支持的依赖项添加。
- Jython (opens new window) 应该作为 Python 支持的依赖项添加。
org.jetbrains.kotlin:kotlin-script-util
依赖项和包含org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory
行的META-INF/services/javax.script.ScriptEngineFactory
文件应该被添加 Kotlin 脚本支持。 有关详细信息,请参阅此示例 (opens new window)。
您需要有脚本模板库。 为 JavaScript 做到这一点的一种方法是通过 WebJars (opens new window)。
# 脚本模板
可以声明一个 ScriptTemplateConfigurer
来指定要使用的脚本引擎、要加载的脚本文件、调用什么函数来渲染模板等等。 以下示例使用 Mustache 模板和 Nashorn JavaScript 引擎:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.scriptTemplate();
}
@Bean
public ScriptTemplateConfigurer configurer() {
ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
configurer.setEngineName("nashorn");
configurer.setScripts("mustache.js");
configurer.setRenderObject("Mustache");
configurer.setRenderFunction("render");
return configurer;
}
}
以下示例显示了 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>
对于 Java 和 XML 配置,controller 看起来没有什么不同,如以下示例所示:
@Controller
public class SampleController {
@GetMapping("/sample")
public String test(Model model) {
model.addAttribute("title", "Sample title");
model.addAttribute("body", "Sample body");
return "template";
}
}
以下示例显示了 Mustache 模板:
<html>
<head>
<title>{{title}}</title>
</head>
<body>
<p>{{body}}</p>
</body>
</html>
使用以下参数调用渲染函数:
String template
: 模板内容地图模型
:视图模型RenderingContext renderingContext
:RenderingContext
(opens new window) 允许访问应用上下文、语言环境、模板加载器和 URL(自 5.0 起)
如果您的模板技术需要一些自定义,您可以提供一个实现自定义渲染功能的脚本。 例如,Handlerbars (opens new window) 需要在使用之前编译模板,并且需要一个 polyfill (opens new window) 来模拟一些浏览器工具,但在服务器端脚本引擎中不可用。
以下示例显示了如何执行此操作:
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
registry.scriptTemplate();
}
@Bean
public ScriptTemplateConfigurer configurer() {
ScriptTemplateConfigurer configurer = new ScriptTemplateConfigurer();
configurer.setEngineName("nashorn");
configurer.setScripts("polyfill.js", "handlebars.js", "render.js");
configurer.setRenderFunction("render");
configurer.setSharedEngine(false);
return configurer;
}
}
polyfill.js
只定义了 Handlebars 正常运行所需的 window
对象,如下:
var window = {}
这个基本的 render.js
实现在使用之前编译模板。 生产就绪的实现还应该存储任何重复使用的缓存模板或预编译模板。 您可以在脚本端这样做(并处理您需要的任何定制——管理模板引擎配置,例如)。 以下示例显示了如何执行此操作:
function render(template, model) {
var compiledTemplate = Handlebars.compile(template)
return compiledTemplate(model)
}
查看 Spring Framework 单元测试,Java (opens new window) 和资源 (opens new window),以获取更多配置示例。
# JSP 和 JSTL
Spring Framework 具有将 Spring MVC 与 JSP 和 JSTL 结合使用的内置集成。
# RSS and Atom
AbstractAtomFeedView
和 AbstractRssFeedView
都继承自 AbstractFeedView
基类,分别用于提供 Atom 和 RSS Feed 视图。 它们基于 ROME (opens new window) 项目,位于 org.springframework.web.servlet.view.feed 包中。
AbstractAtomFeedView
要求您实现 buildFeedEntries()
方法并可选择覆盖 buildFeedMetadata()
方法(默认实现为空)。 以下示例显示了如何执行此操作:
public class SampleContentAtomView extends AbstractAtomFeedView {
@Override
protected void buildFeedMetadata(Map<String, Object> model,
Feed feed, HttpServletRequest request) {
// implementation omitted
}
@Override
protected List<Entry> buildFeedEntries(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response) throws Exception {
// implementation omitted
}
}
类似的要求适用于实现 AbstractRssFeedView
,如以下示例所示:
public class SampleContentRssView extends AbstractRssFeedView {
@Override
protected void buildFeedMetadata(Map<String, Object> model,
Channel feed, HttpServletRequest request) {
// implementation omitted
}
@Override
protected List<Item> buildFeedItems(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response) throws Exception {
// implementation omitted
}
}
buildFeedItems()
和 buildFeedEntries()
方法传入 HTTP 请求,以防您需要访问 Locale。 传入 HTTP 响应仅用于设置 cookie 或其他 HTTP 标头。 方法返回后,提要会自动写入响应对象。
有关创建 Atom 视图的示例,请参阅 Alef Arendsen 的 Spring Team 博客 entry (opens new window)。
# PDF and Excel
Spring 提供了返回 HTML 以外的输出的方法,包括 PDF 和 Excel 电子表格。
# 文档视图简介
HTML 页面并不总是用户查看模型输出的最佳方式,Spring 使从模型数据动态生成 PDF 文档或 Excel 电子表格变得简单。 该文档是视图,从服务器流出正确的内容类型,(希望)使客户端 PC 能够运行他们的电子表格或 PDF 查看器应用程序作为响应。
为了使用 Excel 视图,您需要将 Apache POI 库添加到类路径中。 对于 PDF 生成,您需要添加(最好)OpenPDF 库。
# PDF 视图
单词列表的简单 PDF 视图可以扩展 org.springframework.web.servlet.view.document.AbstractPdfView
并实现 buildPdfDocument()
方法,如以下示例所示:
public class PdfWordList extends AbstractPdfView {
protected void buildPdfDocument(Map<String, Object> model, Document doc, PdfWriter writer,
HttpServletRequest request, HttpServletResponse response) throws Exception {
List<String> words = (List<String>) model.get("wordList");
for (String word : words) {
doc.add(new Paragraph(word));
}
}
}
控制器可以从外部视图定义(按名称引用它)或作为处理程序方法的 View
实例返回此类视图。
# Excel 视图
从 Spring Framework 4.2 开始,org.springframework.web.servlet.view.document.AbstractXlsView
作为 Excel 视图的基类提供。 它基于 Apache POI,具有专门的子类(AbstractXlsxView
和 AbstractXlsxStreamingView
),取代了过时的 AbstractExcelView
类。
编程模型类似于 AbstractPdfView
,以 buildExcelDocument()
作为核心模板方法,控制器能够从外部定义(按名称)或作为处理程序方法的 View
实例返回此类视图。
# Jackson
Spring 提供对 Jackson JSON 库的支持。
# 基于 Jackson 的 JSON MVC 视图
MappingJackson2JsonView
使用 Jackson 库的 ObjectMapper
将响应内容渲染为 JSON。 默认情况下,模型映射的全部内容(特定于框架的类除外)都编码为 JSON。 对于需要过滤 map 内容的情况,您可以使用 modelKeys
属性指定一组特定的模型属性进行编码。 您还可以使用 extractValueFromSingleKeyModel
属性直接提取和序列化单键模型中的值,而不是作为模型属性的映射。
您可以根据需要使用 Jackson 提供的注释自定义 JSON 映射。 当您需要进一步控制时,您可以通过 ObjectMapper
属性注入自定义 ObjectMapper
,适用于需要为特定类型提供自定义 JSON 序列化器和反序列化器的情况。
# 基于 Jackson 的 XML 视图
MappingJackson2XmlView
使用 Jackson XML 扩展 (opens new window) XmlMapper
将响应内容渲染为 XML。 如果模型包含多个条目,您应该使用 modelKey
bean 属性显式设置要序列化的对象。 如果模型包含单个条目,它会自动序列化。
您可以根据需要使用 JAXB 或 Jackson 提供的注释自定义 XML 映射。当您需要进一步控制时,您可以通过 ObjectMapper
属性注入自定义 XmlMapper
,对于需要为特定类型提供序列化器和反序列化器的自定义 XML 的情况
# XML
MarshallingView
使用 XML Marshaller
(在 org.springframework.oxm
包中定义)将响应内容渲染为 XML。 您可以使用 MarshallingView
实例的 modelKey
属性显式设置要编组的对象。 或者,视图遍历所有模型属性并编组 Marshaller
支持的第一个类型。 有关 org.springframework.oxm
包中功能的更多信息,请参阅 Marshalling XML using O/X Mappers (opens new window)。
# XSLT
XSLT 是 XML 的一种转换语言,作为 Web 应用程序中的一种视图技术很受欢迎。 如果您的应用程序自然地处理 XML,或者如果您的模型可以很容易地转换为 XML,那么 XSLT 作为一种视图技术是一个不错的选择。 以下部分展示了如何生成 XML 文档作为模型数据,并在 Spring Web MVC 应用程序中使用 XSLT 对其进行转换。
此示例是一个简单的 Spring 应用程序,它在 Controller
中创建关键字列表并将它们添加到模型映射中。 返回映射以及我们的 XSLT 视图的视图名称。 有关 Spring Web MVC 的 Controller
接口的详细信息,请参阅 Annotated Controllers (opens new window)。 XSLT 控制器将单词列表转换为准备转换的简单 XML 文档。
# Beans
配置是一个简单的 Spring Web 应用程序的标准配置:MVC 配置必须定义一个 XsltViewResolver
和常规 MVC 注释配置。以下示例显示了如何执行此操作:
@EnableWebMvc
@ComponentScan
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public XsltViewResolver xsltViewResolver() {
XsltViewResolver viewResolver = new XsltViewResolver();
viewResolver.setPrefix("/WEB-INF/xsl/");
viewResolver.setSuffix(".xslt");
return viewResolver;
}
}
# Controller
我们还需要一个控制器来封装我们的单词生成逻辑。
控制器逻辑封装在一个 @Controller
类中,处理方法定义如下:
@Controller
public class XsltController {
@RequestMapping("/")
public String home(Model model) throws Exception {
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element root = document.createElement("wordList");
List<String> words = Arrays.asList("Hello", "Spring", "Framework");
for (String word : words) {
Element wordNode = document.createElement("word");
Text textNode = document.createTextNode(word);
wordNode.appendChild(textNode);
root.appendChild(wordNode);
}
model.addAttribute("wordList", root);
return "home";
}
}
到目前为止,我们只创建了一个 DOM 文档并将其添加到模型映射中。请注意,您还可以将 XML 文件作为 Resource
加载并使用它来代替自定义 DOM 文档。
有可用的软件包可以自动 'domify' 一个对象图,但是在 Spring 中,您可以完全灵活地以您选择的任何方式从您的模型创建 DOM。这可以防止 XML 的转换在模型数据的结构中发挥太大作用,这在使用工具管理 DOMification 过程时是一种危险。
# Transformation
最后,XsltViewResolver
解析 “home” XSLT 模板文件并将 DOM 文档合并到其中以生成我们的视图。如 XsltViewResolver
配置所示,XSLT 模板位于 WEB-INF/xsl
目录下的 war
文件中,并以 xslt
文件扩展名结尾。
以下示例显示了 XSLT 转换:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" omit-xml-declaration="yes"/>
<xsl:template match="/">
<html>
<head><title>Hello!</title></head>
<body>
<h1>My First Words</h1>
<ul>
<xsl:apply-templates/>
</ul>
</body>
</html>
</xsl:template>
<xsl:template match="word">
<li><xsl:value-of select="."/></li>
</xsl:template>
</xsl:stylesheet>
前面的转换渲染为以下 HTML:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Hello!</title>
</head>
<body>
<h1>My First Words</h1>
<ul>
<li>Hello</li>
<li>Spring</li>
<li>Framework</li>
</ul>
</body>
</html>