2.2.3 Spring 通用的标签库
除了表单绑定标签库之外,Spring
还提供了更为通用的JSP
标签库。要使用它,必须在页面上对其进行声明:
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
JSP标签 | 描述 |
---|---|
<s:bind> |
将绑定属性的状态导出到一个名为status 的页面作用域属性中,与<s:path> 组合使用获取绑定属性的值 |
<s:escapeBody> |
将标签体中的内容进行HTML 和/或JS 转义 |
<s:hasBindErrors> |
根据指定模型对象(在请求属性中)是否有绑定错误,有条件地渲染内容 |
<s:htmlEscape> |
为当前页面设置默认的HTML 转义值 |
<s:message> |
根据给定的编码获取信息,然后要么进行渲染(默认行为),要么将其设置为页面作用域、请求作用域、会话作用域或应用作用域的变量(通过使用var 和scope 属性实现) |
<s:nestedPath> |
设置嵌入式的path ,用于<s:bind> 之中 |
<s:theme> |
根据给定的编码获取主题信息,然后要么进行渲染(默认行为),要么将其设置为页面作用域、请求作用域、会话作用域或应用作用域的变量(通过使用var 和scope 属性实现) |
<s:transform> |
使用命令对象的属性编辑器转换命令对象中不包含的属性 |
<s:url> |
创建相对于上下文的URL ,支持URI 模版变量以及HTML/XML/JS 转义,可以渲染URL (默认行为),也可以将其设置为页面作用域、请求作用域、会话作用域或应用作用域的变量(通过使用var 和scope 属性实现) |
<s:eval> |
计算符合Spring 表达式语言(SpEL )语法的某个表达式的值,然后然后要么进行渲染(默认行为),要么将其设置为页面作用域、请求作用域、会话作用域或应用作用域的变量(通过使用var 和scope 属性实现) |
2.2.4 展现国际化信息
在进行国际化中,对于渲染文本来说,<s:message>
是很好的方案:
<h1><s:message code="spittr.welcome" /></h1>
按照这里的方式,<s:message>
将会根据key
为spittr.welcome
的信息来渲染文本。因此,如果希望此标签能完成任务,就需要配置一个这样的信息源。Spring
有多个信息源的类,它们都实现了MessageSource
接口。比较常用的是ResourceBundleMessageSource
。它会从一个属性文件中加载信息,这个属性文件的名称是根据基础名称衍生而来的:
@Bean
public MessageSource messageSource(){
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("message");
return messageSource;
}
在上面代码中,核心在于设置basename
属性,将其设置为message
后,ResourceBundleMessageSource
就会试图在根路径的属性文件中解析信息,这些属性文件的名称是根据这个基础名衍生得到的(message.properties
)。
另外可选方案是使ReloadableResourceBundleMessageSource
,使用和工作方式和ResourceBundleMessageSource
非常类似,但是它能够重新加载信息属性,而不必重新编译或重启应用。
@Bean
public MessageSource messageSource(){
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("file:///etc/spittr/message");
messageSource.setCacheSeconds(10);
return messageSource;
}
说明:这里basename
设置为在应用外部查找,还可以设置为在类路径下(以“classpath:”
作为前缀)、文件系统中(以“file:”
作为前缀)或Web
应用的根路径下(没有前缀)查找属性。
2.2.5 创建URL
<s:url>
是一个很小的标签,其主要任务就是创建URL
,然后将其赋值给一个变量或者渲染到响应中。它是JSTL
中<c:url>
标签的替代者,但是有几项特殊技巧。
按照其最简单的形式,<s:url>
会接受一个相对于Servlet
上下文的URL
,并在渲染的时候,预先添加上Servlet
上下文路径。
<a href="<s:url href="/spitter/register" />">Register</a>
如果应用的Servlet
上下文名为spittr
,那么响应中将渲染为如下的HTML
:
<a href="/spittr/spitter/register">Register</a>
还可以使用<s:url>
创建URL
,并将其赋值给一个变量供模版在稍后使用:
<s:url href="/spitter/register" var="registerUrl" />
<a href="${registerUrl}">Register</a>
默认情况,URL
是在页面作用域内创建的。但是通过设置scope
属性,我们可以让<s:url>
在应用作用域内、会话作用域内或请求作用域内创建URL
:
<s:url href="/spitter/register" var="registerUrl" scope="request" />
如果希望在URL
上添加参数的话,那么你可以使用<s:param>
标签。
<s:url href="/spittles" var="spittlesUrl">
<s:param name="max" value="60">
<s:param name="count" value="20">
</s:url>
上面讲到的功能,在JSTL <c:url>
中也有,但是如果向要创建带有路径参数的URL
,则需要使用<s:url>
了。
<s:url href="/spittles/{username}" var="spittlesUrl">
<s:param name="username" value="jack">
</s:url>
当href
属性中的占位符匹配<s:param>
中所指定的参数时,这个参数将会插入到占位符的位置中。如果<s:param>
参数无法匹配href
中的任何占位符,那么这个参数(此处是username
)将会作为查询参数。
<s:url>
标签还可以解决URL
的转移需求,如过希望将渲染得到的URL
内容展现在Web
页面上(而不是作为超链接),可以这样:
<s:url href="/spittles" htmlEscape="true">
<s:param name="max" value="60">
<s:param name="count" value="20">
</s:url>
所渲染的结果如下:
/spitter/spittles?max=60&count=20
当然如果想在JS
中使用URL
则需要设置javascriptEscape
属性:
<s:url href="/spittles" javascriptEscape="true" var="spittlesJSUrl">
<s:param name="max" value="60">
<s:param name="count" value="20">
</s:url>
使用如下:
<script>
var spittlesUrl = "{spittlesJSUrl}"
</script>
渲染结果为:
<script>
var spittlesUrl = "\/spitter\/spittles?max=60&count=20"
</script>
2.2.6 转义内容
<s:escapeBody>
标签是一个通用的转义标签。它会渲染标签中内嵌的内容,并且在必要的时候进行转义。如想在页面展现一个HTML
代码片段:
<s:escapeBody htmlEscape="true">
<h1>Hello</h1>
</s:escapeBody>
它将会渲染成如下内容:
<h1>Hello</h1>
在浏览器中显示的时候就会自动转义为:
<h1>Hello</h1>
当然还可以设置javascriptEscape
属性对JS
进行转义,但是此标签不能将内容设置为变量。
三、使用 Apache Tiles 视图定义布局
如果我们想为应用中的所有页面定义一个通用的头部和底部。最原始的方式就是查找每个JSP
,并为其添加头部和底部的HTML
。但是这种方式的扩展性并不好。更好的方式是使用布局引擎,如Apache Tiles
,定义适用于所有页面的通用页面布局。
3.1 配合Tiles视图解析器
为了在Spring
中使用Tiles
,需要配置几个bean
。需要一个TilesConfigurer bean
,它会负责定位和加载Tile
定义并协调生成Tiles
。除此之外,还需要TilesViewResolver bean
将逻辑视图名称解析为Tile
定义。
这两个组件又有两种形式:针对Apache Tiles 2
(位于org.springframework.web.servlet.view.tiles2
)和Apache Tiles 3
(位于org.springframework.web.servlet.view.tiles3
)分别都有这么两个组件。
首先,配置TilesConfigurer
来解析Tile
定义。
@Bean
public TilesConfigurer tilesConfigurer(){
TilesConfigurer tiles = new TilesConfigurer();
tiles.setDefinitions(new String[] {"/WEB-INF/layout/tiles.xml"});
tiles.setCheckRefresh(true);
return tiles;
}
说明:配置TilesConfigurer
的时候,所要设置的最重要的属性就是definitions
。接受一个String
类型的数组,其中每个条目都指定一个Tile
定义的XML
文件。这里让它在"/WEB-INF/layout/"
下查找tiles.xml
文件。如果我们想让其加载"/WEB-INF/"
目录下的所有名为tiles.xml
的文件则可以这样配置路径:/WEB-INF/**/tiles.xml
。
接下来,让我们配置TilesViewResolver
,可以看到,这是一个很基本的bean
定义,没有什么要设置的属性:
@Bean
public ViewResolver viewResolver(){
return new TilesViewResolver();
}
当然也可以使用XML
的方式进行配置:
<bean id="tilesCOnfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/layout/tiles.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
协作,而TilesViewResolver
会将逻辑视图名称解析为引用Tile
定义的视图。
3.1.1 定义Tiles
下面看如何定义Tiles
文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!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>
<!--定义base Tile-->
<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"><!--扩展base Tile-->
<put-attribute name="body" value="/WEB-INF/views/home.jsp" />
</definition>
<definition name="registerForm" extends="base"><!--扩展base Tile-->
<put-attribute name="body" value="/WEB-INF/views/registerForm.jsp" />
</definition>
<definition name="profile" extends="base"><!--扩展base Tile-->
<put-attribute name="body" value="/WEB-INF/views/profile.jsp" />
</definition>
<definition name="spittles" extends="base"><!--扩展base Tile-->
<put-attribute name="body" value="/WEB-INF/views/spittles.jsp" />
</definition>
<definition name="spittle" extends="base"><!--扩展base Tile-->
<put-attribute name="body" value="/WEB-INF/views/spittle.jsp" />
</definition>
</tiles-definitions>
说明:每个<definition>
元素都定义了一个Tile
,最终引用的是一个JSP
模版。可以看到base Tile
中设置了基本的模版,及顶部和底部。而后面的Tile
都是用来扩展base Tile
的,后面的Tile
都将作为body
插入到整个页面,和顶部、底部一起组成一个完整的页面。我们看base Tile
所引用的page.jsp
模版:
<%@ 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>
说明:从上面可以看到三个页面是如何组成一个完整的页面的,其实扩展的Tile
会继承base Tile
的相关属性,如home Tile
实际上包含了如下定义:
<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>
为了完整的了解home Tile
,如下展现了home.jsp
:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<h1>Welcome to Spittr</h1>
<a href="<c:url value="spittles" />">Spittles</a>
<a href="<c:url value="spittler/register" />">Register</a>
四、使用Thymeleaf
JSP
规范与Servlet
规范紧密耦合,这意味着它只能用在基于Servlet
的Web
应用之中。JSP
模版不能作为通用的模版(如格式化Email
),也不能用于非Servlet
的Web
应用。
4.1 配置Thymeleaf视图解析器
为了要在Spring
中使用Thymeleaf
,需要配置三个启用Thymeleaf
与Spring
集成的bean
:
-
ThymeleafViewResolver
:将逻辑试图名称为解析为Thymeleaf
模版视图; -
SpringTemplateEngine
:处理模版并渲染结果; -
TemplateResolver
:加载Thymeleaf
模版。
如下为声明这些bean
的Java
配置(在WebConfig.java
中):
@Bean
public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
@Bean
public TemplateEngine templateEngine(TemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
return templateEngine;
}
@Bean
public TemplateResolver templateResolver() {
TemplateResolver templateResolver = new ServletContextTemplateResolver();
templateResolver.setPrefix("/WEB-INF/views/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
return templateResolver;
}
当然也可以使用XML
方式进行配置:
<bean id="viewResolver" class="org.thymeleaf.spring3.view.ThymeleafViewResolver"
p:templateEngine-ref="templateEngine" />
<bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine"
p:templateEngine-ref="templateResolver" />
<bean id="templateResolver" class="org.thymeleaf.templateresolver.ServletContextTemplateResolver"
p:prefix="/WEB-INF/templates"
p:suffix=".html"
p:templateMode="HTML5"/>
说明:ThymeleafViewResolver
是Spring MVC
中ViewResolver
的一个实现,会将一个逻辑视图名解析为一个Thymeleaf
模版。在ThymeleafViewResolver bean
中注入了一个对SpringTemplateEngine bean
的引用。SpringTemplateEngine
会在Spring
中启用Thymeleaf
引擎,用来解析模版,并基于这些模版渲染结果。可以看到,我们为其注入了一个TemplateResolver bean
的引用。TemplateResolver
会最终定位和查找模版。
4.2 定义Thymeleaf模版
Thymeleaf
在很大程度上就是HTML
文件,与JSP
不同,它没有什么特殊的标签或标签库。它通过自定义的命名空间,为标准HTML
标签集合添加Thymeleaf
属性。
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"><!--声明Thymeleaf命名空间-->
<head>
<title>Spitter</title>
<link rel="stylesheet" type="text/css"
th:href="@{/resources/style.css}"></link><!--到样式表的th:href链接-->
</head>
<body>
<div id="header" th:include="page :: header"></div>
<div id="content">
<h1>Welcome to Spitter</h1>
<!--到页面的th:href链接-->
<a th:href="@{/spittles}">Spittles</a> |
<a th:href="@{/spitter/register}">Register</a>
</body>
</html>
说明:th:href
属性的特殊之处在于它的值中可以包含Thymeleaf
表达式,用来计算动态的值。Thymeleaf
的价值在于它与纯HTML
模版非常接近。唯一的区别就是使用了th:href
属性。这意味着Thymeleaf
模版与JSP
不同,它能够按照原始的方式进行编辑甚至渲染,而不必经过任何类型的处理器。
4.2.1 借助Thymeleaf实现表单绑定
下面直接通过表单页面registerForm.html
进行说明:
<form method="POST" th:object="${spitter}">
<div class="errors" th:if="${#fields.hasErrors('*')}">
<ul>
<li th:each="err : ${#fields.errors('*')}"
th:text="${err}">Input is incorrect</li>
</ul>
</div>
<label th:class="${#fields.hasErrors('firstName')}? 'error'">First Name</label>:
<input type="text" th:field="*{firstName}"
th:class="${#fields.hasErrors('firstName')}? 'error'" /><br/>
<label th:class="${#fields.hasErrors('lastName')}? 'error'">Last Name</label>:
<input type="text" th:field="*{lastName}"
th:class="${#fields.hasErrors('lastName')}? 'error'" /><br/>
<label th:class="${#fields.hasErrors('email')}? 'error'">Email</label>:
<input type="text" th:field="*{email}"
th:class="${#fields.hasErrors('email')}? 'error'" /><br/>
<label th:class="${#fields.hasErrors('username')}? 'error'">Username</label>:
<input type="text" th:field="*{username}"
th:class="${#fields.hasErrors('username')}? 'error'" /><br/>
<label th:class="${#fields.hasErrors('password')}? 'error'">Password</label>:
<input type="password" th:field="*{password}"
th:class="${#fields.hasErrors('password')}? 'error'" /><br/>
<input type="submit" value="Register" />
</form>
说明:这里我们使用的是Thymeleaf
的方言。th:class
属性会渲染为一个class
属性,它的值是根据给定的表达式计算得到的。如果有有错误,class
在渲染时的值为error
,如果这个域没有错误,将不会渲染class
属性。th:field
属性用来引用后端对象的相关域(如spitter
的firstName
属性),其中星号表示为所有表单对象(此处为spitter
)定义后端对象。使用th:if
属性来检查是否有校验错误,如果有,会渲染<div>
,否则不渲染。<li>
标签上的th:each
属性将会通知Thymeleaf
为每项错误都渲染一个<li>
,在每次迭代中会将当前错误设置到一个名为err
的变量中。
“${}”
和“*{}”
有什么区别:前者是变量表达式。一般来讲,会是OGNL
表达式,在使用Spring
时是SpEL
表达式。在${spitter}
例子中,会解析key
为spitter
的model
属性。而后者是选择表达式。变量表达式是基于整个SpEL
上下文计算的,而选择表达式是基于一个选中对象计算的。本例中选择的是Spitter
对象。