# 1.9 View技术

Spring MVC中视图技术的使用是可插入的，无论您决定使用Thymeleaf，Groovy标记模板，JSP还是其他技术，主要取决于配置更改。 本章介绍与Spring MVC集成的视图技术。 我们假设您已经熟悉View Resolution。

## 1.9.1 Thymeleaf

Thymeleaf是一种现代的服务器端Java模板引擎，它强调可以通过双击在浏览器中预览的自然HTML模板，这对于独立处理UI模板（例如，由设计人员）而无需使用非常有用。 正在运行的服务器。 如果要替换JSP，Thymeleaf提供了最广泛的功能集之一，以使这种过渡更加容易。 Thymeleaf是积极开发和维护的。 有关更完整的介绍，请参见Thymeleaf项目主页。

Thymeleaf与Spring MVC的集成由Thymeleaf项目管理。 该配置涉及一些Bean声明，例如ServletContextTemplateResolver，SpringTemplateEngine和ThymeleafViewResolver。 有关更多详细信息，请参见Thymeleaf + Spring。

## 1.9.2 FreeMarker

Apache FreeMarker是一个模板引擎，用于生成从HTML到电子邮件等的任何类型的文本输出。 Spring框架具有内置的集成，可以将Spring MVC与FreeMarker模板一起使用。

**View配置**

以下示例显示如何将FreeMarker配置为一种视图技术：

```java
@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中进行配置：

```markup
<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，以完全控制所有属性，如以下示例所示：

```markup
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
    <property name="templateLoaderPath" value="/WEB-INF/freemarker/"/>
</bean>
```

您的模板需要存储在上例所示的FreeMarkerConfigurer指定的目录中。 给定上述配置，如果您的控制器返回欢迎的视图名称，则解析器将查找/WEB-INF/freemarker/welcome.ftl模板。

**FreeMarker Configuration**

您可以通过在FreeMarkerConfigurer bean上设置适当的bean属性，将FreeMarker的“设置”和“ SharedVariables”直接传递给FreeMarker配置对象（由Spring管理）。 freemarkerSettings属性需要一个java.util.Properties对象，而freemarkerVariables属性需要一个java.util.Map。 以下示例显示了如何执行此操作：

```markup
<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或业务层中Validator的验证失败的结果。 Spring还支持FreeMarker中的相同功能，并带有用于生成表单输入元素本身的附加便利宏。

**绑定宏**

spring-webmvc.jar文件中为这两种语言维护了一组标准宏，因此它们始终可用于经过适当配置的应用程序。

Spring库中定义的某些宏被视为内部（私有）宏，但是在宏定义中不存在这种范围，使所有宏对调用代码和用户模板可见。以下各节仅关注您需要在模板中直接调用的宏。如果您希望直接查看宏代码，则该文件名为spring.ftl，位于org.springframework.web.servlet.view\.freemarker包中。

**简单绑定**

在充当Spring MVC控制器的表单视图的HTML表单（vm或ftl模板）中，您可以使用类似于下一个示例的代码绑定到字段值，并以类似于JSP的方式显示每个输入字段的错误消息。当量。以下示例显示了先前配置的personForm视图：

```markup
<!-- 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 "myModelObject.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'参数，该参数由命令对象的名称（除非您在FormController属性中进行了更改，否则为'command'）组成，后跟一个句点和字段的名称。您希望绑定到的命令对象。您还可以使用嵌套字段，例如command.address.street。 bind宏假定由web.xml中的ServletContext参数defaultHtmlEscape指定的默认HTML转义行为。

名为<@ spring.bindEscaped>的宏的可选形式带有第二个参数，并明确指定在状态错误消息或值中应使用HTML转义。您可以根据需要将其设置为true或false。附加的表单处理宏可简化HTML转义的使用，并且您应尽可能使用这些宏。下一节将对它们进行说明。

**输入宏**

两种语言的附加便利宏均简化了绑定和表单生成（包括验证错误显示）。从来没有必要使用这些宏来生成表单输入字段，并且您可以将它们与简单的HTML混合或匹配，或者直接调用我们之前强调的spring绑定宏。

下表可用的宏显示了每个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/>                   |

* 在FTL（FreeMarker）中，实际上并不需要formHiddenInput和formPasswordInput，因为您可以使用常规的formInput宏，将hidden或password指定为fieldType参数的值。

以上任何宏的参数都具有一致的含义：

* path：要绑定的字段名称（即“ command.name”）
* options：可以在输入字段中选择的所有可用值的映射。映射的键表示从表单回发并绑定到命令对象的值。针对键存储的地图对象是在表单上显示给用户的标签，并且可能与表单回发的相应值不同。通常，这种地图由控制器作为参考数据提供。您可以使用任何Map实现，具体取决于所需的行为。对于严格排序的地图，可以将SortedMap（例如TreeMap）与合适的Comparator一起使用；对于应按插入顺序返回值的任意地图，请使用Commons-collection中的LinkedHashMap或LinkedMap。
* 分隔符：当多个选项可用作离散元素（单选按钮或复选框）时，用于分隔列表中的每个字符的字符序列（例如\<br>）。
* 属性：HTML标记本身包含的任意标记或文本的附加字符串。该字符串实际上是由宏回显的。例如，在textarea字段中，您可以提供属性（例如'rows =“ 5” cols =“ 60”'），也可以传递样式信息，例如'style =“ border：1px solid silver”。
* classOrStyle：对于showErrors宏，包装每个错误的span元素使用的CSS类的名称。如果未提供任何信息（或该值为空），则错误将包装在\<b> \</ b>标记中。

