【轉】Spring整合Velocity模板

9.1 使用Velocity模板
Velocity是一種針對Java應用的易用的模板語言。Velocity模板中沒有任何Java代碼,這使得它能夠同時被非開發人員和開發人員輕鬆地理解。Velocity的用戶手冊上是這麼說的:“Velocity將Java代碼從Web頁面中分離出來,使用Web站點從長遠看更容易維護,並且提供了一種可行的JavaServer Pages替代解決方案。”

除了JSP,Velocity可能是用於Web應用的最流行的模板語言。因此很可能你會想採用Velocity作爲視圖層技術開發基於Spring的應用。幸運地是,Spring支持將Velocity作爲Spring MVC的視圖層模板語言。

讓我們通過基於Velocity重新實現Spring培訓應用中的視圖層來看一下如何在Spring MVC中使用Velocity。
9.1.1 定義Velocity視圖
假設你已經選擇使用Velocity而不是JSP來創建Spring培訓應用的視圖。你需要使用Velocity模板編寫的頁面之一是顯示可用課程列表的頁面。程序清單 9.1 顯示了courseList.vm,一個和courseList.jsp等價的用於顯示課程列表的Velocity模板。

 程序清單9.1 基於Velocity的課程列表

<html>

<head>

<title>Course List</title>

</head>

<body>

<h2>COURSE LIST</h2>

<table width="600" border="1" cellspacing="1" cellpadding="1">

<tr bgcolor="#999999">

<td>Course ID</td>

<td>Name</td>

<td>Instructor</td>

<td>Start</td>

<td>End</td>

</tr>

#foreach($course in $courses)

<tr>

<td>

<a href="displayCourse.htm?id=${course.id}">

${course.id}

</a>

</td>

<td>${course.name}</td>

<td>${course.instructor.lastName}</td>

<td>${course.startDate}</td>

<td>${course.endDate}</td>

</tr>

#end // 在所有課程中循環

</table>

</body>

</html>

可能你首先注意到的是這個模板中沒有任何模板標籤。這是因爲Velocity不是基於與JSP類似的標籤的,而是採用了它自己的語言——稱爲Velocity模板語言(VTL)——用於流程控制和其他指令。在courseList.vm中,#foreach指令用於循環處理一個課程列表,顯示每個課程的明細。除了這個Velocity和JSP的基本區別之外,你會發現Velocity的表達式語言和JSP很相似。事實上,當JSP使用${}作爲它自己的表達式語言時,它不過是模仿Velocity的做法而已。這個模板僅僅演示了很少一部分你可以使用Velocity所做的事情。

如果想知道更多,可以訪問Velocity位於http://jakarta.apache.org/velocity的主頁。注意當完成模板之後,你需要配置Spring使它可以在MVC應用中使用Velocity模板作爲視圖。
9.1.2 配置Velocity引擎
首先需要配置的是Velocity引擎自己。要做到這點,可以通過以下方式在Spring配置文件中聲明一個VelocityConfigurer Bean:

<bean id="velocityConfigurer" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">

<property name="resourceLoaderPath">

<value>WEB-INF/velocity/</value>

</property>

</bean>

VelocityConfigurer負責在Spring中設置Velocity引擎。這裏,我們通過屬性resourceLoaderPath告訴Velocity到哪裏尋找它的模板。我們建議將模板放到WEB-INF的某個子目錄下面,這樣可以保證這些模板不能被直接訪問。也可以通過velocityProperties屬性來設置其他Velocity的配置細節。例如下面的VelocityConfigurer配置:

<bean id="velocityConfigurer" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">

<property name="resourceLoaderPath">

<value>WEB-INF/velocity/</value>

</property>

<property name="velocityProperties">

<props>

<prop key="directive.foreach.counter.name">loopCounter</prop>

<prop key="directive.foreach.counter.initial.value">0</prop>

</props>

</property>

</bean>

