我们的首要任务是为我们的杂货店网站创建一个主页。
这个页面的第一个版本将非常简单:只有一个标题和一条欢迎信息。下面是我们的 /WEB-INF/templates/home.html 文件:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Good Thymes Virtual Grocery</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="stylesheet" type="text/css" media="all" href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" /> </head> <body> <p th:text="#{home.welcome}">Welcome to our grocery store!</p> </body> </html>
首先你会注意到,这个文件是 HTML5,任何浏览器都可以正确显示,因为它不包含任何非 HTML 标记(浏览器会忽略他们不理解的所有属性,如th:text)。
但您可能还注意到,这个模板实际上不是一个有效的 HTML5 文档,因为 HTML5 规范不允许我们在 th:* 表单中使用这些非标准属性。事实上,我们甚至在<html>标签中添加了一个 xmlns:th 属性,这是绝对不是一个 HTML5 属性:
<html xmlns:th="http://www.thymeleaf.org">
但这在模板处理中没有任何影响,但它就像一个咒语,可以防止 IDE 提示缺少所有 th:* 属性的命名空间定义。
那么,如果我们想让这个模板成为 html5 有效的呢?这个简单,切换到 Thymeleaf 的数据属性语法,使用 data- 前缀作为属性名,使用连字符(-)分隔符代替分号(:)。如下:
<!DOCTYPE html> <html> <head> <title>Good Thymes Virtual Grocery</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <link rel="stylesheet" type="text/css" media="all" href="../../css/gtvg.css" data-th-href="@{/css/gtvg.css}" /> </head> <body> <p data-th-text="#{home.welcome}">Welcome to our grocery store!</p> </body> </html>
HTML5 规范允许自定义数据前缀属性,因此,使用上面的代码,我们的模板将是一个有效的 HTML5 文档。
注意:
这两种表示法是完全等价和可互换的,但是为了代码示例的简单性和紧凑性,本教程将使用名称空间表示法(th:*)。此外,th:* 表示法更通用,在所有 Thymeleaf 模板模式(XML、TEXT……)中都允许使用,而 data- 表示法只允许在 HTML 模式中使用。
外部化文本是将模板代码的片段从模板文件中提取出来,以便将它们保存在单独的文件中(通常是 .properties 文件),并且可以很容易地用其他语言编写等效文本替换(这个过程称为国际化或简称 i18n),外部化的文本片段通常被称为 "消息"。
消息总是有一个键来标识它们,Thymeeaf 允许您使用 #{…} 语法指定文本应与特定消息相对应:
<p th:text="#{home.welcome}">Welcome to our grocery store!</p>
事实上,我们在这里看到的是 Thymelaf 标准方言的两个不同特征:
th:text 属性,它计算其值(表达式)并将结果设置为标记的主体,有效地替换“Welcome to our grocery store!”文本。
#{home.welcome} 表达式,指示 th:text 属性所使用的文本应该与我们外部化文本中 home.welcome 键对应的文本信息。
现在,这个外部化的文本在哪里?
Thymeleaf 中,外部化文本的位置是完全可配置的,这取决于所使用的特定 org.thymeleaf.messageresolver.IMessageResolver 实现。通常,将使用基于 .properties 文件的实现。如果需要从其他地方获取消息(例如:从数据库获取消息),我们可以创建自己的实现。
但是,我们在初始化期间没有为模板引擎指定消息解析器,这意味着我们的应用程序使用的是由org.thymeleaf.messageresolver.StandardMessageResolver 实现的标准消息解析器。
标准消息解析器希望在与模板同名的文件夹中的属性文件中找到 /WEB-INF/Templates/home.html 的消息,例如:
/WEB-INF/templates/home_en.properties 用于英文文本
/WEB-INF/templates/home_es.properties 用于西班牙语文本
/WEB-INF/templates/home_pt_BR.properties 用于葡萄牙语(巴西)语言文本
/WEB-INF/templates/home.properties 默认文本(如果区域设置不匹配)
让我们来看一下 home_es.properties 文件:
home.welcome=¡Bienvenido a nuestra tienda de comestibles!
这就是我们制作 Thymeleaf 流程模板所需的全部内容。让我们创建我们的主控制器。
为了处理我们的模板,我们将创建一个 HomeController 类,需要实现前面看到的 IGTVGController 接口,重写 process() 方法:
import java.io.Writer; import java.util.Calendar; import org.thymeleaf.ITemplateEngine; import org.thymeleaf.context.WebContext; import org.thymeleaf.web.IWebExchange; /** * 主页控制器 * @author Administrator */ public class HomeController implements IGTVGController { public HomeController() { super(); } @Override public void process(final IWebExchange webExchange, final ITemplateEngine templateEngine, final Writer writer) throws Exception { WebContext ctx = new WebContext(webExchange, webExchange.getLocale()); ctx.setVariable("today", Calendar.getInstance()); templateEngine.process("home", ctx, writer); } }
我们首先看到的是上下文的创建。Thymeleaf 上下文是一个实现 org.thymeleaf.context.IContext 接口的对象。上下文应包含在变量映射中执行模板引擎所需的所有数据,并引用必须用于外部化消息的区域设置。IContext 接口定义如下:
public interface IContext { // 返回应用于处理模板的区域设置。 public Locale getLocale(); // 检查特定变量是否已包含在此上下文中。 public boolean containsVariable(final String name); // 获取包含此上下文中所包含的所有变量名的列表。 public Set<String> getVariableNames(); // 按名称检索特定的变量。 public Object getVariable(final String name); }
该接口有一个专门的扩展 org.thymeleaf.context.IWebContext 接口,旨在用于基于 Servlet API 的 Web 应用程序(如:Spring MVC)。
public interface IWebContext extends IContext { // 返回与模板执行关联的 IWebExchange 对象 public IWebExchange getExchange(); }
Thymeleaf 核心库提供了以下每一个接口的实现:
org.thymeleaf.context.Context 实现了 IContext 接口
org.thymeleaf.context.WebContext 实现了 IWebContext 接口
正如你在控制器代码中看到的,我们需要使用 WebContext。事实上,我们必须这样做,因为使用 WebApplicationTemplateResolver 要求我们使用实现 IWebContext 的上下文。如下:
WebContext ctx = new WebContext(webExchange, webExchange.getLocale());
我们可以使用一些专门的表达式从模板中的 WebContext 中获取请求参数和请求、会话和应用程序属性。例如:
${x} 将返回存储在 thymeleaf 上下文中或作为请求属性的变量 x。
${param.x} 将返回一个名为 x 的请求参数(它可能是多值的)。
${session.x} 将返回一个名为 x 的会话属性。
${application.x} 将返回一个名为 x 的 Servlet 上下文属性。
context 对象准备好了,现在我们可以告诉模板引擎使用 context 来处理模板(通过它的名字),并将响应写入器(writer)传递给它,以便将响应写入它:
templateEngine.process("home", ctx, writer);
让我们看看使用西班牙语语言环境的结果:
<!DOCTYPE html><html> <head> <title>Good Thymes Virtual Grocery</title> <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/> <link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" /> </head> <body> <p>¡Bienvenido a nuestra tienda de comestibles!</p> </body> </html>