以下各节概述了宏的示例（某些在FTL中，有些在VTL中）。两种语言之间存在用法差异的地方，在注解中进行了说明。

**输入栏位** formInput宏带有path参数（command.name）和一个附加的attribute参数（在后面的示例中为空）。该宏与所有其他表单生成宏一起，对path参数执行隐式Spring绑定。绑定保持有效，直到发生新的绑定为止，因此showErrors宏无需再次传递path参数。它在最后创建绑定的字段上进行操作。

showErrors宏采用分隔符参数（用于分隔给定字段上的多个错误的字符），并且还接受第二个参数-这次是类名或样式属性。注意，FreeMarker可以为attributes参数指定默认值。下面的示例演示如何使用formInput和showWErrors宏：

```markup
<@spring.formInput "command.name"/>
<@spring.showErrors "<br>"/>
```

下一个示例显示表单片段的输出，生成名称字段，并在提交表单后在字段中没有值的情况下显示验证错误。 验证通过Spring的Validation框架进行。

生成的HTML类似于以下示例：

```markup
Name:
<input type="text" name="name" value="">
<br>
    <b>required</b>
<br>
<br>
```

formTextarea宏的工作方式与formInput宏相同，并且接受相同的参数列表。 通常，第二个参数（属性）用于传递样式信息或文本区域的行和列属性。

**选择字段**

您可以使用四个选择字段宏在HTML表单中生成常见的UI值选择输入：

* formSingleSelect
* formMultiSelect
* formRadioButtons
* formCheckboxes

四个宏中的每个宏都接受一个选项映射，其中包含表单字段的值和与该值相对应的标签。 值和标签可以相同。

下一个示例是FTL中的单选按钮。 表单支持对象为此字段指定默认值“London”，因此无需验证。 呈现表单时，将在模型中以“ cityMap”为名称提供可供选择的整个城市列表作为参考数据。 以下清单显示了示例：

```
...
Town:
<@spring.formRadioButtons "command.address.town", cityMap, ""/><br><br>
```

上面的清单呈现了一行单选按钮，每个单选按钮用于cityMap中的每个值，并使用分隔符“”。 没有提供其他属性（缺少该宏的最后一个参数）。 cityMap对地图中的每个键值对使用相同的String。 地图的键是表单实际作为POSTed请求参数提交的键。 映射值是用户看到的标签。 在前面的示例中，给定三个知名城市的列表以及表单支持对象中的默认值，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>
```

如果您的应用程序希望通过内部代码处理城市（例如），则可以使用合适的键创建代码地图，如以下示例所示：

```java
protected Map<String, 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, String> 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 Escaping**

前面介绍的表单宏的默认用法将产生符合HTML 4.01的HTML元素，并使用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 -->
```

## 1.9.3 Groovy Markup

Groovy标记模板引擎主要旨在生成类似XML的标记（XML，XHTML，HTML5等），但是您可以使用它来生成任何基于文本的内容。 Spring框架具有内置的集成，可以将Spring MVC与Groovy标记一起使用。

> Groovy标记模板引擎需要Groovy 2.3.1+。

**Configuration**

以下示例显示了如何配置Groovy标记模板引擎：

```java
@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中进行配置：

```markup
<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/"/>
```

**Example**

与传统的模板引擎不同，Groovy标记依赖于使用构建器语法的DSL。 以下示例显示了HTML页面的示例模板：

```groovy
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')
    }
}
```

## 1.9.4 Script Views

Spring框架具有内置的集成，可以将Spring MVC与可以在JSR-223 Java脚本引擎之上运行的任何模板库一起使用。 我们已经在不同的脚本引擎上测试了以下模板库：

| Scripting Library        | Scripting Engine |
| ------------------------ | ---------------- |
| Handlebars               | Nashorn          |
| Mustache                 | Nashorn          |
| React                    | Nashorn          |
| EJS                      | Nashorn          |
| ERB                      | JRuby            |
| String templates         | Jython           |
| Kotlin Script templating | Kotlin           |

> 集成任何其他脚本引擎的基本规则是，它必须实现ScriptEngine和Invocable接口。

**Requirements**

您需要在类路径上具有脚本引擎，其细节因脚本引擎而异：

* Java 8+随附了Nashorn JavaScript引擎。 强烈建议使用可用的最新更新版本。
* 应该将JRuby添加为对Ruby支持的依赖。
* 应该将Jython添加为对Python支持的依赖项。
* 应该添加org.jetbrains.kotlin：kotlin-script-util依赖项和包含org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngineFactory行的META-INF / services / javax.script.ScriptEngineFactory文件。 有关更多详细信息，请参见此示例。

您需要具有脚本模板库。 针对Javascript的一种方法是通过WebJars。

**Script Templates**

您可以声明一个ScriptTemplateConfigurer bean来指定要使用的脚本引擎，要加载的脚本文件，调用呈现模板的函数等等。 以下示例使用Mustache模板和Nashorn JavaScript引擎：

```java
@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中的相同排列：

```markup
<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配置，该控制器看起来没有什么不同，如以下示例所示：

```java
@Controller
public class SampleController {

    @GetMapping("/sample")
    public String test(Model model) {
        model.addObject("title", "Sample title");
        model.addObject("body", "Sample body");
        return "template";
    }
}
```

以下示例显示了Mustache模板：

```markup
<html>
    <head>
        <title>{{title}}</title>
    </head>
    <body>
        <p>{{body}}</p>
    </body>
</html>
```

使用以下参数调用render函数：

