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