spring 4.0 原生支持13种视图解析器
从spring3.2版本开始,spring支持上述13种解析器,但是spring 3.1不支持Tiles 3 TilesViewResolver,其它的12种都支持.
视图解析器所在的配置文件
@Configuration @EnableWebMvc @ComponentScan("_5BuildingSpringwebapplications") public class WebConfig extends WebMvcConfigurerAdapter { @Bean public ViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); resolver.setExposeContextBeansAsAttributes(true); return resolver; } @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } }
配置视图解析器核心代码
@Bean public ViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); resolver.setExposeContextBeansAsAttributes(true); return resolver; }
对应在xml中配置时,如下示,配置在spring应用上下文中
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceViewResolver" /> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean>
下面是示例的逻辑视图名与物理文件的对应关系,注意最后的示例与前两个示例的区别
home resolves to /WEB-INF/views/home.jsp productList resolves to /WEB-INF/views/productList.jsp books/detail resolves to /WEB-INF/views/books/detail.jsp
通常在使用jsp的时候都使用JSTL,但是InternalResourceViewResolver的功能相对比较简单,因此需要将其替换为JstlView,如下示,
在使用jstlview之前,需要在项目里引入jstl相关的jar包
build.gradle配置
compile "javax.servlet:jstl:$jstlVersion"
gradle.properties配置 jstlVersion = 1.2
InternalResourceViewResolver被JstlView替换
@Bean public ViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class); return resolver; }
对应的xml配置
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean>
spring提供两种jsp标签库.
One tag library renders HTML form tags that are bound to a model attribute. The other has a hodgepodge of utility tags that come in handy from time to time.
第一种标签库,即表单绑定的jsp标签库包含14个标签,这些标签与html原生标签是有区别的,spring的标签可以获取model对象的属性值.其中一个标签可以将error信息反馈到结果页面中,即下文中提到的errors.
使用这些Spring标签的方法就是在jsp页面中声明.如下示
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>
在上述的声明语句中,prefix="sf"里的"sf"由用户自定义,用于与原生的html标签做区别,比如也可以定义成这样,
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="springformtag" %>
这14个标签如下图示,注意标签前的前缀,这里的前缀是sf,因为之前在spring标签声明时定义了prefix="sf",如果声明时定义的不是sf,在使用时就不能照搬下面的用法了.
很难在一个页面中把上述所有标签的使用都示例出来.下面将结合书中的示例系统说明<sf:form>, <sf:input>, and <sf:password>这三个标签的使用方法.
在第5章中写过一个用户注册页面,当时的写法如下所示
<body> <h1>Register</h1> <form method="POST"> First Name: <input type="text" name="firstName" /><br /> Last Name: <input type="text" name="lastName" /><br /> Username: <input type="text" name="username" /><br /> Password: <input type="password" name="password" /><br /> <input type="submit" value="Register" /> </form> </body>
下面是使用了spring标签的写法
<body> <h1>Register</h1> <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> </body>
<sf:form> 标签与HTML <form> 标签的功能类似,但是它指定了commandName属性,该属性绑定了一个model对象.这要求逻辑视图包含一个同名的model对象,因此需要对原来的控制器做相应的修改
原contrller SpitterController.java中的showRegistrationForm方法
@RequestMapping(value = "/register", method = RequestMethod.GET) public String showRegistrationForm() { return "registerForm"; }
修改后的代码,可以看到修改后,该方法在返回逻辑视图前在逻辑视图中增加了一个名为spitter的model属性,其实可以不用明确指定对象名,因为jsp页面中的对象名与类名一致,这与spring指定的默认对象名相同(类名首字母小写).因此添加属性的代码可写成model.addAttribute(new Spitter())
@RequestMapping(value = "/register", method = RequestMethod.GET) public String showRegistrationForm(Model model) { model.addAttribute("spitter",new Spitter()); return "registerForm"; }
回到使用的spring标签的jsp页面中,First Name: <sf:input path="firstName" />表示该输入框绑定的是new Spitter()类的firstName成员变量.注意,该成员变量名JSP页面中不能写错,如果写成First Name: <sf:input path="firstNamexxxx" />,那么运行时会报错.依此类推,
Last Name: <sf:input path="lastName" />表示该输入框对应的是new Spitter()类的lastName成员变量..等等.再次声明,成员变量名在JSP页面中不要写错.
假如在输入框中输入如下内容
那么该表单提交后,将向后台传递一个firstName为11,lastName为22,email为[email protected],username为1122的Spitter的对象.
在SpitterController.java的processRegistration方法中增加了一行输出可以用来验证对象的成员变量是否是我们在jsp页面的输入
输出结果
注意,如果在域对象spitter的定义中加入了校验,上述的结果是不可能成功的,因为我们在jsp页面中的输入并不满足校验的条件,因此需要将spitter定义中的校验取消掉,如下图示
补充一个第5章中的知识点.回到上述的JSP页面中,可以看到该form表单中并没有写明action属性,如果没有写明的话,提交该表单时将默认返回打开该页面时使用逻辑视图名,但是与打开该页面时视图对应的get方法不同,提交表单时要求逻辑视图对应post方法.下图示
回到校验的话题,如果在上述实验中我们没有取消在域对象spitter定义时给予的校验,那么在校验失败时,如何在jsp页面中向用户展示错误信息呢?
下面先还原在spitter对象定义中给予的检验,如下图示
package _5BuildingSpringwebapplications.domain; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.hibernate.validator.constraints.Email; 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 @Size(min = 2, max = 30, message = "{firstName.size}") private String firstName; @NotNull @Size(min = 2, max = 30, message = "{lastName.size}") private String lastName; @NotNull @Email(message = "{email.valid}") private String email; public Spitter() { } public Spitter(String username, String password, String firstName, String lastName, String email) { this(null, username, password, firstName, lastName, email); } public Spitter(Long id, String username, String password, String firstName, String lastName, String email) { this.id = id; this.username = username; this.password = password; this.firstName = firstName; this.lastName = lastName; this.email = email; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public boolean equals(Object that) { return EqualsBuilder.reflectionEquals(this, that, "firstName", "lastName", "username", "password", "email"); } @Override public int hashCode() { return HashCodeBuilder.reflectionHashCode(this, "firstName", "lastName", "username", "password", "email"); } }
在classpath根目录下新建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.
registerForm.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%> <%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %> <%@ page session="false"%> <html> <head> <title>Spittr</title> <link rel="stylesheet" type="text/css" href="<c:url value="/resources/style.css" />"> </head> <body> <h1>Register</h1> <sf:form method="POST" commandName="spitter"> <sf:errors path="*" element="div" cssClass="errors" /> First Name: <sf:input path="firstName" /> <!-- <span id="firstName.errors">size must be between 2 and 30</span> --> <br /> Last Name: <sf:input path="lastName" /> <!-- <span id="lastName.errors">size must be between 2 and 30</span> --> <br /> Email: <sf:input path="email" /> <!-- <span id="email.errors">size must be between 2 and 30</span> --> <br /> Username: <sf:input path="username" /> <!-- <span id="username.errors">size must be between 2 and 30</span> --> <br /> Password: <sf:password path="password" /> <!-- <span id="password.errors">size must be between 2 and 30</span> --> <br /> <input type="submit" value="Register" /> </sf:form> </body> </html>
结果
除了表单绑定的标签库外,spring还提供另一种JSP标签库.该标签库是spring最早包含的标签库.
引用方法,在JSP页面中添加
<%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
与表单绑定的标签库在引用方法上的区别在于uri上,
通用标签库的uri是
uri="http://www.springframework.org/tags"
而表单绑定的标签库的引用uri是
uri="http://www.springframework.org/tags/form"
标签描述
上图中的某些标签在表单绑定的标签库中已经被淘汰了.<s:bind>比如.