* String template：模板内容
* Map model：视图模型
* RenderingContext renderingContext：RenderingContext，用于访问应用程序上下文，语言环境，模板加载器和URL（自5.0起）

Mustache.render（）与该签名本地兼容，因此您可以直接调用它。

如果您的模板技术需要一些自定义，则可以提供一个实现自定义渲染功能的脚本。 例如，Handlerbars需要在使用模板之前先对其进行编译，并且需要使用polyfill来模拟某些服务器端脚本引擎中不可用的浏览器功能。

以下示例显示了如何执行此操作：

```java
@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;
    }
}
```

当您将非线程安全脚本引擎与不是为并发设计的模板库一起使用时，例如，在Nashorn上运行的Handlebars或React，必须将sharedEngine属性设置为false。 在这种情况下，由于此错误，需要Java 8u60或更高版本。

polyfill.js仅定义Handlebars正常运行所需的window对象，如下所示：

```javascript
var window = {};
```

这个基本的render.js实现在使用模板之前先对其进行编译。 生产就绪的实现还应该存储任何重用的缓存模板或预编译的模板。 您可以在脚本方面进行操作（并处理所需的任何自定义，例如管理模板引擎配置）。 以下示例显示了如何执行此操作：

```javascript
function render(template, model) {
    var compiledTemplate = Handlebars.compile(template);
    return compiledTemplate(model);
}
```

## 1.9.5 JSP and JSTL

Spring框架具有内置的集成，可以将Spring MVC与JSP和JSTL一起使用。

**View Resolvers**

使用JSP开发时，可以声明一个InternalResourceViewResolver或ResourceBundleViewResolver bean。

ResourceBundleViewResolver依靠属性文件来定义映射到类和URL的视图名称。 使用ResourceBundleViewResolver，您可以仅使用一个解析器来混合不同类型的视图，如以下示例所示：

```markup
<!-- the ResourceBundleViewResolver -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
    <property name="basename" value="views"/>
</bean>

# And a sample properties file is used (views.properties in WEB-INF/classes):
welcome.(class)=org.springframework.web.servlet.view.JstlView
welcome.url=/WEB-INF/jsp/welcome.jsp

productList.(class)=org.springframework.web.servlet.view.JstlView
productList.url=/WEB-INF/jsp/productlist.jsp
```

InternalResourceViewResolver也可以用于JSP。 作为最佳实践，我们强烈建议您将JSP文件放在“ WEB-INF”目录下的目录中，以便客户端无法直接访问。

```markup
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>
```

**JSP与JSTL**

使用JSP标准标记库（JSTL）时，必须使用特殊的视图类JstlView，因为JSTL需要一些准备工作，然后I18N功能才能正常工作。

**Spring的JSP标签库**

如前几章所述，Spring提供了请求参数到命令对象的数据绑定。为了促进结合这些数据绑定功能的JSP页面的开发，Spring提供了一些使事情变得更加容易的标记。所有Spring标记都具有HTML转义功能，以启用或禁用字符转义。

spring-webmvc.jar中包含spring.tld标记库描述符（TLD）。有关单个标签的全面参考，请浏览API参考或查看标签库说明。

**Spring的表单标签库**

从2.0版开始，Spring使用JSP和Spring Web MVC时，提供了一组全面的数据绑定感知标签，用于处理表单元素。每个标签都支持其对应的HTML标签对应项的属性集，从而使标签变得熟悉且易于使用。标记生成的HTML符合HTML 4.01 / XHTML 1.0。

与其他表单/输入标签库不同，Spring的表单标签库与Spring Web MVC集成在一起，使标签可以访问命令对象和控制器处理的参考数据。正如我们在以下示例中所示，表单标签使JSP易于开发，读取和维护。

我们浏览一下表单标签，并查看一个如何使用每个标签的示例。我们包含了生成的HTML代码段，其中某些标记需要进一步的注解。

**Configuration**

表单标签库捆绑在spring-webmvc.jar中。库描述符称为spring-form.tld。

要使用此库中的标记，请在JSP页面顶部添加以下指令：

```
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
```

**表单标签**

该标签呈现HTML'form'元素，并向内部标签公开绑定路径以进行绑定。 它将命令对象放在PageContext中，以便内部标签可以访问该命令对象。 该库中的所有其他标签都是form标签的嵌套标签。

假设我们有一个名为User的域对象。 它是一个具有firstName和lastName之类的属性的JavaBean。 我们可以将其用作表单控制器的表单支持对象，该表单控制器返回form.jsp。 以下示例显示了form.jsp的外观：

```markup
<form:form>
    <table>
        <tr>
            <td>First Name:</td>
            <td><form:input path="firstName"/></td>
        </tr>
        <tr>
            <td>Last Name:</td>
            <td><form:input path="lastName"/></td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="submit" value="Save Changes"/>
            </td>
        </tr>
    </table>
</form:form>
```

页面控制器从放置在PageContext中的命令对象中检索firstName和lastName值。 继续阅读以了解如何将内部标签与表单标签一起使用的更复杂的示例。

下面的清单显示了生成的HTML，它看起来像标准格式：

```markup
<form method="POST">
    <table>
        <tr>
            <td>First Name:</td>
            <td><input name="firstName" type="text" value="Harry"/></td>
        </tr>
        <tr>
            <td>Last Name:</td>
            <td><input name="lastName" type="text" value="Potter"/></td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="submit" value="Save Changes"/>
            </td>
        </tr>
    </table>
</form>
```

前面的JSP假定表单支持对象的变量名是command。 如果已将表单支持对象以另一个名称（肯定是最佳实践）放入模型中，则可以将表单绑定到命名变量，如以下示例所示：

