?
本文檔使用 PHP中文網(wǎng)手冊(cè) 發(fā)布
Velocity 和FreeMarker 是兩種模板語(yǔ)言,都可以做為view層技術(shù)在Spring MVC 應(yīng)用中使用。 它們的語(yǔ)言風(fēng)格和適用對(duì)象都很相似,這里把它們放在一起討論。至于它們語(yǔ)義和語(yǔ)法上的不同,可以參考 FreeMarker站點(diǎn)。
使用Velocity或FreeMarker需要包含
velocity-1.x.x.jar
或
freemarker-2.x.jar
。另外Velocity還需要
commons-collections.jar
。一般把這些jar包放在
WEB-INF/lib
下,這樣可以保證J2EE Server找到它們并加到web應(yīng)用的classpath下。這里同樣假設(shè)你的
'WEB-INF/lib'
目錄下已有
spring.jar
!Spring的發(fā)布包中已經(jīng)提供了最新的穩(wěn)定版本的Velocity、FreeMarker和commons
collections,可以從相應(yīng)的
/lib/
子目錄下得到。如果你想在Velocity中使用Spring的dateToolAttribute或numberToolAttribute,那你還需要
velocity-tools-generic-1.x.jar
通過在
'*-servlet.xml'
中增加相關(guān)的配置bean,可以初始化相應(yīng)的配置,如下:
<!-- 該bean使用一個(gè)存放模板文件的根路徑來(lái)配置Velocity環(huán)境。你也可以通過指定一個(gè)屬性文件來(lái)更精細(xì)地控制Velocity,但對(duì)基于文件的模板載入來(lái)說(shuō),默認(rèn)的方式已相當(dāng)健全 --> <bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer"> <property name="resourceLoaderPath" value="/WEB-INF/velocity/"/> </bean> <!-- 也可以把ResourceBundle或XML文件配置到視圖解析器中。如果你需要根據(jù)Locale來(lái)解析不同的視圖,就需要使用resource bundle解析器。 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver"> <property name="cache" value="true"/> <property name="prefix" value=""/> <property name="suffix" value=".vm"/> </bean>
<!-- freemarker config --> <bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="templateLoaderPath" value="/WEB-INF/freemarker/"/> </bean> <!-- 也可以把ResourceBundle或XML文件配置到視圖解析器中。如果你需要根據(jù)Locale來(lái)解析不同的視圖,就需要使用resource bundle解析器。 --> <bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> <property name="cache" value="true"/> <property name="prefix" value=""/> <property name="suffix" value=".ftl"/> </bean>
對(duì)于非web應(yīng)用,你需要在application context的配置文件中聲明
VelocityConfigurationFactoryBean
或者
FreeMarkerConfigurationFactoryBean
模板文件需要存放在配置*Configurer
bean時(shí)所指定的目錄下,就像上面的例子所示。
這里不準(zhǔn)備詳細(xì)敘述使用這兩種語(yǔ)言創(chuàng)建模板的細(xì)節(jié),你可以參考相應(yīng)的站點(diǎn)獲取那些信息。
如果你用了我們推薦的視圖解析器,你會(huì)發(fā)現(xiàn)從邏輯視圖名到相應(yīng)模板文件的映射方式與使用
InternalResourceViewResolver
處理JSP時(shí)的映射方式類似。比如若你的控制器返回了ModelAndView對(duì)象,其中包含一個(gè)叫做“welcome”的視圖名,則視圖解析器將試圖查找
/WEB-INF/freemarker/welcome.ftl
或/WEB-INF/velocity/welcome.vm
作為合適的模板。
以上著重介紹的基本配置適合大部分應(yīng)用需求,然而仍然有一些不常見的或高級(jí)需求的情況,Spring提供了另外的配置選項(xiàng)來(lái)滿足這種需求。
這個(gè)文件是可選的,不過一旦指定,其所包含的值即影響Velocity運(yùn)行時(shí)狀態(tài)。只有當(dāng)你要做一些高級(jí)配置時(shí)才需要這個(gè)文件,這時(shí)你可以在上面定義的
VelocityConfigurer
中指定它的位置。
<bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer"> <property name="configLocation value="/WEB-INF/velocity.properties"/> </bean>
另一種方法,你可以直接在Velocity config bean的定義中指定velocity屬性,來(lái)取代“configLocation”屬性。
<bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer"> <property name="velocityProperties"> <props> <prop key="resource.loader">file</prop> <prop key="file.resource.loader.class"> org.apache.velocity.runtime.resource.loader.FileResourceLoader </prop> <prop key="file.resource.loader.path">${webapp.root}/WEB-INF/velocity</prop> <prop key="file.resource.loader.cache">false</prop> </props> </property> </bean>
關(guān)于Spring中Velocity的配置請(qǐng)參考
API文檔或者參考Velocity自身文檔中的例子和定義來(lái)了解如何配置
'velocity.properties'
文件。
FreeMarker的'Settings'和'SharedVariables'配置可以通過直接設(shè)置
FreeMarkerConfigurer
的相應(yīng)屬性來(lái)傳遞給Spring管理的FreeMarker
Configuration
對(duì)象,其中
freemarkerSettings
屬性需要一個(gè)java.util.Properties
類型對(duì)象,
freemarkerVariables
需要一個(gè)
java.util.Map
類型對(duì)象。
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> <property name="templateLoaderPath" value="/WEB-INF/freemarker/"/> <property name="freemarkerVariables"> <map> <entry key="xml_escape" value-ref="fmXmlEscape"/> </map> </property> </bean> <bean id="fmXmlEscape" class="freemarker.template.utility.XmlEscape"/>
關(guān)于settings和variables如何影響
Configuration
對(duì)象的細(xì)節(jié)信息,請(qǐng)參考FreeMarker的文檔。
Spring提供了一個(gè)在JSP中使用的標(biāo)簽庫(kù),其中包含一個(gè)
<spring:bind/>
標(biāo)簽,它主要用來(lái)在表單中顯示支持對(duì)象(譯者注:即表單數(shù)據(jù)傳輸對(duì)象)的數(shù)據(jù),并在一個(gè)
Validator
(工作在Web層或業(yè)務(wù)邏輯層)校驗(yàn)失敗時(shí)顯示失敗信息。
從1.1版本開始,Spring為Velocity和FreeMarker也提供了同樣的功能,而且還另外提供了便于使用的宏,用來(lái)生成表單輸入元素。
spring.jar
文件為這兩種語(yǔ)言維護(hù)了一套標(biāo)準(zhǔn)宏,對(duì)于正確配置的應(yīng)用,它們總是可用的。
在Spring庫(kù)中定義的一些宏被認(rèn)為是內(nèi)部的(私有的),但當(dāng)宏定義中不存在這樣的范圍界定時(shí),
會(huì)使得所有的宏均是可見的,能夠任意調(diào)用代碼和用戶模板。下面的一節(jié)將集中討論模板中可以直接調(diào)用的宏。
如果希望直接查看宏的代碼,它們分別是
org.springframework.web.servlet.view.velocity
包中的spring.vm文件和
org.springframework.web.servlet.view.freemarker
包中的spring.ftl文件。
在扮演Spring表單控制器對(duì)應(yīng)視圖的html表單(或vm/ftl模板)里,
你可以模仿下面的代碼來(lái)綁定表單數(shù)據(jù)并顯示錯(cuò)誤信息(和JSP的形式非常相似)。
注意默認(rèn)情況下命令對(duì)象的名字是“command”,你可以在配置自己的表單控制器時(shí)通過設(shè)置'commandName'屬性來(lái)覆蓋默認(rèn)值。
例子代碼如下(其中的personFormV
和personFormF
是前面定義的視圖):
<!-- velocity宏自動(dòng)可用 --> <html> ... <form action="" method="POST"> Name: #springBind( "command.name" ) <input type="text" name="${status.expression}" value="$!status.value" /><br> #foreach($error in $status.errorMessages) <b>$error</b> <br> #end <br> ... <input type="submit" value="submit"/> </form> ... </html>
<!-- FreeMarker宏必須導(dǎo)入到一個(gè)名稱空間,這里推薦你定義為'spring'空間 --> <#import "spring.ftl" as spring /> <html> ... <form action="" method="POST"> Name: <@spring.bind "command.name" /> <input type="text" name="${spring.status.expression}" value="${spring.status.value?default("")}" /><br> <#list spring.status.errorMessages as error> <b>${error}</b> <br> </#list> <br> ... <input type="submit" value="submit"/> </form> ... </html>
#springBind
/
<@spring.bind>
需要一個(gè)'path'屬性,格式為命令對(duì)象的名字(默認(rèn)值為'command',
除非你在配置FormController的屬性時(shí)改變它)后跟圓點(diǎn)再加上你希望綁定到的命令對(duì)象的屬性名。
你也可以使用類似“command.address.street”的格式來(lái)處理嵌套對(duì)象。使用
bind
宏時(shí),HTML轉(zhuǎn)碼行為由web.xml中名為
defaultHtmlEscape
的ServletContext參數(shù)指定。
上述宏的另一種可選形式是
#springBindEscaped
/
<@spring.bindEscaped>
,它另外接受一個(gè)布爾型參數(shù),顯式指定了輸出值或錯(cuò)誤信息這些狀態(tài)信息時(shí)是否使用HTML轉(zhuǎn)碼。
附加的表單處理宏簡(jiǎn)化了HTML轉(zhuǎn)碼的使用,只要有可能,你就應(yīng)該使用它們。關(guān)于它們的細(xì)節(jié)將在下節(jié)講述。
為這兩種語(yǔ)言附加的一些很方便的宏同時(shí)簡(jiǎn)化了表單綁定和表單生成(包括顯示校驗(yàn)錯(cuò)誤信息)。 不需要使用這些宏來(lái)生成表單輸入域,它們可以被混雜并匹配到簡(jiǎn)單HTML,或者直接調(diào)用前面講過的spring綁定宏。
下表展示了可用的宏的VTL定義和FTL定義,以及它們需要的參數(shù)。
表?14.1.?宏定義表
macro | VTL definition | FTL definition |
---|---|---|
message (輸出一個(gè)根據(jù)code參數(shù)選擇的資源綁定字符串) |
#springMessage($code)
|
<@spring.message code/>
|
messageText (輸出一個(gè)根據(jù)code參數(shù)選擇的資源綁定字符串,找不到的話輸出default參數(shù)的值) |
#springMessageText($code $text)
|
<@spring.messageText code,
text/>
|
url (在URL相對(duì)路徑前面添加應(yīng)用上下文根路徑application context root) |
#springUrl($relativeUrl)
|
<@spring.url relativeUrl/>
|
formInput (標(biāo)準(zhǔn)表單輸入域) |
#springFormInput($path
$attributes)
|
<@spring.formInput path,
attributes, fieldType/>
|
formHiddenInput * (表單隱藏輸入域) |
#springFormHiddenInput($path
$attributes)
|
<@spring.formHiddenInput
path, attributes/>
|
formPasswordInput *(標(biāo)準(zhǔn)表單密碼輸入域;注意不會(huì)為這種類型的輸入域裝配數(shù)據(jù)) |
#springFormPasswordInput($path
$attributes)
|
<@spring.formPasswordInput
path, attributes/>
|
formTextarea (大型文本(自由格式)輸入域) |
#springFormTextarea($path
$attributes)
|
<@spring.formTextarea path,
attributes/>
|
formSingleSelect (單選列表框) |
#springFormSingleSelect( $path
$options $attributes)
|
<@spring.formSingleSelect
path, options, attributes/>
|
formMultiSelect (多選列表框) |
#springFormMultiSelect($path
$options $attributes)
|
<@spring.formMultiSelect
path, options, attributes/>
|
formRadioButtons (單選框) |
#springFormRadioButtons($path
$options $separator $attributes)
|
<@spring.formRadioButtons
path, options separator,
attributes/>
|
formCheckboxes (復(fù)選框) |
#springFormCheckboxes($path
$options $separator $attributes)
|
<@spring.formCheckboxes path,
options, separator,
attributes/>
|
showErrors (簡(jiǎn)化針對(duì)所綁定輸入域的校驗(yàn)錯(cuò)誤信息輸出) |
#springShowErrors($separator
$classOrStyle)
|
<@spring.showErrors
separator, classOrStyle/>
|
* 在FTL(FreeMarker)中,這二種宏實(shí)際上并不是必需的,因?yàn)槟憧梢允褂闷胀ǖ? formInput
宏,指定fieldType
參數(shù)的值為 'hidden
' 或 'password
'即可 。
上面列出的所有宏的參數(shù)都具有一致的含義,如下述:
path:待綁定屬性的名字(如:command.name)
選項(xiàng):一個(gè)Map,其中保存了所有可從輸入域中選擇的值。map中的鍵值(keys)代表將從表單綁定到命令對(duì)象然后提交到后臺(tái)的實(shí)值(values)。存儲(chǔ)在Map中的與相應(yīng)鍵值對(duì)應(yīng)的對(duì)象就是那些在表單上顯示給用戶的標(biāo)簽,它們可能與提交到后臺(tái)的值不同。通常這樣的map由控制器以引用數(shù)據(jù)的方式提供。你可以根據(jù)需求的行為選擇一種Map實(shí)現(xiàn)。比如對(duì)順序要求嚴(yán)格時(shí),可使用一個(gè)
SortedMap
,如一個(gè)TreeMap
加上適當(dāng)?shù)腃omparator;對(duì)要求按插入順序返回的情況,可以使用commons-collections提供的
LinkedHashMap
或LinkedMap
。
分隔符:當(dāng)使用多選的時(shí)候(radio buttons 或者 checkboxes),用于在列表中分隔彼此的字符序列(如 "<br>")。
屬性:一個(gè)附加的以任意標(biāo)簽或文本構(gòu)成的字符串,出現(xiàn)在HTML標(biāo)簽內(nèi)。該字符串被宏照原樣輸出。例如:在一個(gè)textarea標(biāo)簽內(nèi)你可能會(huì)提供'rows="5" cols="60"'這樣的屬性,或者你會(huì)傳遞'style="border:1px solid silver"'這樣的樣式信息。
classOrStyle:供showErrors宏用來(lái)以這種樣式顯示錯(cuò)誤信息,其中錯(cuò)誤信息嵌套于使用該CSS類名的span標(biāo)簽內(nèi)。如果不提供或內(nèi)容為空,則錯(cuò)誤信息嵌套于<b></b>標(biāo)簽內(nèi)。
宏的例子在下面描述,其中一些是FTL的,一些是VTL的。兩種語(yǔ)言之間的用法差別在旁注中解釋。
<!-- 上面提到的Name域的例子,使用VTL中定義的表單宏 --> ... Name: #springFormInput("command.name" "")<br> #springShowErrors("<br>" "")<br>
formInput宏接受一個(gè)path參數(shù)(command.name)和一個(gè)附加的屬性參數(shù)(在上例中為空)。該宏與所有其他表單生成宏一樣,對(duì)path參數(shù)代表的屬性實(shí)施一種隱式綁定,這種綁定保持有效狀態(tài)直到一次新的綁定開始,所以showErrors宏不再需要傳遞path參數(shù)――它簡(jiǎn)單地操作最近一次綁定的屬性(field)。
showErrors宏接受兩個(gè)參數(shù):分隔符(用于分隔多條錯(cuò)誤信息的字符串)和CSS類名或樣式屬性。注意在FreeMarker中可以為屬性參數(shù)指定默認(rèn)值(這點(diǎn)兒Velocity做不到)。上面的兩個(gè)宏調(diào)用在FTL中可以這么表達(dá):
<@spring.formInput "command.name"/> <@spring.showErrors "<br>"/>
上面展示的用于生成name表單輸入域的代碼片斷產(chǎn)生的輸出如下,同時(shí)還顯示了輸入值為空的情況下提交表單后產(chǎn)生的校驗(yàn)錯(cuò)誤信息(校驗(yàn)過程由Spring的驗(yàn)證框架提供)。
生成的HTML如下:
Name: <input type="text" name="name" value="" > <br> <b>required</b> <br> <br>
參數(shù)(屬性)用來(lái)向textarea傳遞樣式信息或行列數(shù)屬性。
有四種用于在HTML表單中生成通用選擇輸入框的宏。
formSingleSelect
formMultiSelect
formRadioButtons
formCheckboxes
每個(gè)宏都將接受一個(gè)由選項(xiàng)值和選項(xiàng)標(biāo)簽的集合構(gòu)成的Map,其中選項(xiàng)值和其標(biāo)簽可以相同。
下面展示了一個(gè)在FTL中使用radio按鈕的例子。表單支撐對(duì)象(form backing object)提供了一個(gè)默認(rèn)值'London',所以該域不需要校驗(yàn)。當(dāng)渲染表單時(shí),整個(gè)待展現(xiàn)的城市列表由模型對(duì)象的'cityMap'屬性以引用數(shù)據(jù)的方式提供。
... Town: <@spring.formRadioButtons "command.address.town", cityMap, "" /><br><br>
這將產(chǎn)生一行radio按鈕――
cityMap
中一個(gè)值對(duì)應(yīng)一個(gè)按鈕,并以""分隔。沒有額外的屬性,因?yàn)楹甑淖詈笠粋€(gè)參數(shù)不存在。cityMap中所有的key-value都使用String類型值。map中的key用作輸入域的值(將被作為請(qǐng)求參數(shù)值提交到后臺(tái)),value用作顯示給用戶的標(biāo)簽。上述示例中,表單支撐對(duì)象提供了一個(gè)默認(rèn)值以及三個(gè)著名城市作為可選值,它產(chǎn)生的HTML代碼如下:
Town: <input type="radio" name="address.town" value="London"> London <input type="radio" name="address.town" value="Paris" checked="checked"> Paris <input type="radio" name="address.town" value="New York"> New York
如果你希望在應(yīng)用中按照內(nèi)部代碼來(lái)處理城市,你得以適當(dāng)?shù)逆I值創(chuàng)建map,如下:
protected Map referenceData(HttpServletRequest request) throws Exception { Map cityMap = new LinkedHashMap(); cityMap.put("LDN", "London"); cityMap.put("PRS", "Paris"); cityMap.put("NYC", "New York"); Map m = new HashMap(); m.put("cityMap", cityMap); return m; }
現(xiàn)在上述代碼將產(chǎn)生出以相關(guān)代碼為值的radio按鈕,同時(shí)你的用戶仍能看到對(duì)他們顯示友好的城市名。
Town: <input type="radio" name="address.town" value="LDN"> London <input type="radio" name="address.town" value="PRS" checked="checked"> Paris <input type="radio" name="address.town" value="NYC"> New York
缺省情況下使用上面這些宏將產(chǎn)生符合HTML 4.01標(biāo)準(zhǔn)的標(biāo)簽,并且Spring的綁定支持使用web.xml中定義的HTML轉(zhuǎn)碼行為。為了產(chǎn)生符合XHTML標(biāo)準(zhǔn)的標(biāo)簽以及覆蓋默認(rèn)的HTML轉(zhuǎn)碼行為,你可以在你的模板(或者模板可見的模型對(duì)象)中指定兩個(gè)變量。在模板中指定的好處是稍后的模板處理中可以為表單中不同的域指定不同的行為。
要切換到符合XHTML的輸出,你可以設(shè)置model/context變量xhtmlCompliant的值為true:
## for Velocity.. #set($springXhtmlCompliant = true) <#-- for FreeMarker --> <#assign xhtmlCompliant = true in spring>
在進(jìn)行完這些處理之后,由Spring宏產(chǎn)生的所有標(biāo)簽都符合XHTML標(biāo)準(zhǔn)了。
類似地,可以為每個(gè)輸入域指定HTML轉(zhuǎn)碼行為:
<#-- 該句覆蓋默認(rèn)HTML轉(zhuǎn)碼行為 --> <#assign htmlEscape = true in spring> <#-- next field will use HTML escaping --> <@spring.formInput "command.name" /> <#assign htmlEscape = false in spring> <#-- all future fields will be bound with HTML escaping off -->