一.将模型数据渲染为Html
在上一篇文章中,咱们所编写的控制器方法都没有直接产生浏览器中渲染所需的HTML.这些方法只是将数据填充到模型中,而后将模型传递给一个用来渲染的视图.这些方法会返回一个String类型的值,这个值是视图的逻辑名称,不是引用直接的视图实现.尽管咱们也编写了几个简单的JavaServerPage(JSP)视图,可是控制器并不关心这些.将控制器中请求处理的逻辑和视图中的渲染实现解耦是Spring MVC的 一个重要特性。若是控制器中的方法直接负责产生HTML的话,就很 难在不影响请求处理逻辑的前提下,维护和更新视图。控制器方法和 视图的实现会在模型内容上达成一致,这是二者的最大关联,除此之 外,二者应该保持足够的距离。
可是,若是控制器只经过逻辑视图名来了解视图的话,那Spring该如 何肯定使用哪个视图实现来渲染模型呢?这就是Spring视图解析器 的任务了。在上一篇文章中,咱们使用名为InternalResourceViewResolver的 视图解析器。在它的配置中,为了获得视图的名字,会使用“/WEB- INF/views/”前缀和“.jsp”后缀,从而肯定来渲染模型的JSP文件的物理 位置。如今咱们来了解视图解析器的基础知识以及Spring提供的其余视图解析器:
在Spring之中,定义了名为ViewResolver的接口:javascript
1 package org.springframework.web.servlet; 2
3 import java.util.Locale; 4
5 public interface ViewResolver { 6
7 View resolveViewName(String viewName, Locale locale) throws Exception; 8
9 }
--当给resolveViewName()方法传入一个视图名和Locale对象时,他会返回一个View实例,View接口定义以下:css
1 /*
2 * Copyright 2002-2012 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0
9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */
16
17 package org.springframework.web.servlet; 18
19 import java.util.Map; 20 import javax.servlet.http.HttpServletRequest; 21 import javax.servlet.http.HttpServletResponse; 22
23 import org.springframework.http.MediaType; 24
25 public interface View { 26
27 String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus"; 28
29 String PATH_VARIABLES = View.class.getName() + ".pathVariables"; 30
31 String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType"; 32
33 String getContentType(); 34
35 void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception; 36
37 }
--View接口的任务就是接受模型以及Servlet的request和Response对象,并将输出结果渲染到response中.进而展示到用户的浏览器中.实际上咱们不须要这么麻烦,尽管咱们能够编写ViewResolver和view的实现,在有些特定的场景下,这样作也是有必要的,可是通常来说,咱们并不须要关心这些接口.Spring对于这些接口也提供了一些内置的实现:html
--事实上Spring所提供的视图解析器在实际的应用中,咱们只会用到其中不多的一部分,每一项都对应Java Web应用中 特定的某种视图技术。InternalResourceViewResolver通常会 用于JSP,TilesViewResolver用于Apache Tiles视图,而FreeMarkerViewResolver和VelocityViewResolver分别 对应FreeMarker和Velocity模板视图。java
二.建立JSP视图
JavaServer Pages做为Java Web应用程序的视图技术已经有了很是长的时间.Spring提供了两种支持JSP视图的方式:
1.InternalResourceViewResolver会将视图名解析为JSP文件.另外,若是你的JSP页面只使用了JSP标准标签库(JavaServer Pages Standard Tag Library,JSTL)的话,InternalResourceViewResolver可以将视图名解析为 JstlView形式的JSP文件,从而将JSTL本地化和资源bundle变量暴 露给JSTL的格式化(formatting)和信息(message)标签。
2.Springg提供了两个JSP标签库,一个用于表单到模型的绑定,另外一 个提供了通用的工具类特性。
例
--无论你使用JSTL,仍是准备使用Spring的JSP标签库,配置解析JSP的视图解析器都是很是重要的。尽管Spring还有其余的几个视图解析器 都能将视图名映射为JSP文件,但就这项任务来 讲,InternalResourceViewResolver是最简单和最经常使用的视图 解析器。jquery
--配置适用于JSP的视图解析器
有一些视图解析器,如ResourceBundleViewResolver会直接将 逻辑视图名映射为特定的View接口实现, 而InternalResourceViewResolver所采起的方式并不那么直 接。它遵循一种约定,会在视图名上添加前缀和后缀,进而肯定一个 Web应用中视图资源的物理路径。
考虑一个简单的场景,假设逻辑视图名为home。通用的 实践是将JSP文件放到Web应用的WEB-INF目录下,防止对它的直接 访问。若是咱们将全部的JSP文件都放在“/WEB-INF/views/”目录下, 而且home页的JSP名为home.jsp,那么咱们能够肯定物理视图的路径 就是逻辑视图名home再加上“/WEB-INF/views/”前缀和“.jsp”后缀:web
当使用@Bean注解的时候,咱们能够按照以下的方式配 置InternalResourceView Resolver,使其在解析视图时,遵循上述的约定:spring
1 @Bean 2 public ViewResolver viewResolver() { 3 //配置JSP视图解析器
4 InternalResourceViewResolver resolver = new InternalResourceViewResolver(); 5 resolver.setPrefix("/WEB-INF/views/"); //设置jsp所在的目录
6 resolver.setSuffix(".jsp"); //设置后缀名称
7 resolver.setExposeContextBeansAsAttributes(true); 8 return resolver; 9 }
做为替代方案,若是你更喜欢使用基于XML的Spring配置,那么能够按照以下的方式配置InternalResourceViewResolver:express
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
5 <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
6 p:prefix="/WEB-INF/views" p:suffix=".jsp"/>
7 </beans>
--当咱们完成了InternalResourceViewResolver的配置以后,他就会将逻辑视图解析为JSP文件:
1.home将会解析为“/WEB-INF/views/home.jsp”;
2.productList将会解析为“/WEB-INF/views/productList.jsp”;
3.books/detail将会解析为“/WEB-INF/views/books/detail.jsp”.apache
--解析JSTL视图
到目前为止,咱们对InternalResourceViewResolver的配置都 很基础和简单。它最终会将逻辑视图名解析 为InternalResourceView实例,这个实例会引用JSP文件。可是 若是这些JSP使用JSTL标签来处理格式化和信息的话,那么咱们会希 望InternalResourceViewResolver将视图解析为JstlView。
JSTL的格式化标签须要一个Locale对象,以便于恰当地格式化地域 相关的值,如日期和货币。信息标签能够借助Spring的信息资源和 Locale,从而选择适当的信息渲染到HTML之中。经过解析 JstlView,JSTL可以得到Locale对象以及Spring中配置的信息资源。
若是想让InternalResourceViewResolver将视图解析 为JstlView,而不是InternalResourceView的话,那么咱们只需设置它的viewClass属性便可:设计模式
1 package com.sky.config; 2
3 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.context.annotation.Configuration; 6 import org.springframework.web.servlet.ViewResolver; 7 import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; 8 import org.springframework.web.servlet.config.annotation.EnableWebMvc; 9 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 10 import org.springframework.web.servlet.view.InternalResourceViewResolver; 11 import org.springframework.web.servlet.view.JstlView; 12
13
14 /**
15 * @author : S K Y 16 * @version :0.0.1 17 */
18 @Configuration 19 @EnableWebMvc //启用Spring MVC
20 @ComponentScan(basePackages = {"com.sky.*"}) //启用组件扫描
21 public class WebConfig extends WebMvcConfigurerAdapter { 22 @Bean 23 public ViewResolver viewResolver() { 24 //配置JSP视图解析器
25 InternalResourceViewResolver resolver = new InternalResourceViewResolver(); 26 resolver.setPrefix("/WEB-INF/views/"); //设置jsp所在的目录
27 resolver.setSuffix(".jsp"); //设置后缀名称
28 resolver.setViewClass(JstlView.class); 29 resolver.setExposeContextBeansAsAttributes(true); 30 return resolver; 31 } 32
33 @Override 34 public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { 35 configurer.enable(); //配置静态资源的处理
36 } 37
38 }
--一样咱们也能够在XML中完成这一项配置
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
5 <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
6 p:prefix="/WEB-INF/views" p:suffix=".jsp"
7 p:viewClass="org.springframework.web.servlet.view.JstlView"/>
8 </beans>
--使用Spring的JSP库
当为JSP添加功能时,标签库是一种很强大的方式,可以避免在脚本 块中直接编写Java代码。Spring提供了两个JSP标签库,用来帮助定义 Spring MVC Web的视图。其中一个标签库会用来渲染HTML表单标 签,这些标签能够绑定model中的某个属性。另一个标签库包含了 一些工具类标签,咱们随时均可以很是便利地使用它们。
咱们能够将Spittle应用的注册表单绑定到模型上,这样表单就能够预先填充值,而且在表单提交上失败后,可以展示校验错误.
--Spring的表单绑定JSP标签库包含了14个标签,它们中的大多数都用来 渲染HTML中的表单标签。可是,它们与原生HTML标签的区别在于 它们会绑定模型中的一个对象,可以根据模型中对象的属性填充值。 标签库中还包含了一个为用户展示错误的标签,它会将错误信息渲染 到最终的HTML之中。为了使用表单绑定库,须要在JSP页面中对其进行声明:<%@taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>;Spring提供了14个相关的标签:
--咱们能够在咱们的JSP界面中使用这些标签:
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2 <%@taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
3 <%--<script src="${pageContext.request.contextPath}/js/jquery-1.11.3.min.js" ></script>--%>
4 <html>
5 <head>
6 <title>Spring标签库的使用</title>
7 </head>
8 <style type="text/css">
9 .reg_form {
10 width: 210px;
11 padding: 5px;
12 display: inline-block;
13 border: 1px solid black; /*设置边框*/
14 }
15
16 .text {
17 display: block;
18 width: 180px;
19 height: 20px;
20 margin: 20px 10px 0 10px;
21 }
22
23 .submit {
24 width: 100px;
25 height: 20px;
26 margin: 20px 50px 0 50px;
27 }
28 </style>
29 <body>
30 <body>
31 <sf:form method="post" commandName="registerForm" cssClass="reg_form">
32 用户名: <sf:input path="username" cssClass="text"/>
33 密码: <sf:password path="password" cssClass="text"/>
34 <button class="submit">点击注册</button>
35 </sf:form>
36 </body>
37 </html>
--其基本的使用能够还能够指定cssClass属性.其做用至关于咱们所使用的原生HTML标签中的class属性.可是,对于咱们来讲,此外,在使用<sf:input/>标签时可以容许咱们指定type属性,例如HTML5中的date,range,email:
1 <body>
2 <sf:form method="post" commandName="registerForm" cssClass="reg_form">
3 用户名: <sf:input path="username" cssClass="text"/>
4 密码: <sf:password path="password" cssClass="text"/>
5 邮箱: <sf:input path="email" cssClass="text" type="email"/>
6 <button class="submit">点击注册</button>
7 </sf:form>
8 </body>
--由于咱们使用commandName="registerForm"指定了该属性对象,因此咱们须要存在一个key为registerForm的对象,不然的话,咱们的表单将没法正常渲染(会出现JSP错误).所以咱们修改Controller的实现:
1 @RequestMapping(value = "/registerForm", method = RequestMethod.GET) 2 public String showRegistrationForm(Model model) { 3 model.addAttribute("registerForm", new User()); 4 return "registerForm"; 5 } 6
7 @RequestMapping(value = "/registerForm", method = RequestMethod.POST) 8 public String postRegistrationForm(Model model, @Valid User user, Errors errors) throws UnsupportedEncodingException { 9 if (errors.hasErrors()) { 10 model.addAttribute("registerForm", user); 11 return "registerForm"; 12 } else { 13 String encode = URLEncoder.encode(user.getUsername(), "UTF-8"); //对于中文字符,须要进行转化
14 return "redirect:/homepage?username=" + encode; //重定向到咱们的基本信息页
15 } 16 }
--此时咱们在GET方法中,直接传递了一个空的key为registerForm的User对象,而在POST方法中咱们则使用了JSR303校验,此时,若是咱们输入的信息有错误的话,那么回从新跳转到当前的注册JSP中,当顺利经过校验,则会跳转到咱们的首页,可是仍是存在必定的缺陷,如今咱们在注册失败的时候虽然成功实现了咱们当前的数据回显功能,可是用户依然不知道他所填写的信息在哪一个环节出错了,咱们但愿的是,能够经过Spring的标签来完成咱们错误信息的回显功能,所以咱们须要使用到<sf:errors>标签
--这里咱们须要特别注意一些问题:若是咱们想要保证成功映射到错误信息,那么我所映射的类的变量名必须是标准化的默认命名(类名首字母小写),例如咱们所映射的是User类的校验,那么咱们必须保证User类的变量名为user,而且在使用Model传递User信息时,也必须指定为user.(在这里须要说明使用Spring的form标签,那么commandName标签所寻找的并非请求的地址名称,而是咱们的变量名称),将咱们的JSP页面以及Controller,User类作出以下修改:
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2 <%@taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
3 <%--<script src="${pageContext.request.contextPath}/js/jquery-1.11.3.min.js" ></script>--%>
4
5 <!DOCTYPE html>
6 <html>
7 <head>
8 <title>Spring标签库的使用</title>
9 </head>
10 <style type="text/css">
11 .reg_form {
12 width: 210px;
13 padding: 5px;
14 display: inline-block;
15 border: 1px solid black; /*设置边框*/
16 }
17
18 .text {
19 display: block;
20 width: 180px;
21 height: 20px;
22 margin: 20px 10px 0 10px;
23 }
24
25 .submit {
26 width: 100px;
27 height: 20px;
28 margin: 20px 50px 0 50px;
29 }
30
31 .error {
32 display: block;
33 color: red;
34 }
35 </style>
36 <body>
37 <sf:form method="POST" commandName="user" cssClass="reg_form">
38 用户名: <sf:input path="username" cssClass="text"/> <sf:errors path="username" cssClass="error"/>
39 密码: <sf:password path="password" cssClass="text"/> <sf:errors path="password" cssClass="error"/>
40 邮箱: <sf:input path="email" cssClass="text" type="email"/> <sf:errors path="email" cssClass="error"/>
41 <button class="submit">点击注册</button>
42 </sf:form>
43 </body>
44 </html>
1 @RequestMapping(value = "/registerForm", method = RequestMethod.GET) 2 public String showRegistrationForm(Model model) { 3 model.addAttribute("user", new User()); 4 return "registerForm"; 5 } 6
7 @RequestMapping(value = "/registerForm", method = RequestMethod.POST) 8 public String postRegistrationForm(Model model, @Validated User user, Errors errors) throws UnsupportedEncodingException { 9 if (errors.hasErrors()) { 10 model.addAttribute("user", user); 11 List<ObjectError> allErrors = errors.getAllErrors(); 12 for (ObjectError error : allErrors) { 13 System.out.println("错误信息: " + error.getDefaultMessage()); 14 // errors.rejectValue("password", "Enter password");
15 } 16 return "registerForm"; 17 } else { 18 String encode = URLEncoder.encode(user.getUsername(), "UTF-8"); //对于中文字符,须要进行转化
19 return "redirect:/homepage?username=" + encode; //重定向到咱们的基本信息页
20 } 21 }
1 package com.sky.spittle; 2
3 import javax.validation.constraints.Email; 4 import javax.validation.constraints.NotNull; 5 import javax.validation.constraints.Size; 6
7 /**
8 * @author : S K Y 9 * @version :0.0.1 10 */
11 public class User { 12 @NotNull 13 @Size(min = 5, max = 15, message = "用户名长度必须在5-15之间") //设置当Size标签没有经过校验时默认错误信息
14 private String username; 15 @NotNull 16 @Size(min = 5, max = 15, message = "密码长度必须在5-15之间") 17 private String password; 18 @Email 19 @NotNull 20 @Size(min = 1,message = "email邮箱不能为空") 21 private String email; 22
23 public String getUsername() { 24 return username; 25 } 26
27 public void setUsername(String username) { 28 this.username = username; 29 } 30
31 public String getPassword() { 32 return password; 33 } 34
35 public void setPassword(String password) { 36 this.password = password; 37 } 38
39 public String getEmail() { 40 return email; 41 } 42
43 public void setEmail(String email) { 44 this.email = email; 45 } 46
47 @Override 48 public String toString() { 49 return "User{" +
50 "username='" + username + '\'' +
51 ", password='" + password + '\'' +
52 ", email='" + email + '\'' +
53 '}'; 54 } 55 }
--咱们能够发现此时在JSP中咱们并无去定义<sf:from>标签所访问的地址信息,而是定义了/registerForm地址所映射的User类的变量名称,此时咱们直接提交空表单,将有以下的效果:
--可是此时咱们必然会思考一个问题,若是咱们有两个Controller方法同时都是用了user映射,会使什么样的结果:
1 @RequestMapping(value = "/registerForm", method = RequestMethod.POST) 2 public String postRegistrationForm(Model model, @Validated User user, Errors errors) throws UnsupportedEncodingException { 3 if (errors.hasErrors()) { 4 model.addAttribute("user", user); 5 List<ObjectError> allErrors = errors.getAllErrors(); 6 for (ObjectError error : allErrors) { 7 System.out.println("错误信息: " + error.getDefaultMessage()); 8 // errors.rejectValue("password", "Enter password");
9 } 10 return "registerForm"; 11 } else { 12 String encode = URLEncoder.encode(user.getUsername(), "UTF-8"); //对于中文字符,须要进行转化
13 return "redirect:/homepage?username=" + encode; //重定向到咱们的基本信息页
14 } 15 } 16
17 @RequestMapping(value = "/abc/test", method = RequestMethod.GET) 18 public String secondRegistrationForm(Model model) { 19 model.addAttribute("user", new User()); 20 return "registerForm"; 21 } 22
23 @RequestMapping(value = "/abc/test", method = RequestMethod.POST) 24 public String secondRegistrationForm(Model model, @Validated User user, Errors errors) throws UnsupportedEncodingException { 25 if (errors.hasErrors()) { 26 model.addAttribute("user", user); 27 return "registerForm"; 28 } else { 29 String encode = URLEncoder.encode(user.getUsername(), "UTF-8"); //对于中文字符,须要进行转化
30 return "redirect:/homepage?username=" + encode; //重定向到咱们的基本信息页
31 } 32 }
--咱们能够发现此时这两个方法只有两个区别:1.请求映射的地址不一样,2.没有进行遍历错误信息的控制台打印,咱们再次运行查看结果:
--当咱们是经过/abc/test路径访问咱们的注册页面时,咱们直接点击提交,能够发现控制台不存在打印的信息
--为了做为区别,我在调用/abc/test请求地址的POST方法时,增长了如上图所示的打印信息,那么咱们再经过/registerFrom地址访问查看结果:
--那么如今咱们能够明白,Spring的form标签在提交表单的时候,是依据了咱们访问该地址时所映射的地址来寻找POST请求,这样一来也就不存在咱们所谓的冲突问题了,可是若是说咱们没有定义/registerForm的POST请求方法会怎么样呢,此时很遗憾,会直接返回给咱们一个错误的信息界面405
--固然这个问题咱们在进行开发设计的时候就可以避免出现(除非你是恶意的想要留下bug...)
--除了能够在每个输入框以后都追加一个相应的错误信息的处理,咱们也能够在底部,留下一个全部错误信息的展现,此时对于<sf:error>标签的path属性,咱们应当定义为"*",同时<sf:error>标签的默认展现是使用<span>标签来完成的,咱们也可使用<sf:error>标签的element属性来定义使用"div"等形式展示:
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2 <%@taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
3 <%--<script src="${pageContext.request.contextPath}/js/jquery-1.11.3.min.js" ></script>--%>
4
5 <!DOCTYPE html>
6 <html>
7 <head>
8 <title>Spring标签库的使用</title>
9 </head>
10 <style type="text/css">
11 .reg_form {
12 width: 210px;
13 padding: 5px;
14 display: inline-block;
15 border: 1px solid black; /*设置边框*/
16 }
17
18 .text {
19 display: block;
20 width: 180px;
21 height: 20px;
22 margin: 20px 10px 0 10px;
23 }
24
25 .submit {
26 width: 100px;
27 height: 20px;
28 margin: 20px 50px 0 50px;
29 }
30
31 .error {
32 display: block;
33 color: red;
34 }
35 </style>
36 <body>
37 <sf:form method="post" commandName="user" cssClass="reg_form">
38 用户名: <sf:input path="username" cssClass="text"/>
39 密码: <sf:password path="password" cssClass="text"/>
40 邮箱: <sf:input path="email" cssClass="text" type="email"/>
41 <sf:errors path="*" element="div" cssClass="error"/>
42 <button class="submit">点击注册</button>
43 </sf:form>
44 </body>
45 </html>
--观察此时的输出内容以及标签的组成,咱们能够发如今<sf:error>标签进行输出的时候,会自动的追加<br/>换行标签,显然这样的展现效果,也是咱们所指望的.此外,咱们还能够着重显示须要修改的输入域,为了实现这一操做咱们可使用CSSErrorClass属性来完成,同时咱们借助<sf:label>变迁,对咱们的输入提示信息也做出处理:
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2 <%@taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
3 <%--<script src="${pageContext.request.contextPath}/js/jquery-1.11.3.min.js" ></script>--%>
4
5 <!DOCTYPE html>
6 <html>
7 <head>
8 <title>Spring标签库的使用</title>
9 </head>
10 <style type="text/css">
11 .reg_form {
12 width: 210px;
13 padding: 5px;
14 display: inline-block;
15 border: 1px solid black; /*设置边框*/
16 }
17
18 .text {
19 display: block;
20 width: 180px;
21 height: 20px;
22 margin: 20px 10px 0 10px;
23 }
24
25 .submit {
26 width: 100px;
27 height: 20px;
28 margin: 20px 50px 0 50px;
29 }
30
31 .error {
32 display: block;
33 color: red;
34 }
35
36 .label_error {
37 display: block;
38 color: red;
39 }
40
41 .input_error {
42 border: 1px solid red;
43 }
44 </style>
45 <body>
46 <sf:form method="post" commandName="user" cssClass="reg_form">
47 <sf:label path="username" cssErrorClass="label_error">用户名: </sf:label>
48 <sf:input path="username" cssClass="text" cssErrorClass="input_error"/>
49 <sf:label path="password" cssErrorClass="label_error">密码: </sf:label>
50 <sf:password path="password" cssClass="text" cssErrorClass="input_error"/>
51 <sf:label path="email" cssErrorClass="label_error">邮箱: </sf:label>
52 <sf:input path="email" cssClass="text" type="email" cssErrorClass="input_error"/>
53 <sf:errors path="*" element="div" cssClass="error"/>
54 <button class="submit">点击注册</button>
55 </sf:form>
56 </body>
57 </html>
--固然,若是在咱们的项目之中,在不少的地方都使用了相同的验证错误描述信息,那么咱们在给User类的@Size标签设置message属性时,咱们可使用properties文件来完成信息的注入,若是这么作的话咱们就须要建立一个名为ValidationMessage.properties的文件放置在咱们的根路径下:
--想要顺利加载咱们的资源配置文件,则须要在WebConfig中配置咱们的MessageResource,一般使用的都是ReloadableResourceBundleMessageSource,同时为了可以正确的获取到资源配置文件的信息内容,咱们须要在注解的message属性中使用{}括号来包裹咱们的key值,不然key值会被解释为正常的提示信息进行输出:
1 @Bean 2 public MessageSource messageSource(){ 3 ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 4 messageSource.setDefaultEncoding("UTF-8"); 5 messageSource.setBasename("classpath:ValidationMessages"); //配置资源文件的名称
6 return messageSource; 7 }
--能够发现成功的获取到了咱们的错误信息
--使用资源文件的时候,咱们还可以对一些校验注解的属性进行自动的获取填充,例如如今想要实现咱们资源文件对于长度限制的说明,最好的效果是:用户名的长度在{min}和{max}之间;咱们但愿"min","max"的值可以自动的进行填充,而不是须要咱们去手动设置,那么咱们能够这样作(此时若是认为每次都去使用JDK的工具进行中文和Unicode的转化显得十分麻烦,咱们能够在idea中在settings中找到File Encodings,将Transparent native-to-ascii conconversion勾选上):
--因为咱们并无进行email的max长度限制,因此产生的是Integer.MAX_VALUE的值,此外咱们还能够对资源文件进行国际化配置,在此再也不赘述,咱们所须要作的只是给出全部支持语言的配置文件便可,能够查看以前的文章(点击此处跳转).
--Spring的通用标签库
除了表单绑定标签库以外,Spring还提供了更为通用的JSP标签库,实际上这个标签库是Spring最先的标签库,想要使用Spring的通用标签库,咱们能够这样声明:<%@taglib prefix="s" uri="http://www.springframework.org/tags" %>,列出Spring的JSP标签支持:
--在其中例如<s:bind>标签时已经被淘汰的表单绑定标签,他的使用相对于<sf:form>来讲更加的复杂;在咱们以前的JSP文件之中,包含有不少的硬编码的文本,若是说咱们的网页展现有着国际化的问题的话,咱们必定但愿这些硬编码的文本也能够经过资源文件进行读取,这样不至于咱们对于不一样的语言单独作出一个咱们的界面实现,例如在欢迎页中,咱们但愿在中国进行访问的时候,显示的是"欢迎来到微博",可是在英文国家进行访问的时候咱们更但愿显示的是"welcome to spittr",咱们咱们能够这样进行咱们的资源文件配置:
--然后,咱们须要在MessageResource中声明咱们的这个资源配置文件:
1 @Bean 2 public MessageSource messageSource() { 3 ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 4 messageSource.setDefaultEncoding("UTF-8"); 5 messageSource.setBasenames("classpath:ValidationMessages", "classpath:HomeMessage");//配置资源文件的名称
6 return messageSource; 7 }
--在JSP页面中,咱们可使用<s:message>标签来引用咱们的资源配置文件的内容信息:
1 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
2 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
3 <%@taglib prefix="s" uri="http://www.springframework.org/tags" %>
4 <html>
5 <head>
6 <title>微博</title>
7 </head>
8 <body>
9 <h1><s:message code="home.welocme"/></h1><c:out value="${username}"/>
10 <button id="register" type="button" onclick="register()" id="a" onz>登陆</button>
11 <button type="button" id="b">注册</button>
12 </body>
13 <script type="text/javascript" src="<c:url value="/js/jquery-1.11.3.min.js"/>"></script> 14 <script type="text/javascript">
15 function register() { //点击跳转到指定路径
16 //若是咱们不指定pageContext.request.contextPath属性的话,那么久没法成功找到咱们的属性地址
17 window.location.href = "${pageContext.request.contextPath}/spittles/register"; 18 } 19 </script>
20 </html>
--查看咱们最后的运行结果:
--事实上,咱们在配置MessageSource的时候,还可使用ResourceBundleMessageSource,他会从一个属性文件中加载信息,这个属性文件的名称须要是由所配置的base_name衍生而来的,而咱们选用的ReloadableResourceBundleMessageSource与ResourceBundleMessageSource的区别在于能够从新加载信息属性,而没必要从新编译或重启应用.咱们在配置MessageSource的时候,设置的base_name若是是基于类路径的话,咱们选用的是"classpath"前缀,若是使用的是文件系统中的文件,那么咱们能够选中"file"前缀.同时咱们还可使用serCacheSeconds()方法来设置缓存时间,以减少服务器的压力.
--建立URL
<s:url>是一个很小的标签。它主要的任务就是建立URL,而后将其 赋值给一个变量或者渲染到响应中。它是JSTL中<c:url>标签的替 代者,可是它具有几项特殊的技巧。
--按照其最简单的形式,<s:url>会接受一个相对于Servlet上下文的 URL,并在渲染的时候,预先添加上Servlet上下文路径。例如,考虑 以下<s:url>的基本用法:
<script type="text/javascript" src="<s:url value="/js/jquery-1.11.3.min.js"/>"></script>
--另外,咱们还可使用<s:url>建立URL,并将其赋值给一个变量供 模板在稍后使用:
<s:url value="/js/jquery-1.11.3.min.js" var="scriptSrc"/>
<script type="text/javascript" src="${scriptSrc}"></script>
-- 默认状况下,URL是在页面做用域内建立的。可是经过设置scope属 性,咱们可让<s:url>在应用做用域内、会话做用域内或请求做 用域内建立URL:
1 <%--<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>--%> 2 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
3 <%@taglib prefix="s" uri="http://www.springframework.org/tags" %>
4 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
5 <html>
6 <head>
7 <title>微博</title>
8 </head>
9 <body>
10 <h1><s:message code="home.welocme"/></h1><c:out value="${username}"/>
11 <button id="register" type="button" onclick="register()" id="a" onz>登陆</button>
12 <button type="button" id="b">注册</button>
13 </body>
14 <s:url value="/js/jquery-1.11.3.min.js" var="scriptSrc"/>
15 <s:url value="/spittles/register" var="register" scope="request"/>
16 <script type="text/javascript" src="${scriptSrc}"></script>
17 <script type="text/javascript">
18 $(function () { 19 alert("hello"); 20 }); 21 function register() { //点击跳转到指定路径
22 //若是咱们不指定pageContext.request.contextPath属性的话,那么久没法成功找到咱们的属性地址
23 window.location.href = "${register}"; 24 } 25 </script>
26 </html>
--同时咱们还可使用<s:param>子标签来注入参数:
1 <%--<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>--%> 2 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
3 <%@taglib prefix="s" uri="http://www.springframework.org/tags" %>
4 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
5 <html>
6 <head>
7 <title>微博</title>
8 </head>
9 <body>
10 <s:url value="/homepage" var="homepage">
11 <s:param name="username" value="我爱Java"/>
12 </s:url>
13 <h1><s:message code="home.welocme"/></h1><c:out value="${username}"/>
14 <button id="register" type="button" onclick="register()" id="a" onz>登陆</button>
15 <button type="button" id="b">注册</button>
16 <button type="button" onclick="registerWithSpringTag()">使用Spring标签注入参数的访问</button>
17 </body>
18 <s:url value="/js/jquery-1.11.3.min.js" var="scriptSrc"/>
19 <s:url value="/spittles/register" var="register" scope="request"/>
20 <script type="text/javascript" src="${scriptSrc}"></script>
21 <script type="text/javascript">
22 $(function () { 23 alert("hello"); 24 }); 25
26 function register() { //点击跳转到指定路径
27 //若是咱们不指定pageContext.request.contextPath属性的话,那么久没法成功找到咱们的属性地址
28 window.location.href = "${register}"; 29 } 30
31 function registerWithSpringTag() { 32 window.location.href = "${homepage}"; 33 } 34 </script>
35 </html>
--以上功能咱们使用<c:url>也可以顺利实现,可是若是咱们建立带有路径(path)参数的URL,只能使用Spring提供的标签来完成:
1 <s:url value="/homePath/{username}" var="pathVariable">
2 <s:param name="username">路径参数:Java</s:param>
3 </s:url>
--当咱们所指定的value属性中的占位符匹配<s:param>中的指定参数时,这个参数将会插入到占位符的位置中.若是<s:param>参数没法匹配href中的任何占位符,那么这个参数就会做为查询参数.<s:url>标签还能够解决URL的转义需求。例如,若是你但愿将渲染 获得的URL内容展示在Web页面上(而不是做为超连接),那么你应 该要求<s:url>进行HTML转义,这须要将htmlEscape属性设置 为true。
1 <br/>
2 <s:url value="/homePath/{username}" htmlEscape="true">
3 <s:param name="username">在界面上展示该连接地址</s:param>
4 </s:url>
--能够发现即便咱们使用了中文字符,也能很好的进行转义.同时咱们也可使用JS将htmlEscape属性设置为true的连接地址进行变量赋值,从而达到某些操做的目的.
--转义内容
<s:escapeBody>标签是一个通用的转义标签。它会渲染标签体中 内嵌的内容,而且在必要的时候进行转义。例如,假设你但愿在页面上展示一个HTML代码片断。为了正确显 示,咱们须要将“<”和“>”字符替换为“<”和“>”,不然的话, 浏览器将会像解析页面上其余HTML那样解析这段HTML内容。 固然,没有人禁止咱们手动将其转义为“<”和“>”,可是这很 烦琐,而且代码难以阅读。咱们可使用<s:escapeBody>,并让 Spring完成这项任务:
1 <s:escapeBody htmlEscape="true">
2 <h1>你好</h1>
3 <div>
4 <label>标题内容</label>
5 </div>
6 </s:escapeBody>
--同时咱们还能设置javaScriptEscape属性进行转义.
三.使用Apache Tiles视图定义布局
到如今为止,咱们不多关心应用中Web页面的布局问题。每一个JSP完 全负责定义自身的布局,在这方面其实这些JSP也没有作太多工做。 假设咱们想为应用中的全部页面定义一个通用的头部和底部。最原始 的方式就是查找每一个JSP模板,并为其添加头部和底部的HTML。但 是这种方法的扩展性并很差,也难以维护。为每一个页面添加这些元素 会有一些初始成本,然后续的每次变动都会耗费相似的成本。更好的方式是使用布局引擎,如Apache Tiles,定义适用于全部页面 的通用页面布局。Spring MVC以视图解析器的形式为Apache Tiles提 供了支持,这个视图解析器可以将逻辑视图名解析为Tile定义。
--配置Tiles视图解析器
为了在Spring中使用Tiles,须要配置几个bean。咱们须要一 个TilesConfigurer bean,它会负责定位和加载Tile定义并协调生 成Tiles。除此以外,还须要TilesViewResolver bean将逻辑视图 名称解析为Tile定义。
这两个组件又有两种形式:针对Apache Tiles 2和Apache Tiles 3分别都 有这么两个组件。这两组Tiles组件之间最为明显的区别在于包名。针 对Apache Tiles 2的TilesConfigurer/TilesViewResolver位于 org.springframework.web.servlet.view.tiles2包中,而 针对Tiles 3的组件位于 org.springframework.web.servlet.view.tiles3包中。对 于该例子来说,假设咱们使用的是Tiles 3。
--配置TilesConfigurer来解析定义
1 @Bean 2 public TilesConfigurer tilesConfigurer() { 3 TilesConfigurer tilesConfigurer = new TilesConfigurer(); 4 tilesConfigurer.setDefinitions("/WEB-INF/layout/tiles.xml"); 5 tilesConfigurer.setCheckRefresh(true); //启用刷新功能
6 return tilesConfigurer; 7 }
--当配置TilesConfigurer的时候,所要设置的最重要的属性就 是definitions。这个属性接受一个String类型的数组,其中每 个条目都指定一个Tile定义的XML文件。对于Spittr应用来说,咱们让 它在“/WEB-INF/layout/”目录下查找tiles.xml。其实咱们还能够指定多个Tile定义文件,甚至可以在路径位置上使用 通配符,固然在上例中咱们没有使用该功能。例如,咱们要 求TilesConfigurer加载“/WEB-INF/”目录下的全部名字为tiles.xml 的文件,那么能够按照以下的方式设置definitions属性:
1 @Bean 2 public TilesConfigurer tilesConfigurer() { 3 TilesConfigurer tilesConfigurer = new TilesConfigurer(); 4 tilesConfigurer.setDefinitions("/WEB-INF/**/tiles.xml"); 5 tilesConfigurer.setCheckRefresh(true); //启用刷新功能
6 return tilesConfigurer; 7 }
--咱们使用了Ant风格的通配符(**),因此 TilesConfigurer会遍历“WEB-INF/”的全部子目录来查找Tile定 义。然后咱们能够定义TilesViewResolver:
1 @Bean 2 public ViewResolver tilesViewResolver() { 3 return new TilesViewResolver(); 4 }
--固然咱们也可使用XML来完成二者的配置
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
4 xmlns:util="http://www.springframework.org/schema/util"
5 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
6 <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
7 p:prefix="/WEB-INF/views" p:suffix=".jsp"
8 p:viewClass="org.springframework.web.servlet.view.JstlView"/>
9 <util:list id="list">
10 <value>/WEB-INF/layout/tiles.xml</value>
11 <value>/WEB-INF/**/tiles.xml</value>
12 </util:list>
13 <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
14 <property name="definitions" ref="list"/>
15 </bean>
16 <bean id="tilesViewResolver" class="org.springframework.web.servlet.view.tiles3.TilesViewResolver"/>
17 </beans>
--TilesConfigurer会加载Tile定义并与Apache Tiles协做, 而TilesViewRe-solver会将逻辑视图名称解析为引用Tile定义的 视图。它是经过查找与逻辑视图名称相匹配的Tile定义实现该功能 的。咱们须要建立几个Tile定义以了解它是如何运转的,定义Apache Tiles所须要的jar包以下(org.apache.tiles):
--定义Tiles
Apache Tiles提供了一个文档类型定义(document type definition, DTD),用来在XML文件中指定Tile的定义。每一个定义中须要包含一 个<definition>元素,这个元素会有一个或多个<put- attribute>元素。
1 <?xml version="1.0" encoding="UTF-8" ?>
2 <!DOCTYPE tiles-definitions PUBLIC 3 "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" 4 "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
5 <tiles-definitions>
6 <definition name="base" template="/WEB-INF/layout/page.jsp"> <!--定义基本的页面-->
7 <put-attribute name="header" value="/WEB-INF/layout/header.jsp"/> <!--定义页面的头部-->
8 <put-attribute name="footer" value="/WEB-INF/layout/footer.jsp"/> <!--定义页面的尾部-->
9 </definition>
10 <definition name="home" extends="base">
11 <put-attribute name="body"
12 value="/WEB-INF/views/home.jsp"/>
13 </definition>
14 <definition name="registerForm" extends="base">
15 <put-attribute name="body" value="/WEB-INF/views/registerForm.jsp"/>
16 </definition>
17 <definition name="spittles" extends="base">
18 <put-attribute name="body" value="/WEB-INF/views/register.jsp"/>
19 </definition>
20 </tiles-definitions>
--每一个<definition>元素都定义了一个Tile,它最终引用的是一个 JSP模板。在名为base的Tile中,模板引用的是“/WEB- INF/layout/page.jsp”。某个Tile可能还会引用其余的JSP模板,使这些 JSP模板嵌入到主模板中。对于base Tile来说,它引用的是一个头部 JSP模板和一个底部JSP模板,page .jsp的定义以下:
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2 <%@taglib prefix="t" uri="http://tiles.apache.org/tags-tiles" %>
3 <html>
4 <head>
5 <title>Title</title>
6 </head>
7 <style type="text/css">
8 * {
9 margin: 0;
10 padding: 0;
11 }
12
13 #footer {
14 width: 1920px;
15 height: 100px;
16 background-color: lightblue;
17 text-align: center;
18 }
19 #header{
20 width: 1920px;
21 height: 100px;
22 background-color: lightgrey;
23 text-align: center;
24 }
25 </style>
26 <body>
27 <div id="header">
28 <t:insertAttribute name="header"/>
29 </div>
30 <div id="content">
31 <t:insertAttribute name="body"/>
32 </div>
33 <div id="footer">
34 <t:insertAttribute name="footer"/>
35 </div>
36 </body>
37 </html>
--在这里只是为了演示出效果,因此只是采用了当前的简单的CSS样式来做为区分,在最终咱们能够看到展示出的效果为:
--注:在这里因为(org.apache.tiles)相关的依赖包实在太多了,因此我将整个项目使用maven从新构建了一下,给出相关的maven依赖配置:
1 <dependencies>
2 <!--单元测试-->
3 <dependency>
4 <groupId>junit</groupId>
5 <artifactId>junit</artifactId>
6 <version>4.11</version>
7 <scope>test</scope>
8 </dependency>
9 <!--servlet aip 运行必须-->
10 <dependency>
11 <groupId>javax.servlet</groupId>
12 <artifactId>javax.servlet-api</artifactId>
13 <version>3.1.0</version>
14 <scope>provided</scope>
15 </dependency>
16 <dependency>
17 <groupId>javax.servlet.jsp.jstl</groupId>
18 <artifactId>jstl-api</artifactId>
19 <version>1.2</version>
20 </dependency>
21 <dependency>
22 <groupId>taglibs</groupId>
23 <artifactId>standard</artifactId>
24 <version>1.1.2</version>
25 </dependency>
26 <!--JSTL-->
27 <dependency>
28 <groupId>javax.servlet.jsp.jstl</groupId>
29 <artifactId>jstl</artifactId>
30 <version>1.2</version>
31 </dependency>
32 <!--spring-->
33 <dependency>
34 <groupId>org.springframework</groupId>
35 <artifactId>spring-webmvc</artifactId>
36 <version>4.3.4.RELEASE</version>
37 </dependency>
38 <!--JSR 303校验-->
39 <dependency>
40 <groupId>org.hibernate.validator</groupId>
41 <artifactId>hibernate-validator</artifactId>
42 <version>6.0.13.Final</version>
43 </dependency>
44 <!--org.apache.tiles-->
45 <dependency>
46 <groupId>org.apache.tiles</groupId>
47 <artifactId>tiles-jsp</artifactId>
48 <version>3.0.8</version>
49 </dependency>
50 </dependencies>
四.使用Thymeleaf
尽管JSP已经存在了很长的时间,而且在Java Web服务器中无处不 在,可是它却存在一些缺陷。JSP最明显的问题在于它看起来像 HTML或XML,但它其实上并非。大多数的JSP模板都是采用HTML 的形式,可是又掺杂上了各类JSP标签库的标签,使其变得很混乱。 这些标签库可以以很便利的方式为JSP带来动态渲染的强大功能,但 是它也摧毁了咱们想维持一个格式良好的文档的可能性。
标签库和JSP缺少良好格式的一个反作用就是它不多可以与其产生的 HTML相似。因此,在Web浏览器或HTML编辑器中查看未经渲染的 JSP模板是很是使人困惑的,并且获得的结果看上去也很是丑陋。这 个结果是不完整的——在视觉上这简直就是一场灾难!由于JSP并非真正的HTML,不少浏览器和编辑器展示的效果都很难在审美上接 近模板最终所渲染出来的效果。
同时,JSP规范是与Servlet规范紧密耦合的。这意味着它只能用在基 于Servlet的Web应用之中。JSP模板不能做为通用的模板(如格式化 Email),也不能用于非Servlet的Web应用。多年来,在Java应用中,有多个项目试图挑战JSP在视图领域的统治 性地位。最新的挑战者是Thymeleaf,它展示了一些切实的承诺,是 一项很使人兴奋的可选方案。Thymeleaf模板是原生的,不依赖于标 签库。它能在接受原始HTML的地方进行编辑和渲染。由于它没有与 Servlet规范耦合,所以Thymeleaf模板可以进入JSP所没法涉足的领 域。
--配置Thymeleaf视图解析器
为了要在Spring中使用Thymeleaf,咱们须要配置三个启用Thymeleaf与Spring集成的bean:
1.ThymeleafViewResolver:将逻辑视图名称解析为Thymeleaf 模板视图;
2.SpringTemplateEngine:处理模板并渲染结果;
3.TemplateResolver:加载Thymeleaf模板。
--使用Java代码的方式,配置Spring对Thymeleaf的支持,首先咱们须要引入thymeleaf的相关依赖
1 <!--Spring中使用thymeleaf-->
2 <dependency>
3 <groupId>org.thymeleaf</groupId>
4 <artifactId>thymeleaf-spring4</artifactId>
5 <version>3.0.9.RELEASE</version>
6 </dependency>
--进行thymele使用的相关配置
1 package com.sky.config; 2
3 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.context.annotation.Configuration; 6 import org.springframework.web.servlet.ViewResolver; 7 import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; 8 import org.springframework.web.servlet.config.annotation.EnableWebMvc; 9 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 10 import org.springframework.web.servlet.view.InternalResourceViewResolver; 11 import org.springframework.web.servlet.view.JstlView; 12 import org.springframework.web.servlet.view.tiles3.TilesConfigurer; 13 import org.springframework.web.servlet.view.tiles3.TilesViewResolver; 14 import org.thymeleaf.TemplateEngine; 15 import org.thymeleaf.spring4.SpringTemplateEngine; 16 import org.thymeleaf.spring4.view.ThymeleafViewResolver; 17 import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver; 18 import org.thymeleaf.templateresolver.AbstractTemplateResolver; 19 import org.thymeleaf.templateresolver.ServletContextTemplateResolver; 20
21 import javax.servlet.ServletContext; 22
23 /**
24 * @author : S K Y 25 * @version :0.0.1 26 */
27 @Configuration 28 @EnableWebMvc 29 @ComponentScan(basePackages = {"com.sky.*"}) 30 public class WebConfig extends WebMvcConfigurerAdapter { 31 /*
32 @Bean 33 public ViewResolver viewResolver() { 34 InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); 35 viewResolver.setPrefix("/WEB-INF/views/"); //设置前缀名称 36 viewResolver.setSuffix(".jsp"); //设置后缀名称 37 viewResolver.setViewClass(JstlView.class); 38 viewResolver.setExposeContextBeansAsAttributes(true); 39 return viewResolver; 40 } 41 */
42
43 @Override 44 public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { 45 configurer.enable(); //配置静态资源的处理
46 } 47
48 /* //tiles模板使用设置 49 @Bean 50 public TilesConfigurer tilesConfigurer() { 51 TilesConfigurer tilesConfigurer = new TilesConfigurer(); 52 tilesConfigurer.setDefinitions("/WEB-INF/layout/tiles.xml"); 53 tilesConfigurer.setCheckRefresh(true); //启用刷新功能 54 return tilesConfigurer; 55 } 56
57 //tiles模板使用设置 58 @Bean 59 public TilesViewResolver tilesViewResolver() { 60 return new TilesViewResolver(); 61 }*/
62
63 @Bean 64 public ViewResolver thymeleafViewResolver(TemplateEngine templateEngine) { //Thymeleaf视图解析器
65 ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver(); 66 thymeleafViewResolver.setTemplateEngine(templateEngine); 67 thymeleafViewResolver.setCharacterEncoding("UTF-8"); //须要设置编码格式,不然使用中文乱码
68 return thymeleafViewResolver; 69 } 70
71 @Bean 72 public TemplateEngine templateEngine(AbstractTemplateResolver templateResolver) { //模板引擎
73 SpringTemplateEngine templateEngine = new SpringTemplateEngine(); 74 templateEngine.setTemplateResolver(templateResolver); 75 return templateEngine; 76 } 77
78 @Bean 79 public AbstractConfigurableTemplateResolver templateResolver(ServletContext servletContext) { //模板解析器
80 AbstractConfigurableTemplateResolver templateResolver =
81 new ServletContextTemplateResolver(servletContext); 82 templateResolver.setPrefix("/WEB-INF/thymeleaves/"); 83 templateResolver.setSuffix(".html"); 84 templateResolver.setTemplateMode("HTML5"); 85 templateResolver.setCharacterEncoding("UTF-8"); //须要设置编码格式,不然使用中文乱码
86 return templateResolver; 87 } 88 }
--ThymeleafViewResolver是Spring MVC中ViewResolver的一个 实现类。像其余的视图解析器同样,它会接受一个逻辑视图名称,并 将其解析为视图。不过在该场景下,视图会是一个Thymeleaf模板。须要注意的是ThymeleafViewResolver bean中注入了一个对 SpringTemplate Engine bean的引 用。SpringTemplateEngine会在Spring中启用Thymeleaf引擎,用 来解析模板,并基于这些模板渲染结果。能够看到,咱们为其注入了一个TemplateResolver bean的引用。
--TemplateResolver会最终定位和查找模板。与以前配 置InternalResourceViewResolver相似,它使用了prefix和 suffix属性。前缀和后缀将会与逻辑视图名组合使用,进而定位 Thymeleaf引擎。它的templateMode属性被设置成了HTML5,这表 明咱们预期要解析的模板会渲染成HTML5输出。
--定义Thymeleaf模板
Thymeleaf在很大程度上就是HTML文件,与JSP不一样,它没有什么特 殊的标签或标签库。Thymeleaf之因此可以发挥做用,是由于它经过 自定义的命名空间,为标准的HTML标签集合添加Thymeleaf属性。如 下的程序清单展示了home.html,也就是使用Thymeleaf命名空间的首页模板。
1 <!DOCTYPE html>
2 <html lang="en" xmlns="http://www.w3.org/1999/xhtml"
3 xmlns:th="http://www.thymeleaf.org">
4 <head>
5 <meta charset="UTF-8">
6 <title>title</title>
7 <!--到样式表的th:href连接-->
8 <!-- <link rel="stylesheet" type="text/css" 9 th:href="@{/resources/style.css}"/>-->
10 </head>
11 <body>
12 <h1>欢迎来到微博</h1>
13 <a th:href="@{/thyme/register}">点击注册</a>
14 </body>
15 </html>
--首页模板相对来说很简单,只使用了th:href属性。这个属性与对 应的原生HTML属性很相似,也就是href属性,而且能够按照相同的 方式来使用。th:href属性的特殊之处在于它的值中能够包含 Thymeleaf表达式,用来计算动态的值。它会渲染成一个标准的href 属性,其中会包含在渲染时动态建立获得的值。这是Thymeleaf命名 空间中不少属性的运行方式:它们对应标准的HTML属性,而且具备 相同的名称,可是会渲染一些计算后获得的值。在本例中,使 用th:href属性的三个地方都用到了“@{}”表达式,用来计算相对于 URL的路径(就像在JSP页面中,咱们可能会使用的JSTL<c:url>标 签或Spring<s:url>标签相似)。
--Thymeleaf可以按照原始的方式进行编辑 甚至渲染,而没必要通过任何类型的处理器。固然,咱们须要 Thymeleaf来处理模板并渲染获得最终指望的输出。即使如此,若是 没有任何特殊的处理,home.html也可以加载到Web浏览器中,而且看 上去与完整渲染的效果很相似。同时,咱们还能在Thymeleaf中使用Spring表达式语言,咱们亲切的能够成为Thymeleaf的Spring方言.
1 <!DOCTYPE html>
2 <html lang="en" xmlns="http://www.w3.org/1999/xhtml"
3 xmlns:th="http://www.thymeleaf.org">
4 <head>
5 <meta charset="UTF-8">
6 <title>title</title>
7 <!--到样式表的th:href连接-->
8 <!-- <link rel="stylesheet" type="text/css" 9 th:href="@{/resources/style.css}"/>-->
10 </head>
11 <style type="text/css">
12 .error {
13 color: red;
14 }
15
16 .error_text {
17 border: 1px solid red;
18 }
19
20 .title {
21 color: green;
22 width: 20px;
23 height: 10px;
24 border: 1px solid black;
25 }
26 </style>
27 <body>
28 <form method="post" th:object="${user}">
29 <label class="title" th:class="${#fields.hasErrors('username')} ? 'error'">用户名:</label>
30 <input type="text" th:class="${#fields.hasErrors('username')}?'error_text'"
31 th:field="*{username}"/>
32 <label th:text="${#fields.errors('username')}"
33 th:class="${#fields.hasErrors('username') }? 'error'"
34 th:if="${#fields.hasErrors('username')}">XXX</label>
35 <br/>
36 <label class="title" th:class="${#fields.hasErrors('password')} ? 'error'">密码:</label>
37 <input type="password" th:class="${#fields.hasErrors('password')}?'error_text'"
38 value="123" th:value="*{password}"/>
39 <label th:text="${#fields.errors('password')}"
40 th:class="${#fields.hasErrors('password')} ? 'error'"
41 th:if="${#fields.hasErrors('password')}"></label> <br/>
42 <label class="title" th:class="${#fields.hasErrors('email')} ? 'error'">邮箱:</label>
43 <input type="email" th:class="${#fields.hasErrors('email')}?'error_text'"
44 th:field="*{email}"/>
45 <label th:text="${#fields.errors('email')}"
46 th:class="${#fields.hasErrors('email')} ? 'error'"
47 th:if="${#fields.hasErrors('email')}"></label><br/>
48 <button>提交</button>
49 </form>
50 </body>
51 </html>
--在上述的例子中咱们使用了较多的表达式完成了总体的实际,在<form>标签之中咱们可使用th:object属性在${user}中指定咱们接受的传递参数,这里须要注意的是,thymeleaf在解析咱们的html页面的时候,若是咱们没有传递${user}中所指定的内容,那么就不会去显示咱们在form表单中所定义的信息,因此和以前同样咱们须要在get请求中给出一个空的user对象做为初始的传值:
1 @RequestMapping(value = "/register", method = RequestMethod.GET) 2 public String register(Model model) { 3 model.addAttribute("user", new User()); 4 return "register"; 5 } 6
7 @RequestMapping(value = "/register", method = RequestMethod.POST) 8 public String registerPost(@Validated User user, Errors errors, Model model) { 9 System.out.println(user); 10 if (errors.hasErrors()) { 11 return "register"; 12 } else { 13 return "home"; 14 } 15 }
--基本的controller设计用JSP中的设计模式是同样的,在thymeleaf中咱们可使用 th:class="${#fields.hasErrors('password')} ? 'error'"来指定当获取到相对应的错误信息的时候所显示的class属性,使用th:text="${#fields.errors('password')}"来指定当错误信息存在时所展示的内容,用 th:if="${#fields.hasErrors('password')}"来指定只有当存在该错误信息的时候才会显示出咱们所定义的错误信息.固然咱们也能够将全部的错误信息都在一个指定的位置显示出来,同时使用遍历循环的方式来展示咱们的数据信息:
1 <!DOCTYPE html>
2 <html lang="en" xmlns="http://www.w3.org/1999/xhtml"
3 xmlns:th="http://www.thymeleaf.org">
4 <head>
5 <meta charset="UTF-8">
6 <title>title</title>
7 <!--到样式表的th:href连接-->
8 <!-- <link rel="stylesheet" type="text/css" 9 th:href="@{/resources/style.css}"/>-->
10 </head>
11 <style type="text/css">
12 .error {
13 color: red;
14 }
15
16 .error_text {
17 border: 1px solid red;
18 }
19
20 .title {
21 color: green;
22 width: 20px;
23 height: 10px;
24 border: 1px solid black;
25 }
26 </style>
27 <body>
28 <form method="post" th:object="${user}">
29 <label class="title" th:class="${#fields.hasErrors('username')} ? 'error'">用户名:</label>
30 <input type="text" th:class="${#fields.hasErrors('username')}?'error_text'"
31 th:field="*{username}"/>
32 <!-- <label th:text="${#fields.errors('username')}" 33 th:class="${#fields.hasErrors('username') }? 'error'" 34 th:if="${#fields.hasErrors('username')}">XXX</label>-->
35 <br/>
36 <label class="title" th:class="${#fields.hasErrors('password')} ? 'error'">密码:</label>
37 <input type="password" th:class="${#fields.hasErrors('password')}?'error_text'"
38 th:field="*{password}"/>
39 <!--<label th:text="${#fields.errors('password')}" 40 th:class="${#fields.hasErrors('password')} ? 'error'" 41 th:if="${#fields.hasErrors('password')}"></label> --><br/>
42 <label class="title" th:class="${#fields.hasErrors('email')} ? 'error'">邮箱:</label>
43 <input type="email" th:class="${#fields.hasErrors('email')}?'error_text'"
44 th:field="*{email}"/>
45 <!--<label th:text="${#fields.errors('email')}" 46 th:class="${#fields.hasErrors('email')} ? 'error'" 47 th:if="${#fields.hasErrors('email')}"></label>--><br/>
48 <div th:if="${#fields.hasErrors('*')}">
49 <ul>
50 <li th:each="error:${#fields.errors('*')}"
51 th:text="${error}"
52 class="error"
53 ></li>
54 </ul>
55 </div>
56 <button>提交</button>
57 </form>
58 </body>
59 </html>
--相比于JSP的JSTL标签库的使用,thymeleaf的标签库显得更加的简洁,<div>元素使用th:if属性来检查是否有校验错 误。若是有的话,会渲染<div>,不然的话,它将不会渲染。在<div>中,会使用一个无顺序的列表来展示每项错误。<li>标签 上的th:each属性将会通知Thymeleaf为每项错误都渲染一个<li>, 在每次迭代中会将当前错误设置到一个名为err的变量中。 <li>标签还有一个th:text属性。这个命令会通知Thymeleaf计算某 一个表达式(在本例中,也就是error变量)并将它的值渲染为<li> 标签的内容体。实际上的效果就是每项错误对应一个<li>元素,并 展示错误的文本。--你可能会想知道“${}”和“*{}”括起来的表达式到底有什么区 别。“${}”表达式(如${spitter})是变量表达式(variable expression)。通常来说,它们会是对象图导航语言(Object-Graph Navigation Language,OGNL)表达式.但在使用Spring 的时候,它们是SpEL表达式。在${spitter}这个例子中,它会解 析为key为spitter的model属性。而对于“*{}”表达式,它们是选择表达式(selection expression)。变 量表达式是基于整个SpEL上下文计算的,而选择表达式是基于某一 个选中对象计算的。在本例的表单中,选中对象就是<form>标签中 th:object属性所设置的对象:模型中的Spitter对象。所以,“* {firstName}”表达式就会计算为Spitter对象的firstName属性。