```markup
<form:form modelAttribute="user">
    <table>
        <tr>
            <td>First Name:</td>
            <td><form:input path="firstName"/></td>
        </tr>
        <tr>
            <td>Last Name:</td>
            <td><form:input path="lastName"/></td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="submit" value="Save Changes"/>
            </td>
        </tr>
    </table>
</form:form>
```

**input标签**

默认情况下，此标记呈现具有绑定值和type ='text'的HTML输入元素。 有关此标签的示例，请参见表单标签。 您还可以使用HTML5特定类型，例如电子邮件，电话，日期和其他。

**checkbox标签**

此标记呈现类型设置为复选框的HTML输入标记。

假设我们的用户有喜好，例如通讯订阅和兴趣爱好列表。 以下示例显示了Preferences类：

```java
public class Preferences {

    private boolean receiveNewsletter;
    private String[] interests;
    private String favouriteWord;

    public boolean isReceiveNewsletter() {
        return receiveNewsletter;
    }

    public void setReceiveNewsletter(boolean receiveNewsletter) {
        this.receiveNewsletter = receiveNewsletter;
    }

    public String[] getInterests() {
        return interests;
    }

    public void setInterests(String[] interests) {
        this.interests = interests;
    }

    public String getFavouriteWord() {
        return favouriteWord;
    }

    public void setFavouriteWord(String favouriteWord) {
        this.favouriteWord = favouriteWord;
    }
}
```

相应的form.jsp可能类似于以下内容：

```markup
<form:form>
    <table>
        <tr>
            <td>Subscribe to newsletter?:</td>
            <%-- Approach 1: Property is of type java.lang.Boolean --%>
            <td><form:checkbox path="preferences.receiveNewsletter"/></td>
        </tr>

        <tr>
            <td>Interests:</td>
            <%-- Approach 2: Property is of an array or of type java.util.Collection --%>
            <td>
                Quidditch: <form:checkbox path="preferences.interests" value="Quidditch"/>
                Herbology: <form:checkbox path="preferences.interests" value="Herbology"/>
                Defence Against the Dark Arts: <form:checkbox path="preferences.interests" value="Defence Against the Dark Arts"/>
            </td>
        </tr>

        <tr>
            <td>Favourite Word:</td>
            <%-- Approach 3: Property is of type java.lang.Object --%>
            <td>
                Magic: <form:checkbox path="preferences.favouriteWord" value="Magic"/>
            </td>
        </tr>
    </table>
</form:form>
```

复选框标记有三种方法，它们可以满足您所有的复选框需求。

* 方法一：当绑定值为java.lang.Boolean类型时，如果绑定值为true，则将input（checkbox）标记为已选中。 value属性对应于setValue（Object）value属性的解析值。
* 方法二：当绑定值的类型为array或java.util.Collection时，如果绑定的Collection中存在已配置的setValue（Object）值，则将input（checkbox）标记为已选中。
* 方法三：对于任何其他绑定值类型，如果已配置的setValue（Object）等于绑定值，则将input（复选框）标记为已选中。

请注意，无论采用哪种方法，都会生成相同的HTML结构。 以下HTML代码段定义了一些复选框：

```markup
<tr>
    <td>Interests:</td>
    <td>
        Quidditch: <input name="preferences.interests" type="checkbox" value="Quidditch"/>
        <input type="hidden" value="1" name="_preferences.interests"/>
        Herbology: <input name="preferences.interests" type="checkbox" value="Herbology"/>
        <input type="hidden" value="1" name="_preferences.interests"/>
        Defence Against the Dark Arts: <input name="preferences.interests" type="checkbox" value="Defence Against the Dark Arts"/>
        <input type="hidden" value="1" name="_preferences.interests"/>
    </td>
</tr>
```

您可能不希望在每个复选框之后看到其他隐藏字段。如果未选中HTML页面中的复选框，则提交表单后，其值就不会作为HTTP请求参数的一部分发送到服务器，因此我们需要一种解决方法来使HTML中的这个问题生效，以使Spring表单数据绑定生效。 checkbox标记遵循现有的Spring约定，其中包括每个复选框均带有下划线（\_）前缀的隐藏参数。通过这样做，您可以有效地告诉Spring“复选框在表单中可见，并且我希望表单数据绑定到我的对象以反映复选框的状态，无论如何。”

**checkboxes标签**

该标签呈现类型设置为复选框的多个HTML输入标签。

本节以前面的复选框标签节中的示例为基础。有时，您希望不必在JSP页面中列出所有可能的爱好。您宁愿在运行时提供可用选项的列表，然后将其传递给标记。这就是复选框标签的目的。您可以传入包含items属性中可用选项的数组，列表或映射。通常，bound属性是一个集合，因此它可以保存用户选择的多个值。以下示例显示了使用此标记的JSP：

```markup
<form:form>
    <table>
        <tr>
            <td>Interests:</td>
            <td>
                <%-- Property is of an array or of type java.util.Collection --%>
                <form:checkboxes path="preferences.interests" items="${interestList}"/>
            </td>
        </tr>
    </table>
</form:form>
```

本示例假定interestList是一个可用作模型属性的列表，其中包含要从中选择的值的字符串。 如果您使用的是地图，则将地图输入键用作值，并将地图输入的值用作要显示的标签。 您还可以使用自定义对象，在其中您可以通过使用itemValue提供值的属性名称，并通过使用itemLabel提供标签。

**radiobutton标签**

此标记呈现类型设置为radio的HTML input元素。

典型的用法模式涉及绑定到相同属性但值不同的多个标记实例，如以下示例所示：