可以注意到velocityProperties屬性使用一個<props>元素來設置多個屬性。在這裏可以設置的屬性與一個典型的Velocity應用中通過“velocity.properties”文件設置的屬性是一樣的。缺省地,Velocity的#foreach循環維護一個名爲$velocityCount的循環計數器,該計數器在第一輪循環開始時從1開始計數。但這裏我們設置屬性directive.foreach.counter.name爲loopCounter,因此將使用$loopCounter來引用循環計數器。我們也通過設置屬性directive.foreach.counter.initial.value爲0使循環計數器由零開始計數。(想知道關於Velocity配置屬性的信息,請參考Velocity開發者指南http://jakarta.apache.org/velocity/developer-guide.html。)
9.1.3 解析Velocity視圖
要使用Velocity模板視圖,你必須做的最後一件事情是配置一個視圖解析器。具體地說,需要以如下方式在Spring上下文配置中聲明一個VelocityViewResolver Bean:

<bean id="viewResolver" class="org.springframework.

web.servlet.view.velocity.VelocityViewResolver">

<property name="suffix"><value>.vm</value></property>

</bean>

VelocityViewResolver和Velocity的關係與InternalResourceViewResolver和JSP的關係相似。正如InternalResourceViewResolver,它使用prefix屬性和suffix屬性由視圖的邏輯名構造出模板文件的路徑。這裏我們僅僅設置suffix屬性爲“.vm”擴展名。由於模板目錄的路徑已經通過VelocityConfigurer的resourceLoaderPath屬性配置好了,因此這裏不需要設置前綴。

注意:這裏把Bean的ID設置爲viewResolver。這一點很重要,因爲我們並沒有配置DispatcherServlet檢測所有的視圖解析器。如果要同時使用多個視圖解析器,則你很可能需要將這個ID改成某個更合適的名字(並且是惟一的),比如velocityViewResolver。

現在,你的應用系統已經可以渲染基於Velocity模板的視圖了。你只需要在返回的ModelAndView對象中通過邏輯名引用所需的視圖。以ListCourseController爲例,不需要做其他事情,因爲它已經返回如下的ModelAndView對象:

return new ModelAndView("courseList", "courses", allCourses);

視圖的邏輯名爲“courseList”。當解析這個視圖時,“courseList”加上後綴“.vm”構成了一個模板名“courseList.vm”。VelocityViewResolver會在WEB-INF/velocity路徑下尋找這個模板。

至於“courses”模型對象,它會作爲一個Velocity屬性暴露給Velocity模板使用。在程序清單9.1中,它就是在#foreach指令中使用的集合對象。
9.1.4 格式化日期和數字
儘管應用已經配置成可以渲染Velocity視圖了,但我們還有一些雜七雜八的問題需要解決。當你比較程序清單9.1中的couseList.vm和courseList.jsp時,會注意到courseList.vm沒有像courseList.jsp一樣對課程的ID、開始日期和結束日期進行格式化。在courseList.jsp中,課程ID顯示爲一個6位定長的前面以零補齊的數字,而所有的日期以完整格式顯示。爲了完成courseList.vm,你需要對它作進一步的調整,對ID和日期屬性進行格式化。

VTL並不直接支持日期和數字的格式化,而是通過提供日期和時間的工具類來支持格式化。爲了允許使用這些工具,你需要告訴VelocityViewResolver在模板中暴露它們時使用的屬性名。這些屬性名是通過VelocityViewResolver的dateToolAttribute和numberToolAttribute屬性來規定的:

<bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">



<property name="dateToolAttribute">

<value>dateTool</value>

</property>

<property name="numberToolAttribute">

<value>numberTool</value>

</property>

</bean>

在這裏,我們規定數字工具通過numberTool屬性暴露給Velocity使用。因此,要格式化課程ID,你只需要通過數字工具的format()方法來處理課程ID即可,如下:

$numberTool.format("000000", course.id)

方法format()的第一個參數是模式字符串,在這裏我們規定課程ID顯示爲6個數字的域,必要時在前面補零。模式字符串的語法和java.text.DecimalFormat一致。請參考Velocity關於NumberTool的文檔來獲取更多關於該工具功能的信息。

類似地,我們分配日期工具使用dateTool屬性。爲了格式化課程的開始和結束日期,只需使用日期工具的format()方法:

$dateTool.format("FULL", course.startDate)

$dateTool.format("FULL", course.endDate)

與數字工具的format()方法一樣,第一個參數也是模式字符串。模式字符串的語法與java.text.SimpleDateFormat一致。另外,也可以設置模式字符串爲FULL、LONG、MEDIUM、SHORT或DEFAULT中的一個,以使用標準的java.text.DateFormat模式。這裏我們設置模式字符串爲FULL來表示完整的日期格式。請參考Velocity關於DateTool的文檔來獲得更多關於該工具功能的信息。
9.1.5 暴露請求和會話屬性
儘管需要在Velocity模板中顯示的大多數數據都可以通過ModelAndView對象的模型Map傳遞給視圖,有時候也會需要顯示servlet請求或會話中的屬性。比如,當用戶登錄到應用系統時,用戶的信息可能存放在servlet會話中。

如果在每一個控制器中都將請求或會話的屬性複製到模型Map中,這會是非常笨拙的。幸運的是,VelocityViewResolver會幫你將這些屬性複製到模型中。屬性exposeRequestAttributes和exposeSessionAttributes告訴VelocityViewResolver是否需要將servlet請求和會話中的屬性複製到模型中。比如:

<bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">



<property name="exposeRequestAttributes">

<value>true</value>

</property>

<property name="exposeSessionAttributes">

<value>true</value>

</property>

</bean>

這兩個屬性的默認值都爲false。但在這裏我們把這兩個屬性都設置爲true,從而請求和會話的屬性都會被複制到模型中,並且在Velocity模板中可見。9.1.6 在Velocity中綁定表單域
在第8章中,我們看到如何使用Spring的<spring:bind>JSP標籤將表單域綁定到一個命令對象中。這個標籤在向用戶顯示錶單域相關的錯誤時也是非常有用的。

幸運的是,當你使用Velocity而不是JSP時,不必放棄<spring:bind>提供的功能。Spring提供了若干個Velocity宏來模仿<spring:bind>標籤的功能。

例如,假設Spring培訓應用的學生註冊表單是用Velocity模板編寫的。程序清單9.2顯示了registerStudent.vm中的一段,演示如何使用#springBind宏:

 程序清單9.2 在Velocity模板中使用#springBind

#springBind("command.phone")


phone: <input type="text"

name="${status.expression}"

value="http://www.blog.edu.cn/$!status.value">

<font color="#FF0000">${status.errorMessage}</font><br>

#springBind("command.email")


email: <input type="text"

name="${status.expression}"

value="http://www.blog.edu.cn/$!status.value">

<font color="#FF0000">${status.errorMessage}</font><br>

#springBind宏的參數是被綁定表單域的引用路徑。它在模板中設置了一個名爲status的變量用於保存表單域的名稱、值以及可能出現的任何錯誤信息(可能來自一個驗證器)。

如果錯誤信息中包含在HTML中有特殊意義的字符(比如:<,>,&),你可能需要對錯誤信息進行轉義以正確顯示在Web瀏覽器中。在這種情況下,你需要使用宏#springBindEscaped而不是#springBind:

#springBindEscaped("command.email", true)

除了域的引用路徑之外,#springBindEscaped宏接受一個boolean參數,表明是否需要對錯誤信息中的HTML特殊字符進行轉義。如果該參數爲false,則宏#springBindEscaped和#springBind的行爲完全一樣,HTML特殊字符不會被轉義。

爲了在模板中使用Spring的宏,你需要通過VelocityViewResolver的exposeSpringMacroHelpers來使用這些宏:

<bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">



<property name="exposeSpringMacroHelpers">

<value>true</value>

</property>

</bean>

通過把exposeSpringMacroHelpers屬性設爲true,你就能在Velocity模板中使用#springBind和#springBindEscaped宏。

儘管Velocity是一種廣泛使用的JSP的替代技術,它不是惟一可以使用的替代模板技術。FreeMarker是另一種廣爲人知的用於在MVC應用的視圖層中替代JSP的模板語言
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章