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 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属性.但是,对于我们来说,此外,在使用
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的标签来完成我们错误信息的回显功能,因此我们需要使用到
--这里我们需要特别注意一些问题:如果我们想要保证成功映射到错误信息,那么我所映射的类的变量名必须是标准化的默认命名(类名首字母小写),例如我们所映射的是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 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中我们并没有去定义
--但是此时我们必然会思考一个问题,如果我们有两个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 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...)
--除了可以在每一个输入框之后都追加一个相应的错误信息的处理,我们也可以在底部,留下一个所有错误信息的展示,此时对于
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>
--观察此时的输出内容以及标签的组成,我们可以发现在
换行标签,显然这样的展示效果,也是我们所期望的.此外,我们还可以着重显示需要修改的输入域,为了实现这一操作我们可以使用CSSErrorClass属性来完成,同时我们借助
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标签支持:
--在其中例如
--而后,我们需要在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页面中,我们可以使用
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="/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
--按照其最简单的形式,
<script type="text/javascript" src="
--另外,我们还可以使用
<script type="text/javascript" src="${scriptSrc}"></script>
-- 默认情况下,URL是在页面作用域内创建的。但是通过设置scope属 性,我们可以让
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>
--同时我们还可以使用
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>
--以上功能我们使用
1 <s:url value="/homePath/{username}" var="pathVariable">
2 <s:param name="username">路径参数:Javas:param>
3 s:url>
--当我们所指定的value属性中的占位符匹配
1 <br/>
2 <s:url value="/homePath/{username}" htmlEscape="true">
3 <s:param name="username">在界面上展现该链接地址s:param>
4 s:url>
--可以发现即使我们使用了中文字符,也能很好的进行转义.同时我们也可以使用JS将htmlEscape属性设置为true的链接地址进行变量赋值,从而达到某些操作的目的.
--转义内容
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.xmlvalue>
11 <value>/WEB-INF/**/tiles.xmlvalue>
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的定义。每个定义中需要包含一 个
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>
--每个
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>Titletitle>
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>junitgroupId>
5 <artifactId>junitartifactId>
6 <version>4.11version>
7 <scope>testscope>
8 dependency>
9
10 <dependency>
11 <groupId>javax.servletgroupId>
12 <artifactId>javax.servlet-apiartifactId>
13 <version>3.1.0version>
14 <scope>providedscope>
15 dependency>
16 <dependency>
17 <groupId>javax.servlet.jsp.jstlgroupId>
18 <artifactId>jstl-apiartifactId>
19 <version>1.2version>
20 dependency>
21 <dependency>
22 <groupId>taglibsgroupId>
23 <artifactId>standardartifactId>
24 <version>1.1.2version>
25 dependency>
26
27 <dependency>
28 <groupId>javax.servlet.jsp.jstlgroupId>
29 <artifactId>jstlartifactId>
30 <version>1.2version>
31 dependency>
32
33 <dependency>
34 <groupId>org.springframeworkgroupId>
35 <artifactId>spring-webmvcartifactId>
36 <version>4.3.4.RELEASEversion>
37 dependency>
38
39 <dependency>
40 <groupId>org.hibernate.validatorgroupId>
41 <artifactId>hibernate-validatorartifactId>
42 <version>6.0.13.Finalversion>
43 dependency>
44
45 <dependency>
46 <groupId>org.apache.tilesgroupId>
47 <artifactId>tiles-jspartifactId>
48 <version>3.0.8version>
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
2 <dependency>
3 <groupId>org.thymeleafgroupId>
4 <artifactId>thymeleaf-spring4artifactId>
5 <version>3.0.9.RELEASEversion>
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>titletitle>
7
8
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
--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>titletitle>
7
8
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')}">XXXlabel>
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>
--在上述的例子中我们使用了较多的表达式完成了整体的实际,在
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>titletitle>
7
8
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
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 <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 <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的标签库显得更加的简洁,
--你可能会想知道“${}”和“*{}”括起来的表达式到底有什么区 别。“${}”表达式(如${spitter})是变量表达式(variable expression)。一般来讲,它们会是对象图导航语言(Object-Graph Navigation Language,OGNL)表达式.但在使用Spring 的时候,它们是SpEL表达式。在${spitter}这个例子中,它会解 析为key为spitter的model属性。而对于“*{}”表达式,它们是选择表达式(selection expression)。变 量表达式是基于整个SpEL上下文计算的,而选择表达式是基于某一 个选中对象计算的。在本例的表单中,选中对象就是