```markup
<tr>
    <td>Sex:</td>
    <td>
        Male: <form:radiobutton path="sex" value="M"/> <br/>
        Female: <form:radiobutton path="sex" value="F"/>
    </td>
</tr>
```

**radiobuttons tag**

此标记呈现类型设置为radio的多个HTML输入元素。

与checkboxes标记一样，您可能希望将可用选项作为运行时变量传递。 对于此用法，您可以使用单选按钮标签。 您传入包含items属性中可用选项的数组，列表或映射。 如果您使用地图，则将地图输入键用作值，并将地图输入的值用作要显示的标签。 您还可以使用一个自定义对象，在其中您可以通过使用itemValue提供值的属性名称，并通过使用itemLabel提供标签，如以下示例所示：

```markup
<tr>
    <td>Sex:</td>
    <td><form:radiobuttons path="sex" items="${sexOptions}"/></td>
</tr>
```

**password tag**

该标签呈现HTML输入标签，其类型设置为具有绑定值的password。

```markup
<tr>
    <td>Password:</td>
    <td>
        <form:password path="password"/>
    </td>
</tr>
```

请注意，默认情况下，不显示密码值。 如果确实希望显示密码值，则可以将showPassword属性的值设置为true，如以下示例所示：

```markup
<tr>
    <td>Password:</td>
    <td>
        <form:password path="password" value="^76525bvHGq" showPassword="true"/>
    </td>
</tr>
```

**select标签**

此标记呈现HTML“ select”元素。 它支持将数据绑定到所选选项以及使用嵌套选项和选项标签。

假定用户具有技能列表。 相应的HTML可能如下所示：

```markup
<tr>
    <td>Skills:</td>
    <td><form:select path="skills" items="${skills}"/></td>
</tr>
```

如果用户的技能是Herbology，则“技能”行的HTML源可能如下：

```markup
<tr>
    <td>Skills:</td>
    <td>
        <select name="skills" multiple="true">
            <option value="Potions">Potions</option>
            <option value="Herbology" selected="selected">Herbology</option>
            <option value="Quidditch">Quidditch</option>
        </select>
    </td>
</tr>
```

**option tag**

此标记呈现HTML选项元素。 根据边界值设置为选中状态。 以下HTML显示了其典型输出：

```markup
<tr>
    <td>House:</td>
    <td>
        <form:select path="house">
            <form:option value="Gryffindor"/>
            <form:option value="Hufflepuff"/>
            <form:option value="Ravenclaw"/>
            <form:option value="Slytherin"/>
        </form:select>
    </td>
</tr>
```

如果用户的房屋位于Gryffindor，则“House”行的HTML源代码如下：

```markup
<tr>
    <td>House:</td>
    <td>
        <select name="house">
            <option value="Gryffindor" selected="selected">Gryffindor</option> 
            <option value="Hufflepuff">Hufflepuff</option>
            <option value="Ravenclaw">Ravenclaw</option>
            <option value="Slytherin">Slytherin</option>
        </select>
    </td>
</tr>
```

**options tag**

此标记呈现HTML选项元素的列表。 它基于绑定值设置选定的属性。 以下HTML显示了其典型输出：

```markup
<tr>
    <td>Country:</td>
    <td>
        <form:select path="country">
            <form:option value="-" label="--Please Select"/>
            <form:options items="${countryList}" itemValue="code" itemLabel="name"/>
        </form:select>
    </td>
</tr>
```

如果用户居住在英国，则“国家/地区”行的HTML来源如下：

```markup
<tr>
    <td>Country:</td>
    <td>
        <select name="country">
            <option value="-">--Please Select</option>
            <option value="AT">Austria</option>
            <option value="UK" selected="selected">United Kingdom</option> 
            <option value="US">United States</option>
        </select>
    </td>
</tr>
```

如前面的示例所示，选项标签与选项标签的组合使用会生成相同的标准HTML，但可以让您在JSP中显式指定一个仅用于显示（它所属的位置）的值，例如 例如：“-请选择”。

items属性通常填充有item对象的集合或数组。 如果指定了itemValue和itemLabel，则引用这些item对象的bean属性。 否则，项目对象本身将变成字符串。 或者，您可以指定一个项目映射，在这种情况下，映射键将解释为选项值，并且映射值对应于选项标签。 如果同时也指定了itemValue或itemLabel（或两者），则item value属性应用于映射键，item label属性应用于映射值。

**textarea标签**

此标记呈现HTML textarea元素。 以下HTML显示了其典型输出：

```markup
<tr>
    <td>Notes:</td>
    <td><form:textarea path="notes" rows="3" cols="20"/></td>
    <td><form:errors path="notes"/></td>
</tr>
```

**hidden tag**

该标签呈现一个HTML输入标签，其类型设置为使用绑定值隐藏。 要提交未绑定的隐藏值，请使用HTML输入标签，其类型设置为hidden。 以下HTML显示了其典型输出：

```markup
<form:hidden path="house"/>
```

如果我们选择将house value作为隐藏的value提交，则HTML如下所示：

```markup
<input name="house" type="hidden" value="Gryffindor"/>
```

**error tag**

此标记在HTML span元素中呈现字段错误。 它提供对在控制器中创建的错误或由与控制器关联的任何验证程序创建的错误的访问。

假设一旦提交表单，我们想显示firstName和lastName字段的所有错误消息。 我们有一个名为UserValidator的User类实例的验证器，如以下示例所示：

```java
public class UserValidator implements Validator {

    public boolean supports(Class candidate) {
        return User.class.isAssignableFrom(candidate);
    }

    public void validate(Object obj, Errors errors) {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "required", "Field is required.");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "required", "Field is required.");
    }
}
```

