Spring学习(六)--渲染Web视图

一.将模型数据渲染为Html
  在上一篇文章中,我们所编写的控制器方法都没有直接产生浏览器中渲染所需的HTML.这些方法只是将数据填充到模型中,然后将模型传递给一个用来渲染的视图.这些方法会返回一个String类型的值,这个值是视图的逻辑名称,不是引用直接的视图实现.尽管我们也编写了几个简单的JavaServerPage(JSP)视图,但是控制器并不关心这些.将控制器中请求处理的逻辑和视图中的渲染实现解耦是Spring MVC的 一个重要特性。如果控制器中的方法直接负责产生HTML的话,就很 难在不影响请求处理逻辑的前提下,维护和更新视图。控制器方法和 视图的实现会在模型内容上达成一致,这是两者的最大关联,除此之 外,两者应该保持足够的距离。
  但是,如果控制器只通过逻辑视图名来了解视图的话,那Spring该如 何确定使用哪一个视图实现来渲染模型呢?这就是Spring视图解析器 的任务了。在上一篇文章中,我们使用名为InternalResourceViewResolver的 视图解析器。在它的配置中,为了得到视图的名字,会使用“/WEB- INF/views/”前缀和“.jsp”后缀,从而确定来渲染模型的JSP文件的物理 位置。现在我们来了解视图解析器的基础知识以及Spring提供的其他视图解析器:
  在Spring之中,定义了名为ViewResolver的接口:

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接口定义如下:

 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对于这些接口也提供了一些内置的实现:

 

--事实上Spring所提供的视图解析器在实际的应用中,我们只会用到其中很少的一部分,每一项都对应Java Web应用中 特定的某种视图技术。InternalResourceViewResolver一般会 用于JSP,TilesViewResolver用于Apache Tiles视图,而FreeMarkerViewResolver和VelocityViewResolver分别 对应FreeMarker和Velocity模板视图。

二.创建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是最简单和最常用的视图 解析器。

--配置适用于JSP的视图解析器
  有一些视图解析器,如ResourceBundleViewResolver会直接将 逻辑视图名映射为特定的View接口实现, 而InternalResourceViewResolver所采取的方式并不那么直 接。它遵循一种约定,会在视图名上添加前缀和后缀,进而确定一个 Web应用中视图资源的物理路径。
  考虑一个简单的场景,假设逻辑视图名为home。通用的 实践是将JSP文件放到Web应用的WEB-INF目录下,防止对它的直接 访问。如果我们将所有的JSP文件都放在“/WEB-INF/views/”目录下, 并且home页的JSP名为home.jsp,那么我们可以确定物理视图的路径 就是逻辑视图名home再加上“/WEB-INF/views/”前缀和“.jsp”后缀:

   当使用@Bean注解的时候,我们可以按照如下的方式配 置InternalResourceView Resolver,使其在解析视图时,遵循上述的约定:

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:

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”.

--解析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代码片段。为了正确显 示,我们需要将“<”和“>”字符替换为“&lt;”和“&gt;”,否则的话, 浏览器将会像解析页面上其他HTML那样解析这段HTML内容。 当然,没有人禁止我们手动将其转义为“&lt;”和“&gt;”,但是这很 烦琐,并且代码难以阅读。我们可以使用<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属性。

猜你喜欢

转载自www.cnblogs.com/skykuqi/p/11508724.html