Spring实战第六章学习笔记————渲染Web视图
Spring实战第六章学习笔记————渲染Web视图
理解视图解析
在之前所编写的控制器方法都没有直接产生浏览器所需的HTML。这些方法只是将一些数据传入到模型中然后再将模型传递给一个用来渲染的视图。尽管我们编写了几个JSP视图但控制器不关心这些。
将控制器请求处理的逻辑和视图中的渲染实现解耦是SpringMVC的一个重要特性。而控制器只通过逻辑视图名来了解视图,这时就需要Spring视图解析器了。
SpringMVC定义了一个名为ViewResolver的接口,大致如下所示:
public interface ViewResolver {
View resolveViewName(String viewName, Locale locale) throws Exception;
}
当给resolveViewName()方法传入一个视图名和locale对象时,它会返回一个View实例。View是另一个接口,如下所示:
public interface View {
String getContentType();
void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
View接口的任务是接受模型以及Servlet的request对象和response对象,并将结果渲染到response中。但一般来讲我们不需要关心这些接口。Spring提供了多个内置的实现。
| 视图解析器 | 描述 |
|---|---|
| BeanNameViewResolver | 在Spring的application context中的bean中查找与视图名称相同id |
| ContentNegotiatingViewResolver | 委托给一个或多个人视图解析器,而选择哪一个取决于请求的内容类型 |
| FreeMarkerViewResolver | 查找一个基于FreeMarker的模版 |
| InternalResourceViewResolver | 在web应用程序的war文件中查找视图 |
| JasperReportsViewResolver | 解析为JasperReport报表文件 |
| ResourceBundleViewResolver | 根据属性文件(properties file)查找View实现 |
| TilesViewResolver | 通过Tiles模版定义的模版解析,模版的名称与视图名称相同 |
| UrlBasedViewResolver | 根据视图名称直接解析,当视图名称与物理视图名称匹配时 |
| VelocityLayoutViewResolver | 解析为从不同的Velocity模版组成的Velocity布局 |
| VelocityViewResolver | 解析为Velocity模版 |
| XmlViewResolver | 根据XML文件(/WEB_INF/views.xml)中声明的View实现进行解析,与BeanNameViewResolver类似 |
| XsltViewResolver | 基于XSLT视图解析 |
对于表中大部分的解析器,每一项都对应了JavaWeb应用中特定的某种视图技术。InternalResourceViewResolveryibanyongyuJSP,TilesViewResolver用于ApacheTiles视图。而FreeMarkerViewResolver和VelocityViewResolver分别对应FreeMarker和Vwlocity模板视图。
创建JSP视图
Spring提供了两种支持JSP视图的方式:
- InternalResourceViewResolver:可以将视图名称解析到JSP文件。另外,对JSP中使用的JSTL(JavaServer Pages Standard Tag Library)标签也提供了支持。
- Spring提供了两种JSP标签库,一种是form-to-model绑定,另外一种则提供基本的功能。
配置适用于JSP的视图解析器
InternalResourceViewResolver会遵循一种约定,会在视图名上添加前缀和后缀,进而确定一个Web应用中视图资源的物理路径。如图所示:
当使用@Bean注解时,Spring会这样配置InternalResourceViewResolver,使其遵守上述的约定:
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
另外,如果采用基于XML的Spring配置,也可以通过如下方式进行配置:
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/views/"
p:suffix=".jsp" />
当JSP页面使用JSTL标签时,就需要配置InternalResourceViewResolver将视图解析为JstlView。
JSTL格式化标签需要一个Locale来正确地格式化一些特定语言环境的值,如日期和币种。消息标签可以使用Spring消息源和Locale来正确地选中消息并解析到HTML。
要解析JstlView,需要在InternalResourceViewResolver中设置viewClass属性:
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
使用Spring的JSP库
Spring提供了两个JSP标签库,用来帮助定义Spring MVC Web的视图。其中一个标签库会用来渲染HTML表单标签,这些标签可以绑定model中的某个属性。另外一个标签库包含了一些工具类标签。
表单绑定到模型上
Spring的表单绑定JSP标签库包含了14个标签,它们中的大多数都用来渲染HTML中的表单标签。但是,它们与原生HTML标签的区别在于它们会绑定模型中的一个对象,能够根据模型中对象的属性填充值。标签库中还包含了一个为用户展现错误的标签,它会将错误信息渲染到最终的HTML之中。
使用表单绑定库,需要在JSP页面中对其进行声明:
<%@taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>
或
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
| JSP标签 | 描 述 |
|---|---|
| <sf:checkbox> | 渲染成一个HTML <input>标签,其中type属性设置为checkbox |
| <sf:checkboxes> | 渲染成多个HTML <input>标签,其中type属性设置为checkbox |
| <sf:errors> | 在一个HTML <span>中渲染输入域的错误 |
| <sf:form> | 渲染成一个HTML <form>标签,并为其内部标签暴露绑定路径,用于数据绑定 |
| <sf:hidden> | 渲染成一个HTML <input>标签,其中type属性设置为hidden |
| <sf:input> | 渲染成一个HTML <input>标签,其中type属性设置为text |
| <sf:label> | 渲染成一个HTML <label>标签 |
| <sf:option> | 渲染成一个HTML <option>标签,其selected属性根据所绑定的值进行设置 |
| <sf:options> | 按照绑定的集合、数组或Map,渲染成一个HTML <option>标签的列表 |
| <sf:password> | 渲染成一个HTML <input>标签,其中type属性设置为password |
| <sf:radiobutton> | 渲染成一个HTML <input>标签,其中type属性设置为radio |
| <sf:radiobuttons> | 渲染成多个HTML <input>标签,其中type属性设置为radio |
| <sf:select> | 渲染为一个HTML <select>标签 |
| <sf:textarea> | 渲染为一个HTML <textarea>标签 |
选几个标签写出以下代码:
<sf:form method="POST" commandName="spitter">
First Name: <sf:input path="firstName" /><br/>
Last Name: <sf:input path="lastName" /><br/>
Email: <sf:input path="email" /><br/>
Username: <sf:input path="username" /><br/>
Password: <sf:password path="password" /><br/>
<input type="submit" value="Register" />
</sf:form>
sf:form会通过commandName属性构建针对某个模型对象的上下文信息。在其他的表单绑定标签中,会引用这个模型对象的属性。这样模型中必须有一个Spitter对象。所以我们要修改一下SpitterController:
@RequestMapping(value = "/register", method = RequestMethod.GET)
public String showRegistrationForm(Model model) {
model.addAttribute(new Spitter());
return "registerForm";
}
错误信息展示
当存在错误时,请求中会包含错误的信息,这些信息是与模型数据放到一起的。我们应该将这些数据抽取出来展现给用户。
First Name: <sf:input path="firstName" />
<sf:errors path="firstName" cssClass="error"/><br/>
这里将<sf:errors>的path属性设置为firstName,那么就会展示Spitter model对象的firstName的验证错误信息,如果没有错误,那么就不会对其进行解析。如果有,会将其解析为<span>标签。
Spring通用的标签库
处理表单绑定标签库之外,Spring还提供了一个跟基本的JSP标签库。要使用该标签库,需要在页面中做以下声明:
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
声明后可以和使用如下标签:
| JSP标签 | 描述 |
|---|---|
| <s:bind> | 通常和form一起用,用以指明表单要提交到哪个类或类的属性中去 |
| <spring:escapeBody> | 对标签中的内容进行转义处理 |
| <spring:hasBindErrors> | 用于将特定对象(request属性中)中绑定的的errors解析出来 |
| <spring:htmlEscape> | 设置当前页面的默认的HTML转义值 |
| <spring:message> | 根据code取得消息资源,并将其解析为一个page、request、session或者application范围的变量(由var或者scope属性指定) |
| <spring:nestedpath> | 为<spring:bind>配置嵌套路径 |
| <s:theme> | 与<spring:message>相同,只不过处理的是theme消息 |
| <spring:transform> | 来转换表单中不与bean中的属性一一对应的那些属性 |
| <s:url> | 与<spring:message>相同,只不过处理的是URI模版变量 |
| <s:eval> | 与<spring:message>相同,只不过处理的是SpEL表达式 |
展现国际化消息
对于渲染文本来说我们可以借助<s:messsage>让文本位于一个或多个属性文件。
<<h1><s:message code="spittr.welcome" /></h1>
这里<s:message>标签会从某个属性文件中根据key值spittr.welcome读取对应的文本并解析到页面中,在这之前需要对这个key-value进行配置。Spring有一些实现自MessageSource接口的消息源类,其中一个比较常用的就是ResourceBundleMessageSource,它可以从properties文件中加载消息,下面的@Bean方法对该类进行了配置:
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
其中的关键在于设置了basename属性,之后ResourceBundleMessageSource就可以对classpath根路径下相对应的的properties文件进行解析。
另外,还可以使用ReloadableResourceBundleMessageSource,它可以在不重新编译或者重启项目的情况下重新加载消息属性:
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("file:///etc/spittr/messages");
messageSource.setCacheSeconds(10);
return messageSource;
}
与ResourceBundleMessageSource主要的区别在于basename的设置,这里可以将其设置为classpath(需要前缀classpath:)、文件系统(file:)或者项目的根目录(不加任何前缀)。上面代码中设置的就是在文件系统中的/etc/spittr目录中查找文件名称为messages的文件。
下面创建一个名为messages.properties的文件,并添加如下内容:
spittr.welcome=Welcome to Spittr!
创建URL
<s:url>是一个很小的标签,它主要的任务是创建URL,然后将其赋值到一个变量或渲染到响应中。
按照其最简单的形式,<s:url>标签需要一个servlet上下文相关的URL,并对其进行解析。例如:
<a href="<s:url href="/spitter/register" />">Register</a>
如果servlet上下文是spittr,那么上面的链接将会被解析为:
<a href="/spittr/spitter/register">Register</a>
将servlet上下文作为链接前缀添加到目标链接中。而我们还可以用<s:url>创建URL,并将其赋值给一个变量供模板再稍后使用。
<s:url href="/spitter/register" var="registerUrl" />
<a href="${registerUrl}">Register</a>
默认情况下,URL变量是page范围内的。但是也可以通过设置scope属性将其设置为application、session或者request范围内的:
<s:url href="/spitter/register" var="registerUrl" scope="request" />
如果想为URL添加参数,可以通过<s:param>标签进行添加。例如,下面的<s:url>标签通过<s:param>为/spittles设置了max和count属性:
<s:url href="/spittles" var="spittlesUrl">
<s:param name="max" value="60" />
<s:param name="count" value="20" />
</s:url>
使用Apache Tiles视图定义布局
对于WEB页面的布局问题我们通常都会用布局引擎如Apache Tiles来解决。SpringMVC以视图解析器的形式对Apache Tiles提供了支持。
配置Tiles视图解析器
为了在Spring中使用Tiles,需要配置几个bean。我们需要一个Tilesconfigurer bean,
它会负责定位和加载Tile定义并协调生成Tiles。除此之外,还需要TileViewResolver bean将逻辑视图解析为Tile定义。首先配置TilesConfigurer来解析定义。
@Bean
public TilesConfigurer tilesConfigurer() {
TilesConfigurer tiles = new TilesConfigurer();
// 指定tile定义的位置
tiles.setDefinitions(new String[] { "/WEB-INF/layout/tiles.xml" });
// 开启刷新
tiles.setCheckRefresh(true);
return tiles;
}
当配置TilesConfigurer的时候,最重要的属性就是definitions。这个属性接受一个String类型的数组,其中每个条目都指定一个Tile定义的XML文件。接下来配置TileViewResolver :
@Bean
public ViewResolver tilesViewResolver(){
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.xml</value>
<value>/WEB-INF/views/**/tiles.xml</value>
</list>
</property>
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.tiles3.TilesViewResolver" />
定义Tiles
Apache Tiles提供了一个文档类型定义(document type definition,DTD),用来在XML文件中指定Tile的定义。每个定义由
<?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>
<!-- 定义一个基础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">
<put-attribute name="body" value="/WEB-INF/views/home.jsp" />
</definition>
<definition name="registerForm" extends="base">
<put-attribute name="body" value="/WEB-INF/views/registerForm.jsp" />
</definition>
<definition name="profile" extends="base">
<put-attribute name="body" value="/WEB-INF/views/profile.jsp" />
</definition>
<definition name="spittles" extends="base">
<put-attribute name="body" value="/WEB-INF/views/spittles.jsp" />
</definition>
<definition name="spittle" extends="base">
<put-attribute name="body" value="/WEB-INF/views/spittle.jsp" />
</definition>
</tiles-definitions>
每个
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="s"%>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="t"%>
<%@ page session="false"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<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>
其中的关键在于如何使用<t:insertAttribute>标签从Tile标签库插入到其他模版中。使用该标签来引入header、body、footer属性,最终的布局如下图所示:
其中,header和footer属性在tile定义文件中分别指明了,但是body属性呢?base tile的主要作用是作为一个基础模版用来作为其他tile定义扩展使用的。那么扩展了base的tile就继承了base的header和footer属性(也可以进行重写)。它们自己也设置了一个body属性用来引用一个JSP模版。
使用Thymeleaf
略(之后再补充)
浙公网安备 33010602011771号