form.jsp如下所示：

```markup
<form:form>
    <table>
        <tr>
            <td>First Name:</td>
            <td><form:input path="firstName"/></td>
            <%-- Show errors for firstName field --%>
            <td><form:errors path="firstName"/></td>
        </tr>

        <tr>
            <td>Last Name:</td>
            <td><form:input path="lastName"/></td>
            <%-- Show errors for lastName field --%>
            <td><form:errors path="lastName"/></td>
        </tr>
        <tr>
            <td colspan="3">
                <input type="submit" value="Save Changes"/>
            </td>
        </tr>
    </table>
</form:form>
```

如果我们在firstName和lastName字段中提交具有空值的表单，则HTML将如下所示：

```markup
<form method="POST">
    <table>
        <tr>
            <td>First Name:</td>
            <td><input name="firstName" type="text" value=""/></td>
            <%-- Associated errors to firstName field displayed --%>
            <td><span name="firstName.errors">Field is required.</span></td>
        </tr>

        <tr>
            <td>Last Name:</td>
            <td><input name="lastName" type="text" value=""/></td>
            <%-- Associated errors to lastName field displayed --%>
            <td><span name="lastName.errors">Field is required.</span></td>
        </tr>
        <tr>
            <td colspan="3">
                <input type="submit" value="Save Changes"/>
            </td>
        </tr>
    </table>
</form>
```

如果我们要显示给定页面的整个错误列表怎么办？ 下一个示例显示errors标签还支持一些基本的通配符功能。

* path =“ \*”：显示所有错误。
* path =“ lastName”：显示与lastName字段相关的所有错误。
* 如果省略路径，则仅显示对象错误。

以下示例在页面顶部显示错误列表，然后在字段旁边显示特定于字段的错误：

```markup
<form:form>
    <form:errors path="*" cssClass="errorBox"/>
    <table>
        <tr>
            <td>First Name:</td>
            <td><form:input path="firstName"/></td>
            <td><form:errors path="firstName"/></td>
        </tr>
        <tr>
            <td>Last Name:</td>
            <td><form:input path="lastName"/></td>
            <td><form:errors path="lastName"/></td>
        </tr>
        <tr>
            <td colspan="3">
                <input type="submit" value="Save Changes"/>
            </td>
        </tr>
    </table>
</form:form>
```

HTML 如下所示：

```markup
<form method="POST">
    <span name="*.errors" class="errorBox">Field is required.<br/>Field is required.</span>
    <table>
        <tr>
            <td>First Name:</td>
            <td><input name="firstName" type="text" value=""/></td>
            <td><span name="firstName.errors">Field is required.</span></td>
        </tr>

        <tr>
            <td>Last Name:</td>
            <td><input name="lastName" type="text" value=""/></td>
            <td><span name="lastName.errors">Field is required.</span></td>
        </tr>
        <tr>
            <td colspan="3">
                <input type="submit" value="Save Changes"/>
            </td>
        </tr>
    </table>
</form>
```

spring-webmvc.jar中包含spring-form.tld标记库描述符（TLD）。有关单个标签的全面参考，请浏览API参考或查看标签库说明。

**HTTP方法转换**

REST的一个关键原则是使用“统一接口”。这意味着可以使用相同的四种HTTP方法（GET，PUT，POST和DELETE）来操纵所有资源（URL）。对于每种方法，HTTP规范都定义了确切的语义。例如，GET应该始终是安全的操作，这意味着它没有副作用，而PUT或DELETE应该是幂等的，这意味着您可以一遍又一遍地重复这些操作，但是最终结果应该相同。虽然HTTP定义了这四种方法，但HTML仅支持两种：GET和POST。幸运的是，有两种可能的解决方法：您可以使用JavaScript进行PUT或DELETE，或者可以使用“ real”方法作为附加参数（在HTML表单中建模为隐藏的输入字段）进行POST。 Spring的HiddenHttpMethodFilter使用了后一种技巧。该过滤器是一个普通的Servlet过滤器，因此，它可以与任何Web框架（不仅仅是Spring MVC）结合使用。将此过滤器添加到web.xml，然后将带有隐藏方法参数的POST转换为相应的HTTP方法请求。

为了支持HTTP方法转换，Spring MVC表单标签已更新为支持设置HTTP方法。例如，以下代码片段来自“宠物诊所”样本：

```markup
<form:form method="delete">
    <p class="submit"><input type="submit" value="Delete Pet"/></p>
</form:form>
```

前面的示例执行HTTP POST，并将“真实” DELETE方法隐藏在请求参数后面。 它由在web.xml中定义的HiddenHttpMethodFilter拾取，如以下示例所示：

```markup
<filter>
    <filter-name>httpMethodFilter</filter-name>
    <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>httpMethodFilter</filter-name>
    <servlet-name>petclinic</servlet-name>
</filter-mapping>
```

以下示例显示了相应的@Controller方法：

```java
@RequestMapping(method = RequestMethod.DELETE)
public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
    this.clinic.deletePet(petId);
    return "redirect:/owners/" + ownerId;
}
```

**HTML5标签**

Spring表单标签库允许输入动态属性，这意味着您可以输入任何HTML5特定的属性。

表单输入标签支持输入文本以外的类型属性。 这旨在允许呈现新的HTML5特定输入类型，例如电子邮件，日期，范围等。 请注意，由于默认类型为text，因此不需要输入type ='text'。

## 1.9.6 Tiles

