# 6.3.1　配置 Tiles 视图解析器

为了在 Spring 中使用 Tiles，需要配置几个 bean。我们需要一个 TilesConfigurer bean，它会负责定位和加载 Tile 定义并协调生成 Tiles。除此之外，还需要 TilesViewResolver bean 将逻辑视图名称解析为 Tile 定义。

这两个组件又有两种形式：针对 Apache Tiles 2 和 Apache Tiles 3 分别都有这么两个组件。这两组 Tiles 组件之间最为明显的区别在于包名。针对 Apache Tiles 2 的 TilesConfigurer/TilesViewResolver 位于 org.springframework.web .servlet.view\.tiles2 包中，而针对 Tiles 3 的组件位于 org.springframework .web.servlet.view\.tiles3包中。对于该例子来讲，假设我们使用的Tiles 3。

首先，配置 TilesConfigurer 来解析 Tile 定义。

{% code title="程序清单 6.1　配置 TilesConfigurer 来解析定义" %}

```java
@Bean
public TilesConfigurer tilesConfigurer() {
  TilesConfigurer tiles = new TilesConfigurer();
  tiles.setDefinitions(new String[] {
    "/WEB-INF/layout/tiles.xml"
  });
  tiles.setCheckRefresh(true);
  return tiles;
}
```

{% endcode %}

当配置 TilesConfigurer 的时候，所要设置的最重要的属性就是 definitions。这个属性接受一个 String 类型的数组，其中每个条目都指定一个 Tile 定义的 XML 文件。对于 Spittr 应用来讲，我们让它在 `/WEB-INF/layout/` 目录下查找 tiles.xml。

其实我们还可以指定多个 Tile 定义文件，甚至能够在路径位置上使用通配符，当然在上例中我们没有使用该功能。例如，我们要求 TilesConfigurer 加载 `/WEB-INF/` 目录下的所有名字为 tiles.xml 的文件，那么可以按照如下的方式设置 definitions 属性：

```java
tiles.setDefinitions(new String[] {
  "/WEB-INF/**/tiles.xml"
});
```

在本例中，我们使用了 Ant 风格的通配符 `**`，所以 TilesConfigurer 会遍历 `WEB-INF/` 的所有子目录来查找 Tile 定义。

接下来，让我们来配置 TilesViewResolver，可以看到，这是一个很基本的 bean 定义，没有什么要设置的属性：

```java
@Bean
public ViewResolver viewResolver() {
  return new TilesViewResolver();
}
```

如果你更喜欢 XML 配置的话，那么可以按照如下的形式配置 TilesConfigurer 和 TilesViewResolver：

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

TilesConfigurer 会加载 Tile 定义并与 Apache Tiles 协作， 而 TilesView-Resolver 会将逻辑视图名称解析为引用 Tile 定义的视图。它是通过查找与逻辑视图名称相匹配的 Tile 定义实现该功能的。我们需要创建几个 Tile 定义以了解它是如何运转的。

**定义 Tiles Apache**

Tiles 提供了一个文档类型定义（document type definition，DTD），用来在 XML 文件中指定 Tile 的定义。每个定义中需要包含一个元素，这个元素会有一个或多个元素。例如，如下的 XML 文档为 Spittr 应用定义了几个 Tile。

{% code title="程序清单 6.2　为 Spittr 应用定义 Tile" %}

```markup
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN"
       "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
<tiles-definitions>

  <definition name="base" template="/WEB-INF/layout/page.jsp">
    <put-attribute name="header" value="/WEB-INF/layout/header.jsp" />
    <put-attribute name="footer" value="/WEB-INF/layout/footer.jsp" />
  </definition>

  <definition name="home" extends="base">
    <put-attribute name="body" value="/WEB-INF/views/home.jsp" />
  </definition>

  <definition name="registerForm" extends="base">
    <put-attribute name="body" value="/WEB-INF/views/registerForm.jsp" />
  </definition>

  <definition name="profile" extends="base">
    <put-attribute name="body" value="/WEB-INF/views/profile.jsp" />
  </definition>

  <definition name="spittles" extends="base">
    <put-attribute name="body" value="/WEB-INF/views/spittles.jsp" />
  </definition>

  <definition name="spittle" extends="base">
    <put-attribute name="body" value="/WEB-INF/views/spittle.jsp" />
  </definition>

</tiles-definitions>
```

{% endcode %}

