Spring 4 官方文档学习View技术
Posted LarryZeal
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring 4 官方文档学习View技术相关的知识,希望对你有一定的参考价值。
关键词:view technology、template、template engine、markup。内容较多,按需查用即可。
- 介绍
- Thymeleaf
- Groovy Markup Templates
- Velocity & FreeMarker
- JSP & JSTL
- Script templates
- XML Marshalling View(暂空)
- Tiles(暂空)
- XSLT(暂空)
- Document views (PDF/Excel)(暂空)
- JasperReports(暂空)
- Feed Views(暂空)
- JSON Mapping View(暂空)
- XML Mapping View (暂空)
Spring 有很多优越的地方,其中一个就是将view技术与MVC框架的其他部分相隔离。例如,在JSP存在的情况下使用Groovy Markup Templates 还是使用Thymeleaf,仅仅是一个配置问题。
本章覆盖了主要的view技术,嗯嗯,可以与Spring结合的那些,并简明的说明了如何增加新的view技术。
本章假定你已经熟悉了Spring 4 官方文档学习(十一)Web MVC 框架之resolving views 解析视图 -- 它覆盖了views如何耦合到MVC框架的基础。
Thymeleaf是一个非常好的例子:view技术完美的嵌入MVC框架中。该集成的支持不是由Spring团队提供的,而是由Thymeleaf团队提供的。
为Spring配置Thymeleaf,只需要定义几个beans,例如一个ServletContextTemplateResolver、一个SpringTemplateEngine、一个ThymeleafViewResolver。 详见Thymeleaf+Spring。
Groovy Markup Template Engine 是Spring支持的另一个view技术。该模板引擎的主要目标是生成 类XML (XML, Xhtml, HTML5,...)的标记,也可以被用于生成任意基于文本的内容。
嗯嗯,要求classpath中有 Groovy 2.3.1+。
配置Groovy Markup Template Engine 非常简单:
@Configuration @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter { @Override public void configureViewResolvers(ViewResolverRegistry registry) { registry.groovy(); } @Bean public GroovyMarkupConfigurer groovyMarkupConfigurer() { GroovyMarkupConfigurer configurer = new GroovyMarkupConfigurer(); configurer.setResourceLoaderPath("/WEB-INF/"); return configurer; } }
使用MVC namespace的XML 也类似:
<mvc:annotation-driven/> <mvc:view-resolvers> <mvc:groovy/> </mvc:view-resolvers> <mvc:groovy-configurer resource-loader-path="/WEB-INF/"/>
不像传统的模板引擎,这个引擎(Groovy)依赖于DSL -- DSL使用了builder syntax。 下面是一个HTML页面的简单模板:
yieldUnescaped \'<!DOCTYPE html>\' html(lang:\'en\') { head { meta(\'http-equiv\':\'"Content-Type" content="text/html; charset=utf-8"\') title(\'My page\') } body { p(\'This is an example of HTML contents\') } }
Velocity 和 FreeMarker是模板语言,可被用作Spring MVC application中的view技术。它们是非常相似的,并且服务于相似的需要,因此本部分将二者放在一起。关于二者的语法和语义的区别,见FreeMarker站点。
自Spring Framework 4.3起,对Velocity的支持已经是deprecated的了,原因是Apache Velocity project已经有6年没有活动的维护了!我们推荐Spring的FreeMarker支持,或者Thymeleaf--其自身带有Spring支持。
如果想使用Velocity或FreeMarker,你的web应用应该包含velocity-1.x.x.jar
or freemarker-2.x.jar
,另外,Velocity还需要commons-collections.jar
。通常它们会被放在 WEB-INF/lib 文件夹中 -- 该位置会由Java EE server自动发现并添加到应用的classpath中。当然,我们也假定你已经添加了spring-webmvc.jar
。另外,如果想在Velocity views中使用Spring的dateToolAttribute 或 numberToolAttribute,还需要添加velocity-tools-generic-1.x.jar
。
一个合适的配置是这样来初始化的--通过在你的 *-servlet.xml 中添加相关的configurer bean definition,如下:
<!-- 这个bean设置了Velocity环境--基于模板的一个root path。也可以在一个properties文件中指定更多的控制,但默认的已经很好了。
This bean sets up the Velocity environment for us based on a root path for templates. Optionally, a properties file can be specified for more control over the Velocity environment, but the defaults are pretty sane for file based template loading. --> <bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer"> <property name="resourceLoaderPath" value="/WEB-INF/velocity/"/> </bean> <!-- View resolvers can also be configured with ResourceBundles or XML files. If you need different view resolving based on Locale, you have to use the resource bundle resolver. --> <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> <!-- View resolvers can also be configured with ResourceBundles or XML files. If you need different view resolving based on Locale, you have to use the resource bundle resolver. --> <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的应用,添加一个 VelocityConfigurationFactoryBean
或 FreeMarkerConfigurationFactoryBean
即可。-- Java-based config >> @Configuration classes。
你的模板需要保存在一个目录中 -- 该目录由上面提到的 *Configurer bean指定!
本文档不涉及创建模板的细节 -- 有兴趣可以去看相关的站点。
如果你使用了上面提到的view resolvers,那么逻辑视图名会关联到模板文件名 -- 类似于InternalResourceViewResolver之于JSP。所以,当你的controller返回了一个包含welcome 视图名的ModelAndView对象时,该resolver会查找 /WEB-INF/freemarker/welcome.ftl
或 /WEB-INF/velocity/welcome.vm
模板。
上面提到的基本配置适用于大多数应用需求,除此之外,还有一些配置选项 -- 适用于不常见或者高级的需求。
velocity.properties
该文件完全是可选的,如果指定了,那其中的值会被传给Velocity runtime,从而配置velocity。只有高级配置时才需要,如果你需要,在VelocityConfigurer bean definition中指定其位置即可。如下:
<bean id="velocityConfig" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer"> <property name="configLocation" value="/WEB-INF/velocity.properties"/> </bean>
或者,也可以这样指定:
<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>
FreeMarker
通过设置FreeMarkerConfigurer bean的properties,即可将FreeMarker的 Settings 和 SharedVariables 可以被直接传给FreeMarker的Configuration对象(由Spring管理)。freemarkerSettings property需要一个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"/>
详见FreeMarker文档。
Spring提供了在JSP中使用的一个标签库,其中有一个<spring:bind/>标签。该标签主要是让form显示来自form backing objects的值、显示Validator校验失败的结果。从版本1.1开始,Spring在Velocity和FreeMarker中支持同样的功能 -- 带有额外的便捷的宏,用于生成form input元素。 -- 几个意思?
spring-webmvc.jar中维持了宏的一个标准集合,可以用于二者(Velocity和FreeMarker)。
Spring库中定义的一些宏被认为是内部的(私有的),但在宏定义中不存在该scope,所以,所有的宏都是可见的。
下面的部分将专注于在模板内直接调用的那些宏。如果你想看一下这些宏代码,它们位于 org.springframework.web.servlet.view.velocity
或 org.springframework.web.servlet.view.freemarker
包中,名字是spring.vm / spring.ftl。
在你的HTML forms (vm / ftl 模板)中,你可以使用类似下面的代码来为每一个input field绑定field values、显示错误信息 -- 类似于JSP。嗯嗯,此时这些HTML forms (vm / ftl 模板)是作为Spring MVC controller的一种form view。 -- 明明是模板形式的HTML表单!
下面的例子是配合上面提到的 personFormV/personFormF views :
<!-- velocity macros are automatically available --> <html> ... <form action="" method="POST"> Name: #springBind("myModelObject.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 macros have to be imported into a namespace. We strongly recommend sticking to \'spring\' --> <#import "/spring.ftl" as spring/> <html> ... <form action="" method="POST"> Name: <@spring.bind "myModelObject.name"/> <input type="text" name="${spring.status.expression}" value="${spring.status.value?html}"/><br> <#list spring.status.errorMessages as error> <b>${error}</b> <br> </#list> <br> ... <input type="submit" value="submit"/> </form> ... </html>
#springBind
/ <@spring.bind>
需要一个path argument,该argument由你的命令对象的名字(默认为command,可另行指定)、句点、还有你想绑定的field的名字组成。嵌套的field也可以使用,类似于"command.address.street"。bind 宏假定默认的HTML转义行为由web.xml中ServletContext的defaultHtmlEscape 参数指定。
该宏的可选form叫做#springBindEscaped
/ <@spring.bindEscaped>
,会接收第二个参数,并显式的指定HTML转义是否用在status error messages 或 values中。如果设为true或false。额外的form处理宏简化了HTML转义的使用,所以,只要合适就用吧。下一部分会有解释。
form input generation marcos -- 生成表单输入的宏
其他便捷的宏简化了二者绑定和表单的生成(包括校验错误显示)。
只是,使用这些宏来生成表单输入字段永远不是必要,可以混合使用它们,或者直接调用spring bind marcos。
下面的表格是可用宏的表格,列出了VTL和FTL定义,以及其相应的参数列表。
Table 23.1. Table of macro definitions
macro | VTL definition | FTL definition |
---|---|---|
message (output a string from a resource bundle based on the code parameter) |
#springMessage($code) |
<@spring.message code/> |
messageText (output a string from a resource bundle based on the code parameter, falling back to the value of the default parameter) |
#springMessageText($code $text) |
<@spring.messageText code, text/> |
url (prefix a relative URL with the application’s context root) |
#springUrl($relativeUrl) |
<@spring.url relativeUrl/> |
formInput (standard input field for gathering user input) |
#springFormInput($path $attributes) |
<@spring.formInput path, attributes, fieldType/> |
formHiddenInput * (hidden input field for submitting non-user input) |
#springFormHiddenInput($path $attributes) |
<@spring.formHiddenInput path, attributes/> |
formPasswordInput * (standard input field for gathering passwords. Note that no value will ever be populated in fields of this type) |
#springFormPasswordInput($path $attributes) |
<@spring.formPasswordInput path, attributes/> |
formTextarea (large text field for gathering long, freeform text input) |
#springFormTextarea($path $attributes) |
<@spring.formTextarea path, attributes/> |
formSingleSelect (drop down box of options allowing a single required value to be selected) |
#springFormSingleSelect( $path $options $attributes) |
<@spring.formSingleSelect path, options, attributes/> |
formMultiSelect (a list box of options allowing the user to select 0 or more values) |
#springFormMultiSelect($path $options $attributes) |
<@spring.formMultiSelect path, options, attributes/> |
formRadioButtons (a set of radio buttons allowing a single selection to be made from the available choices) |
#springFormRadioButtons($path $options $separator $attributes) |
<@spring.formRadioButtons path, options separator, attributes/> |
formCheckboxes (a set of checkboxes allowing 0 or more values to be selected) |
#springFormCheckboxes($path $options $separator $attributes) |
<@spring.formCheckboxes path, options, separator, attributes/> |
formCheckbox (a single checkbox) |
#springFormCheckbox($path $attributes) |
<@spring.formCheckbox path, attributes/> |
showErrors (simplify display of validation errors for the bound field) |
#springShowErrors($separator $classOrStyle) |
<@spring.showErrors separator, classOrStyle/> |
- 在FTL中,这两个宏实际上是不需要的,因为可以使用常规的formInput marco,指定 hidden或password作为fieldType parameter的值。
上面任意宏的parameters都有一致的含义:
- path:要绑定到的字段的名字 ("command.name")
- options:input field中可以选择的所有可用值组成的Map。--太长,懒得翻译。。。
- separator:多个选项可用时(radio buttons或者checkboxes),字符序列(就是字符串,如<br>)用来将选项隔离。
- attributes:一个额外的字符串 -- 任意标签或包含在HTML标签中的文本组成的字符串。该字符串由宏来显示。例如,在textarea 字段中,你可以提供attributes:rows=”5” cols=”60”,或者传递样式信息--诸如style=”border:1px solid silver”。
- classOrStyle:对showErrors 宏来说,CSS class的名字封装了它要使用的每个error。如果没有提供(或者值是空空),那errors会被封装在<b></b>标签中。
宏的用例:
<!-- the Name field example from above using form macros in VTL --> ... Name: #springFormInput("command.name" "")<br> #springShowErrors("<br>" "")<br>
宏formInput ,会接收path parameter (command.name)和一个额外的attributes parameter -- 上例中是空。该宏,配合其他所有生成表单的宏,共同实现了path parameter隐式地spring bind。该绑定一直持续到新的绑定发生,所以,showErrors宏不需要再次传递path parameter -- 它会直接操作上一次绑定的字段。
宏showErrors,会接收一个separator parameter,并会接收第二个参数,一个class name或style attribute。注意,FreeMarker能够为这些attributes parameter指定默认值,不像Velocity,这两个宏在FTL中这样表示:
<@spring.formInput "command.name"/> <@spring.showErrors "<br>"/>
上述生成的表单的片断如下所示(会生成name字段、会显示校验错误):
Name: <input type="text" name="name" value=""> <br> <b>required</b> <br> <br>
formTextarea宏,其工作方式与formInput宏一致,接收的parameter list也相同。共同地,第二个parameter (attributes) 会用来传递样式信息或该textarea的行列属性。
4个选择字段宏,可用来生成通用UI value selection inputs。
- formSingleSelect
- formMultiSelect
- formRadioButtons
- formCheckboxes
每一个都接收一个options Map,包含了form 字段的值、以及该值相对应的label。value和label可以相同。
下面是一个FTL的radio buttons例子。form backing object指定了该字段的默认值为London,所以,不必需要校验了。当form被渲染时,cities的整个列表会作为名字为cityMap的model中的引用。
... Town: <@spring.formRadioButtons "command.address.town", cityMap, ""/><br><br>
这里会渲染一行radio buttons,cityMap中的每个value都对应一个 -- separator是""。没有提供更多attributes (该宏的最后一个参数缺失)。该map的keys都是form使用POST提交的请求参数,map的values都是用户看到的labels。上面的例子中,有一个由三个城市组成的列表和一个默认值,那HTML会是这样的:
Town: <input type="radio" name="address.town" value="London">London</input> <input type="radio" name="address.town" value="Paris" checked="checked">Paris</input> <input type="radio" name="address.town" value="New York">New York</input>
如果你的应用想使用内部代码来处理cities,如下:
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; }
该code会produce output:
Town: <input type="radio" name="address.town" value="LDN">London</input> <input type="radio" name="address.town" value="PRS" checked="checked">Paris</input> <input type="radio" name="address.town" value="NYC">New York</input>
HTML escaping and XHTML compliance -- HTML转义和XHTML兼容
上面提到的form macros的默认使用,其结果是HTML标签兼容HTML 4.01、使用默认值来HTML转义 -- 定义在web.xml中,被Spring的绑定支持所使用 (--什么鬼,狗屁不通)。
为了兼容XHTML或者想要覆盖默认的HTML转义值,可以在你的模板中(或model中)指定两个变量。在模板中指定它们的优点是它们能够被改成不同的值 -- 在模板处理过程中,从而为form中不同的字段提供不同的行为。
想要将你的标签切换至XHTML兼容的,指定名字为xhtmlCompliant的model/context变量的值为true即可:
# for Velocity.. #set($springXhtmlCompliant = true) <-- for FreeMarker --> <#assign xhtmlCompliant = true in spring>
现在,由Spring macros生成的标签都是XHTML兼容的了。
类似的,HTML转义:
<#-- until this point, default HTML escaping is used --> <#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 -->
Spring为JSP和JSTL views提供了一对开箱即用的解决方案。通过使用定义在WebApplicationContext中的一个普通的view resolver就可以使用JSP或JSTL。当然,你还需要写一些JSPs -- 会自动渲染其view。
设置你的应用来使用JSTL是error的一个常见原因,主要是由不同的servlet spec.、JSP和JSTL版本的混淆导致的。文章 How to Reference and Use JSTL in your Web Application 提供了一个很有用的指导,诸如常见陷阱、如何避免它们。注意,自Spring 3.0 起,最小支持的servlet版本是 2.4 (JSP 2.0, JSTL 1.1),这样会降低混淆的范围。
就像其他所有你集成到Spring的view技术一样,JSPs也需要一个view resolver。开发JSPs时,最常用的view resolver是InternalResourceViewResolver 和 ResourceBundleViewResolver。二者都声明在WebApplicationContext中:
<!-- the ResourceBundleViewResolver --> <bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver"> <property name="basename" value="views"/> </bean> # And a sample properties file is uses (views.properties in WEB-INF/classes): welcome.(class)=org.springframework.web.servlet.view.JstlView welcome.url=/WEB-INF/jsp/welcome.jsp productList.(class)=org.springframework.web.servlet.view.JstlView productList.url=/WEB-INF/jsp/productlist.jsp
如你所见,ResourceBundleViewResolver需要一个properties文件,该文件定义了映射到①一个class和②一个URL的视图名字。使用ResourceBundleViewResolver,你可以混合不同类型的视图 -- 只需要一个解析器!
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <Spring 4 官方文档学习 Spring与Java EE技术的集成20191114 Spring Boot官方文档学习(4.7)