2.2.3 Spring 通用的標簽庫
除了表單綁定標簽庫之外,Spring
還提供了更為通用的JSP
標簽庫。要使用它,必須在頁面上對其進行聲明:
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
JSP標簽 | 描述 |
---|---|
<s:bind> |
將綁定屬性的狀態(tài)導出到一個名為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> |
創(chuàng)建相對于上下文的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 創(chuàng)建URL
<s:url>
是一個很小的標簽,其主要任務就是創(chuàng)建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>
創(chuàng)建URL
,并將其賦值給一個變量供模版在稍后使用:
<s:url href="/spitter/register" var="registerUrl" />
<a href="${registerUrl}">Register</a>
默認情況,URL
是在頁面作用域內創(chuàng)建的。但是通過設置scope
屬性,我們可以讓<s:url>
在應用作用域內、會話作用域內或請求作用域內創(chuàng)建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>
中也有,但是如果向要創(chuàng)建帶有路徑參數的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
模版??梢钥吹?code>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
規(guī)范與Servlet
規(guī)范緊密耦合,這意味著它只能用在基于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
引擎,用來解析模版,并基于這些模版渲染結果??梢钥吹?,我們?yōu)槠渥⑷肓艘粋€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
表達式,用來計算動態(tài)的值。Thymeleaf
的價值在于它與純HTML
模版非常接近。唯一的區(qū)別就是使用了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
的變量中。
“${}”
和“*{}”
有什么區(qū)別:前者是變量表達式。一般來講,會是OGNL
表達式,在使用Spring
時是SpEL
表達式。在${spitter}
例子中,會解析key
為spitter
的model
屬性。而后者是選擇表達式。變量表達式是基于整個SpEL
上下文計算的,而選擇表達式是基于一個選中對象計算的。本例中選擇的是Spitter
對象。