每个 `<definition>` 元素都定义了一个 Tile，它最终引用的是一个 JSP 模板。在名为 base 的 Tile 中，模板引用的是 `/WEBINF/ layout/page.jsp`。某个 Tile 可能还会引用其他的 JSP 模板，使这些 JSP 模板嵌入到主模板中。对于 base Tile 来讲，它引用的是一个头部 JSP 模板和一个底部 JSP 模板。

base Tile 所引用的 page.jsp 模板如下面程序清单所示。

{% code title="程序清单 6.3 主布局模板：引用其他模板来创建视图" %}

```markup
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="t" %>
<%@ page session="false" %>
<html>
  <head>
    <title>Spittr</title>
    <link rel="stylesheet" 
          type="text/css" 
          href="<s:url value="/resources/style.css" />" >
  </head>
  <body>
    <div id="header">
      <t:insertAttribute name="header" />
    </div>
    <div id="content">
      <t:insertAttribute name="body" />
    </div>
    <div id="footer">
      <t:insertAttribute name="footer" />
    </div>
  </body>
</html>
```

{% endcode %}

在程序清单 6.3 中，需要重点关注的事情就是如何使用 Tile 标签库中的 `<t:insert Attribute>` JSP 标签来插入其他的模板。在这里，用它来插入名为 header、body 和 footer 的模板。最终，它会形成图 6.4 所示的布局。

![图 6.4　通用的布局，定义了头部、主体区以及底部](/files/-LnACgkyYsBRAtNyBmiY)

在 base Tile 定义中，header 和 footer 属性分别被设置为引用 `/WEB-INF/ layout/header.jsp` 和 `/WEB-INF/layout/footer.jsp`。但是 body 属性呢？它是在哪里设置的呢？

在这里，base Tile 不会期望单独使用。它会作为基础定义（这是其名字的来历），供其他的 Tile 定义扩展。在程序清单 6.2 的其余内容中，我们可以看到其他的 Tile 定义都是扩展自 base Tile。它意味着它们会继承其 header 和 footer 属性的设置（当然，Tile 定义中也可以覆盖掉这些属性），但是每一个都设置了 body 属性，用来指定每个 Tile 特有的 JSP 模板。

现在，我们关注一下 home Tile，它扩展了 base。因为它扩展了 base，因此它会继承 base 中的模板和所有的属性。尽管 home Tile 定义相对来说很简单，但是它实际上包含了如下的定义：

{% code title="" %}

```markup
<definition name="home" template="/WEB-INF/layout/page.jsp">
  <put-attribute name="header" value="/WEB-INF/layout/header.jsp" />
  <put-attribute name="footer" value="/WEB-INF/layout/footer.jsp" />
  <put-attribute name="body" value="/WEB-INF/views/home.jsp" />
</definition>
```

{% endcode %}

属性所引用的每个模板是很简单的，如下是 header.jsp 模板：

```markup
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
<a href="<s:url value="/" />"><img 
    src="<s:url value="/resources" />/images/spitter_logo_50.png" 
    border="0"/></a>
```

footer.jsp 模板更为简单：

```markup
Copyright &copy; Craig Walls
```

每个扩展自 base 的 Tile 都定义了自己的主体区模板，所以每个都会与其他的有所区别。但是为了完整地了解 home Tile，如下展现了 home.jsp：

```markup
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<h1>Welcome to Spitter</h1>

<a href="<c:url value="/spittles" />">Spittles</a> | 
<a href="<c:url value="/spitter/register" />">Register</a>
```

这里的关键点在于通用的元素放到了 page.jsp、header.jsp 以及 footer.jsp 中，其他的 Tile 模板中不再包含这部分内容。这使得它们能够跨页面重用，这些元素的维护也得以简化。

要想看一下这些元素组合在一起的样子，那么可以看一下图 6.5。如图所示，它包含了一些样式和图像以增加应用的美观性。我们不是专门讨论使用 Tiles 实现页面布局的，因此在本节中不会涵盖所有的细节。但是，我们可以看到页面上的各种组件通过 Tile 定义组合在了一起，并且渲染出了 Spittr 应用的主页。

在 Java Web 应用领域，JSP 长期以来都是占据主导地位的方案。但是，在这个领域有了新的竞争者，也就是 Thymeleaf。接下来让我们 看一下如何在 Spring MVC 应用中使用 Thymeleaf。

![图 6.5　Spittr 首页，通过 Apache Tiles 进行的布局](/files/-LnAELfDs2AUDkzINdSM)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://potoyang.gitbook.io/spring-in-action-v4/untitled-1/untitled-2/untitled.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