您可以像使用其他视图技术一样，将Tiles集成到使用Spring的Web应用程序中。 本节以广泛的方式描述如何执行此操作。

> 本节重点介绍org.springframework.web.servlet.view\.tiles3软件包中Spring对Tiles版本3的支持。

**依存关系**

为了能够使用Tiles，您必须在Tiles 3.0.1或更高版本上添加一个依赖项，并将其传递依赖项添加到您的项目中。

**Configuration**

为了能够使用Tiles，必须使用包含定义的文件对其进行配置（有关定义和其他Tiles概念的基本信息，请参见[https://tiles.apache.org）。](https://tiles.apache.org\)) 在春季，这是通过使用TilesConfigurer完成的。 以下示例ApplicationContext配置说明了如何执行此操作：

```markup
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
    <property name="definitions">
        <list>
            <value>/WEB-INF/defs/general.xml</value>
            <value>/WEB-INF/defs/widgets.xml</value>
            <value>/WEB-INF/defs/administrator.xml</value>
            <value>/WEB-INF/defs/customer.xml</value>
            <value>/WEB-INF/defs/templates.xml</value>
        </list>
    </property>
</bean>
```

前面的示例定义了五个包含定义的文件。 这些文件都位于WEB-INF / defs目录中。 在WebApplicationContext初始化时，将加载文件，并初始化定义工厂。 完成之后，定义文件中包含的Tiles可以用作Spring Web应用程序中的视图。 为了能够使用视图，您必须拥有一个ViewResolver以及与Spring一起使用的任何其他视图技术。 您可以使用UrlBasedViewResolver和ResourceBundleViewResolver这两个实现中的任何一个。

您可以通过添加下划线然后添加语言环境来指定特定于语言环境的Tiles定义，如以下示例所示：

```markup
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
    <property name="definitions">
        <list>
            <value>/WEB-INF/defs/tiles.xml</value>
            <value>/WEB-INF/defs/tiles_fr_FR.xml</value>
        </list>
    </property>
</bean>
```

通过上述配置，tile\_fr\_FR.xml用于具有fr\_FR语言环境的请求，默认情况下使用tile.xml。

> 由于下划线用于指示语言环境，因此建议不要在图块定义的文件名中使用下划线。

**UrlBasedViewResolver**

UrlBasedViewResolver为其要解析的每个视图实例化给定的viewClass。 以下bean定义了一个UrlBasedViewResolver：

```markup
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView"/>
</bean>
```

**ResourceBundleViewResolver**

必须为ResourceBundleViewResolver提供一个属性文件，该文件包含解析程序可以使用的视图名称和视图类。 以下示例显示了ResourceBundleViewResolver的bean定义以及相应的视图名称和视图类（摘自Pet Clinic示例）：

```markup
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
    <property name="basename" value="views"/>
</bean>
```

```java
...
welcomeView.(class)=org.springframework.web.servlet.view.tiles3.TilesView
welcomeView.url=welcome (this is the name of a Tiles definition)

vetsView.(class)=org.springframework.web.servlet.view.tiles3.TilesView
vetsView.url=vetsView (again, this is the name of a Tiles definition)

findOwnersForm.(class)=org.springframework.web.servlet.view.JstlView
findOwnersForm.url=/WEB-INF/jsp/findOwners.jsp
...
```

使用ResourceBundleViewResolver时，可以轻松地混合使用不同的视图技术。

请注意，TilesView类支持JSTL（JSP标准标记库）。

SimpleSpringPreparerFactory和SpringBeanPreparerFactory 作为一项高级功能，Spring还支持两个特殊的Tiles PreparerFactory实现。有关如何在Tiles定义文件中使用ViewPreparer引用的详细信息，请参见Tiles文档。

您可以指定SimpleSpringPreparerFactory以根据指定的准备器类自动连接ViewPreparer实例，应用Spring的容器回调以及应用配置的Spring BeanPostProcessor。如果已激活Spring的上下文范围的注解配置，则将自动检测和应用ViewPreparer类中的注解。请注意，这与默认的PreparerFactory一样，期望Tiles定义文件中的preparer类。

您可以指定SpringBeanPreparerFactory以对指定的准备程序名称（而不是类）进行操作，从而从DispatcherServlet的应用程序上下文中获取相应的Spring bean。在这种情况下，完整的bean创建过程由Spring应用程序上下文控制，从而允许使用显式依赖项注入配置，作用域bean等。注意，您需要为每个准备器名称定义一个Spring bean定义（在Tiles定义中使用）。下面的示例演示如何在TilesConfigurer bean上定义SpringBeanPreparerFactory属性：

```markup
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
    <property name="definitions">
        <list>
            <value>/WEB-INF/defs/general.xml</value>
            <value>/WEB-INF/defs/widgets.xml</value>
            <value>/WEB-INF/defs/administrator.xml</value>
            <value>/WEB-INF/defs/customer.xml</value>
            <value>/WEB-INF/defs/templates.xml</value>
        </list>
    </property>

    <!-- resolving preparer names as Spring bean definition names -->
    <property name="preparerFactoryClass"
            value="org.springframework.web.servlet.view.tiles3.SpringBeanPreparerFactory"/>

</bean>
```

## 1.9.7 RSS and Atom

AbstractAtomFeedView和AbstractRssFeedView都继承自AbstractFeedView基类，分别用于提供Atom和RSS Feed视图。 它们基于java.net的ROME项目，位于org.springframework.web.servlet.view\.feed包中。

AbstractAtomFeedView要求您实现buildFeedEntries（）方法，并可选地覆盖buildFeedMetadata（）方法（默认实现为空）。 以下示例显示了如何执行此操作：

```java
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，如以下示例所示：

```java
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
    }
}
```

如果需要访问Locale，则buildFeedItems（）和buildFeedEntries（）方法会传递HTTP请求。 仅针对Cookie或其他HTTP标头的设置传入HTTP响应。 方法返回后，提要将自动写入响应对象。

有关创建Atom视图的示例，请参见Alef Arendsen的Spring Team Blog条目。

## 1.9.8 PDF and Excel

Spring提供了返回HTML以外的输出的方法，包括PDF和Excel电子表格。本节介绍如何使用这些功能。

**Document view简介**

HTML页面并非始终是用户查看模型输出的最佳方法，而Spring使从模型数据动态生成PDF文档或Excel电子表格变得简单。该文档是视图，并从服务器以正确的内容类型进行流传输，以（希望）使客户端PC能够运行其电子表格或PDF查看器应用程序作为响应。

为了使用Excel视图，您需要将Apache POI库添加到您的类路径中。为了生成PDF，您需要添加（最好是）OpenPDF库。

> 如果可能，您应该使用基础文档生成库的最新版本。特别是，我们强烈建议您使用OpenPDF（例如，OpenPDF 1.2.12）而不是过时的原始iText 2.1.7，因为OpenPDF会得到积极维护并修复了不可信任PDF内容的重要漏洞。

**PDF views**

单词列表的简单PDF视图可以扩展org.springframework.web.servlet.view\.document.AbstractPdfView并实现buildPdfDocument（）方法，如以下示例所示：

```java
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));
        }
    }
}
```

控制器可以从外部视图定义（按名称引用）返回此视图，也可以从处理程序方法返回为视图实例。

**Excel view**

从Spring Framework 4.2开始，提供org.springframework.web.servlet.view\.document.AbstractXlsView作为Excel视图的基类。 它基于Apache POI，具有取代过时的AbstractExcelView类的专用子类（AbstractXlsxView和AbstractXlsxStreamingView）。

编程模型类似于AbstractPdfView，其中buildExcelDocument（）作为中央模板方法，控制器能够从外部定义（按名称）或从处理程序方法作为View实例返回这种视图。

## 1.9.9 Jackson

Spring提供了对Jackson JSON库的支持。

**基于Jackson的JSON MVC视图**

MappingJackson2JsonView使用Jackson library的ObjectMapper将响应内容呈现为JSON。默认情况下，模型映射的全部内容（特定于框架的类除外）均编码为JSON。对于需要过滤地图内容的情况，可以使用modelKeys属性指定一组特定的模型属性进行编码。您也可以使用extractValueFromSingleKeyModel属性，以将单键模型中的值直接提取并序列化，而不是作为模型属性的映射。

您可以根据需要使用Jackson提供的注解来自定义JSON映射。当需要进一步控制时，可以在需要为特定类型提供自定义JSON序列化器和反序列化器的情况下，通过ObjectMapper属性注入自定义ObjectMapper。

**基于Jackson的XML视图**

MappingJackson2XmlView使用Jackson XML扩展程序的XmlMapper将响应内容呈现为XML。如果模型包含多个条目，则应使用modelKey bean属性显式设置要序列化的对象。如果模型包含单个条目，则会自动序列化。

您可以根据需要使用JAXB或Jackson提供的注解自定义XML映射。当需要进一步控制时，可以通过ObjectMapper属性注入自定义XmlMapper，对于自定义XML，需要为特定类型提供序列化器和反序列化器的情况。

## 1.9.10 XML Marshalling

MarshallingView使用XML Marshaller（在org.springframework.oxm包中定义）将响应内容呈现为XML。 您可以使用MarshallingView实例的modelKey bean属性显式设置要编组的对象。 或者，视图遍历所有模型属性，并封送Marshaller支持的第一个类型。 有关org.springframework.oxm包中功能的更多信息，请参见使用O / X映射器编组XML。

## 1.9.11 XSLT Views

XSLT是XML的一种转换语言，在Web应用程序中作为一种视图技术而流行。 如果您的应用程序自然处理XML，或者您的模型可以轻松转换为XML，则XSLT可以作为视图技术的不错选择。 下一节显示了如何将XML文档生成为模型数据，以及如何在Spring Web MVC应用程序中使用XSLT对其进行转换。

这个例子是一个普通的Spring应用程序，它在Controller中创建一个单词列表并将它们添加到模型图中。 返回该地图以及XSLT视图的视图名称。 有关Spring Web MVC的Controller界面的详细信息，请参见带注解的控制器。 XSLT控制器将单词列表转换为准备转换的简单XML文档。

**Beans**

Configuration是简单Spring Web应用程序的标准配置：MVC配置必须定义XsltViewResolver bean和常规MVC注解配置。 以下示例显示了如何执行此操作：

```java
@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类中，其处理程序方法定义如下：

```java
@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文档并将其添加到Model映射中。 请注意，您还可以将XML文件作为资源加载并使用它代替自定义DOM文档。

有可用的软件包自动“对象化”对象图，但是在Spring中，您可以完全灵活地以任何选择的方式从模型中创建DOM。 这样可以防止XML转换在模型数据的结构中扮演过多的角色，这在使用工具来管理DOMification流程时是一种危险。

**Transformation**

最后，XsltViewResolver解析"home” XSLT模板文件，并将DOM文档合并到其中以生成我们的视图。 如XsltViewResolver配置所示，XSLT模板位于WEB-INF / xsl目录的war文件中，并以xslt文件扩展名结尾。

以下示例显示了XSLT转换：

```markup
<?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：

```markup
<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>
```
