第6章 渲染Web视图
6.1 理解视图解析
ViewResolver视图解析器基类
public interface ViewResolver {
View resolveViewName(String viewName, Locale locale) throws Exception;
}
View接口,接收model以及Servlet的request和response对象,并将输出结果渲染到response中。
public interface View {
String getContentType();
void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception;
}
Spring自带13个视图解析器: 下面是部分解析器:
- InternalResourceViewResolver
- TilesViewResolver
下面未测试:
- XmlViewResolver
- FreeMarkerViewResolver对应FreeMarker
- ResourceBundleViewResolver解析资源Bundle(一般为属性文件)
Thymeleaf一种新来代替JSP的新兴技术。
6.2 创建JSP视图
Spring提供2种支持JSP视图的方式:
- JSTL标准标签库
- JSP标签库
6.2.1 配置适用于JSP的视图解析器
InternalResourceViewResolver基础配置和解析JSTL视图
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
6.2.2 使用Spring的JSP库
将表单绑定模型
表单绑定库:14个标签
- <sf:checkbox>
- <sf:checkboxes>
- <sf:errors>
- <sf:form>
- <sf:hidden>
- <sf:input>
- <sf:label>
- <sf:option>
- <sf:options>
- <sf:password>
- <sf:radiobutton>
- <sf:radiobuttons>
- <sf:select>
- <sf:textarea>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>
程序使用代码:
<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" /><sf:errors path="password" /><br/>
<input type="submit" value="Register" />
</sf:form>
commandName属性设置spitter
@RequestMapping(value="/register", method=RequestMethod.GET)
public String showRegistrationForm(Model model) {
model.addAttribute(new Spitter());
return "registerForm";
}
展现错误
- 方式1:错误内容单独显示
<sf:form method="POST" commandName="spitter">
First Name: <sf:input path="firstName" /><sf:errors path="firstName" /><br/>
Last Name: <sf:input path="lastName" /><sf:errors path="lastName" /><br/>
Email: <sf:input path="email" /><sf:errors path="email" /><br/>
Username: <sf:input path="username" /><sf:errors path="username" /><br/>
Password: <sf:password path="password" /><sf:errors path="password" /><br/>
<input type="submit" value="Register" />
</sf:form>
- 方式2:错误内容集中显示
<sf:form method="POST" commandName="spitter">
<sf:errors path="*" element="div" cssClass="errors" />
<sf:label path="firstName" cssErrorClass="error">First Name</sf:label>
<sf:input path="firstName" cssErrorClass="error"/><br/>
<sf:label path="lastName" cssErrorClass="error">Last Name</sf:label>
<sf:input path="lastName" cssErrorClass="error"/><br/>
<sf:label path="email" cssErrorClass="error">Email</sf:label>
<sf:input path="email" cssErrorClass="error"/><br/>
<sf:label path="username" cssErrorClass="error">Username</sf:label>
<sf:input path="username" cssErrorClass="error"/><br/>
<sf:label path="password" cssErrorClass="error">Password</sf:label>
<sf:password path="password" cssErrorClass="error"/><br/>
<input type="submit" value="Register" />
</sf:form>
使用属性文件:ValidationMessages.properties
firstName.size=First name must be between {min} and {max} characters long.
lastName.size=Last name must be between {min} and {max} characters long.
username.size=Username must be between {min} and {max} characters long.
password.size=Password must be between {min} and {max} characters long.
email.valid=The email address must be valid.
Spitter类中的校验注解添加message属性
package spittr;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class Spitter {
private Long id;
@NotNull
@Size(min=5, max=16, message="{username.size}")
private String username;
@NotNull
@Size(min=5, max=25, message="{password.size}")
private String password;
@NotNull
@Email(message="{email.valid}")
private String email;
@NotNull
@Size(min=2, max=30, message="{firstName.size}")
private String firstName;
@NotNull
@Size(min=2, max=30, message="{lastName.size}")
private String lastName;
public Spitter() {
}
public Spitter(String username, String password, String firstName, String lastName) {
this.username = username;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
}
public Spitter(Long id, String username, String password, String firstName, String lastName) {
this.id = id;
this.username = username;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
}
public Long getId() {
return id;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public void setId(Long id) {
this.id = id;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Spring通用的标签库
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
常用标签:
- <s:message>
- <s:url>
- <s:escapeBody>
展现国际化信息
实现MessageSource接口
- 方法1:ResourceBundleMessageSource
@Bean
public MessageSource mesasgeSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
- 方法2:ReloadableResourceBundleMessageSource
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource
= new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setCacheSeconds(10);
return messageSource;
}
默认属性文件 messages.properties
spittr.welcome=hello, welcome to spitter.
中文属性文件:messages_zh_CN.properties 英文属性文件:messages_en_US.properties
创建URL
<s:url value="/spitter/register">
提供变量var
<s:url value="/spitter/register" var="registerUrl" />
<a href="${registerUrl}">Register</a>
scope属性
<s:url value="/spitter/register" var="registerUrl" scope="request" />
<s:param>
<s:url value="/spittles" var="spittlesUrl">
<s:param name="max" value="60" />
<s:param name="count" value="20" />
</s:url>
路径占位符{}
<s:url value="/spitter/{username}" var="spitterUrl">
<s:param name="username" value="jbauer" />
</s:url>
htmlEscape
<s:url value="/spittles" htmlEscape="true">
<s:param name="max" value="60" />
<s:param name="count" value="20" />
</s:url>
渲染结果:/spittr/spittles?max=60&count=20
javaScriptescape
<s:url value="/spittles" var="spittlesUrl" javaScriptEscape="true">
<s:param name="max" value="60" />
<s:param name="count" value="20" />
</s:url>
<script>
var spittlesUrl = "${spittlesUrl}";
</script>
渲染结果:
<script>
var spittlesUrl = "\/spittr\/spittles?max=60&count=20";
</script>
转义内容
<s:escapeBody>
<s:escapeBody htmlEscape="true">
<h1>Register</h1>
</s:escapeBody>
渲染结果:
<h1>Register</h1>
支持JavaScript转义,
<s:escapeBody javaScriptEscape="true">
<h1>Register</h1>
</s:escapeBody>
渲染结果(?对于结果很疑惑): \n\t\u003Ch1\u003ERegister\u003C/h1\u003E\n\t
使用Apache Tiles视图定义布局
配置Tiles视图解析器
@Bean
public ViewResolver ViewResolver() {
return new TilesViewResolver();
}
@Bean
public TilesConfigurer tilesConfigurer() {
TilesConfigurer tilesConfigurer = new TilesConfigurer();
tilesConfigurer.setDefinitions(new String[] {
"/WEB-INF/layout/tiles.xml"
});
tilesConfigurer.setCheckRefresh(true);
return tilesConfigurer;
}
定义tiles
/WEB-INF/layout/tiles.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!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>
<definition name="base" template="/WEB-INF/layout/page.jsp">
<put-attribute name="header"
value="/WEB-INF/layout/header.jsp"></put-attribute>
<put-attribute name="footer"
value="/WEB-INF/layout/footer.jsp"></put-attribute>
</definition>
<definition name="home" extends="base">
<put-attribute name="body"
value="/WEB-INF/layout/home.jsp"></put-attribute>
</definition>
<definition name="registerForm" extends="base">
<put-attribute name="body"
value="/WEB-INF/layout/registerForm.jsp"></put-attribute>
</definition>
<definition name="profile" extends="base">
<put-attribute name="body"
value="/WEB-INF/layout/profile.jsp"></put-attribute>
</definition>
<definition name="spittles" extends="base">
<put-attribute name="body"
value="/WEB-INF/layout/spittles.jsp"></put-attribute>
</definition>
<definition name="spittle" extends="base">
<put-attribute name="body"
value="/WEB-INF/layout/spittle.jsp"></put-attribute>
</definition>
</tiles-definitions>
/WEB-INF/layout/page.jsp
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="t" %>
<%@ page session="false" %>
<html>
<head>
<title>Spittr</title>
<link rel="stylesheet" type="text/css" href="/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>
header.jsp
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
<a href="<s:url value="/" />">
<img src="<s:url value="/resources" />/images/spttr_logo_50.png" border="0" />
</a>
footer.jsp
Copyright © Craig Walls
home.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
<h1>Welcome to Spittr</h1>
<a href="<s:url value="/spittles" />">Spittles</a> |
<a href="<s:url value="/spitter/register"></s:url>">Register</a>
6.4.1 配置Thymeleaf视图解析器
- ThymeleafViewResolver将逻辑视图名称解析为Thymeleaf模板视图
- SpringTemplateEngine处理模板并渲染结果
- SpringResourceTemplateResolver加载Thymeleaf模板
@Bean
public ViewResolver ViewResolver(SpringTemplateEngine templateEngine) {
ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver();
thymeleafViewResolver.setTemplateEngine(templateEngine);
return thymeleafViewResolver;
}
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
return templateEngine;
}
@Bean
public ITemplateResolver templateResolver() {
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setPrefix("/WEB-INF/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("HTML5");
return templateResolver;
}
定义Thymeleaf模板
/WEB-INF/templates/home.html
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spiter</title>
<link rel="stylesheet" type="text/css" th:href="@{/resources/style.css}"></link>
</head>
<body>
<h1>Welcome to Spittr</h1>
<a th:href="@{/spittles}">Spittles</a>
<a th:href="@{/spitter/register}">Register</a>
</body>
</html>
注意:@{}
借助Thymeleaf实现表单绑定
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
<title>Spiter</title>
<style type="text/css">
div.errors {
background-color: #ffcccc;
border: 2px solid red;
}
label.error {
color: red;
}
input.error {
background-color: #ffcccc;
}
</style>
</head>
<body>
<h1>Register</h1>
<form method="POST" th:object="${spitter}">
<div class="errors" th:if="${#fields.hasErrors('*')}">
<ul>
<li th:echo="err : ${#fields.errors('*')}"
th:text="${err}">Input is incorrect</li>
</ul>
</div>
<label th:class="${#fields.hasErrors('firstName')}? 'error'">
FirstName</label>:
<input type="text" th:field="*{firstName}"
th:class="${#fields.hasErrors('firstName')}? 'error'"></input><br></br>
<label th:class="${#fields.hasErrors('lastName')}? 'error'">
lastName</label>:
<input type="text" th:field="*{lastName}"
th:class="${#fields.hasErrors('lastName')}? 'error'"></input><br></br>
<label th:class="${#fields.hasErrors('email')}? 'error'">
email</label>:
<input type="email" th:field="*{email}"
th:class="${#fields.hasErrors('email')}? 'error'"></input><br></br>
<label th:class="${#fields.hasErrors('username')}? 'error'">
username</label>:
<input type="text" th:field="*{username}"
th:class="${#fields.hasErrors('username')}? 'error'"></input><br></br>
<label th:class="${#fields.hasErrors('password')}? 'error'">
password</label>:
<input type="password" th:field="*{password}"
th:class="${#fields.hasErrors('password')}? 'error'"></input><br></br>
<input type="submit" value="Register"></input>
</form>
</body>
</html>
注意:
- ${} 变量表达式
- *{} 选择表达式