六、渲染Web视图

渲染Web视图

理解视图解析

在前面我们已经接触了一个Springmvc中的视图解析器,InternalResourceViewResolver。下图是其继承结构:

InternalResourceViewResolver:这个视图解析器应该不陌生,在SSM项目中整合JSP的时候,一般都会使用到这个视图解析器。下面这段代码应该也似曾相识,在SpringMVC.xml文件中都会配置到,这里只是以代码的方式进行配置,最熟悉的莫过于prefix 和 suffix ,分别是指定视图的前缀和后缀,即当视图解析器根据逻辑视图名映射视图的时候,会在Controller的返回值分别在其前后拼接配置好的前后缀,就形成了物理视图,比如Controller中返回的是home.其物理地址就是/WEB-INF/views/home.jsp 。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver resolver =
new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
/*将视图解析为JstlView
* 查看源码可以看出,默认解析为InternalResourceView
* */
resolver.setViewClass(JstlView.class);
return resolver;
}

我们看一下视图解析器返回的ViewResolver的代码:

1
2
3
4
public interface ViewResolver {
@Nullable
View resolveViewName(String viewName, Locale locale) throws Exception;
}

当给resolveViewName方法传入一个视图名和Locale对象时,会返回一个View实例,View是另外一个接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface View {

String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() +
".responseStatus";

String PATH_VARIABLES = View.class.getName() + ".pathVariables";

String SELECTED_CONTENT_TYPE = View.class.getName() +
".selectedContentType";

@Nullable
default String getContentType() {
return null;
}
void render(@Nullable Map<String, ?> model, HttpServletRequest
request, HttpServletResponse response)throws Exception;
}

View接口的任务就是接受模型以及Servlet的request和response对象,并将输出结果渲染到response中。

https://docs.spring.io/spring/docs/5.1.9.RELEASE/spring-framework-reference/web.html#mvc-viewresolver

spring官网介绍了几种视图解析器。InternalResourceViewResolver一般会用于JSP。TilesViewResolver用于Apache Tiles视图,而FreeMarkerViewResolver和VelocityViewResolver分别用于FreeMarker和Velocity模板视图。

解析JSTL视图

使用InternalResourceViewResolver进行视图解析,默认会将视图解析为InternalResourceView实例,但是如果我们在JSP文件中使用JSTL标签去处理了例如国际化的这个格式化和信息的化,我们希望将视图解析为JSTLView。JSTL的格式化标签需要一个Locale对象,以便于恰当的格式化地域相关的值,如日期和货币。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
....
<body>
<!-- <fmt:setLocale value="${param.setLocale}"/> 区域语言的值从传过来的参数中得到 -->
<fmt:setLocale value="en_US"/> <!--指定区域语言-->
<fmt:bundle basename="globalMessages"> <!-- 指定使用basename为globalMessages的资源文件,也即资源文件第一个单词为globalMessages-->
<center>
<table>
<tr>
<td><fmt:message key="email"/></td>
<td><input type="text" name="email"></td>
</tr>
</table>
</center>
</fmt:bundle>
</body>

如果我们想要将视图渲染为JSTL视图,只需要在下面添加配置:setViewClass即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
@Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver resolver =
new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
/*将视图解析为JstlView
* 查看源码可以看出,默认解析为InternalResourceView
* */
resolver.setViewClass(JstlView.class);
return resolver;
}

Spring的JSP库

​ Spring提供了两个JSP标签库,用来帮助定义SpringMVC Web的视图。其中一个标签库会用来渲染HTML表单标签,这些标签可以绑定model中的某个属性。另外一个标签库包含了一些工具类标签,我们随时都可以非常便利地使用它们。

JSP标签

借助Spring表单绑定标签库中所包含的标签,我们能够将模型对象绑定到渲染后的HTML表单中。

JSP标签 描述
<sf:checkbox> 渲染成一个HTML<input>标签,其中type属性设置为checkbox
<sf:checkboxs> 渲染成多个HTML<input>标签,其中type属性设置为checkbox
<sf:errors> 在一个HTML<span>中输入输入域的错误
<sf:form> 渲染成一个HTML<form>标签,并为其内部标签暴露绑定路径,用于数据绑定
<sf:hidden> 渲染成一个HTML<input>标签,其中type属性设置为hidden
<sf:input> 渲染成一个HTML<input>标签,其中type属性设置为text
<sf:label> 渲染成一个HTML<label>标签
<sf:option> 渲染成一个HTML<option>标签,其selected属性根据所绑定的执行设置
<sf:options> 按照绑定的集合、数组或Map,渲染成一个HTML<option>标签列表
<sf:password> 渲染成一个HTML<input>标签,其中type属性设置为password
<sf:radiobutton> 渲染成一个HTML<input>标签,其中type属性设置为radio
<sf:radiobuttons> 渲染成多个HTML<input>标签,其中type属性设置为radio
<sf:select> 渲染为一个HTML<select>标签
<sf:textarea> 渲染为一个HTML<textarea>标签

将标签绑定到模型上

如果在jsp中引入spring标签呢?

1
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>

sf:form会渲染成一个HTML

标签,但它也会通过 modelAttribute 属性构建针对某个模型对象的上下文信息。在其他的表单绑定标签,会引用这个模型对象的属性。

最初的注册表单:

1
2
3
4
5
6
<form method="post" >
用户名:<input name="username" type="text" ><br>
密码:<input name="password" type="password" ><br>
年龄:<input name="age" type="number" ><br>
<input type="submit" value="提交"><br>
</form>

使用Spring的标签库:

1
2
3
4
5
6
7
8
9
<sf:form method="post" modelAttribute="user" >
账号:<sf:input path="username"/>
<sf:errors path="username" cssClass="error" /><br>
密码:<sf:password path="password"/>
<sf:errors path="password" cssClass="error" /><br>
年龄:<sf:input path="age"/>
<sf:errors path="age" cssClass="error" /><br>
<input type="submit" value="提交" ><br>
</sf:form>

这里需要自定义一个error的css样式

1
2
3
span.error{
color:red;
}

如果不填数据提交,则会出现响应的错误提示。

为什么会出现这种提示信息呢?当然不仅仅是因为将前面的标签换成了Spring的标签。还有就是在Controller中绑定了对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@GetMapping("/register")
public String toRegister(Model model){
model.addAttribute("user",new User());
return "register";
}

/*处理表单数据,并验证*/
@PostMapping("/register")
public String register(@Valid User user, Errors errors){
if (errors.hasErrors()){
return "register";//注册失败,重新返回到注册页面
}
userService.saveUser(user);
return "redirect:/registerSuccess";
}

User:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class User implements Serializable {

@NotNull
@Size(min = 4,max = 20, message = "{username.size}")
private String username;

@NotNull
@Size(min = 6,max = 32,message = "密码需要在{min} 到 {max} 位之间")
private String password;

@NotNull(message = "年龄不能为空")
@Min(value = 1,message = "年龄要大于等于1")
@Max(value = 150,message = "年龄需要小于150")
private Integer age;

//all or noArgsConstructor
//getter and setter
//toString
}

可以看出,username的@size使用{}大括号来读取配置文件信息,解决了硬编码问题。由于这是输入数据验证,所以默认读取的是classpath下的,ValidationMessages.properties文件。如下:

1
username.size = 账号需要在{min} 到 {max} 位之间

而{min}和{max}可以读取到@size中的min和max属性。

如何将错误信息显示到一个地方呢?

1
2
3
4
5
6
7
<sf:form method="post" modelAttribute="user" >
<sf:errors element="div" path="*" cssClass="errors"/>
账号:<sf:input path="username"/><br>
密码:<sf:password path="password" /><br>
年龄:<sf:input path="age"/><br>
<input type="submit" value="提交" ><br>
</sf:form>

可以看出,将之前每一个输入框后都有一个对应的sf:errors 用来显示错误信息。而这里进行修改了,将错误信息全部放到了一个div元素中,并且path使用通配符*来描述。

并且css样式定义为了errors:

1
2
3
4
div.errors{
background-color: #ffcccc;
border: 2px solid red;
}

此时,测试结果如下:

通过上面的测试结果图片可以看出,此时虽然错误信息全部显示在一起了,但是并没有明显的显示是哪一个输入框或是输入属性填写有问题。下面,再次修改:

1
2
3
4
5
6
7
8
9
10
<sf:form method="post" modelAttribute="user" >
<sf:errors element="div" path="*" cssClass="errors"/>
<sf:label path="username" cssErrorClass="error">账号:</sf:label>
<sf:input cssErrorClass="error" path="username"/><br>
<sf:label path="password" cssErrorClass="error">密码:</sf:label>
<sf:password cssErrorClass="error" path="password" /><br>
<sf:label path="age" cssErrorClass="error">年龄:</sf:label>
<sf:input cssErrorClass="error" path="age"/><br>
<input type="submit" value="提交" ><br>
</sf:form>

如上代码,我们已经使用div类型的sf:errors来统一显示错误信息,并且将提示信息使用了sf:label进行修饰,使用path进行绑定数据,同时使用cssErrorClass属性来引用出现错误时的css样式。

1
2
3
4
5
6
label.error{
color:red;
}
input.error{
background-color: #ffcccc;
}

所以,上述的代码运行结果如:

至此,Spring的JSP标签就告一段落了。

Spring 通用的标签库

如何引入在JSP文件中引入spring通用的标签库?

1
<%@ taglib prefix="s" uri="http://www.springframework.org/tags" %>

以上标签大部分使用得很少,因为部分标签已被Spring所淘汰了。

展现国际化信息

​ 我们都知道,如果将文本内容硬编码到网页里面,就无法实现动态改变网页显示内容,比如我们在没学习JavaScript之前,构建网页只是使用了HTML+CSS,那么我们的页面一旦写好了,在运行过程中是无法动态修改的。而我们后台系统中,绝大部分数据是以图表的形式展示,而表格的数据是动态切换的,比如:表格分页。分页的时候,网页URL并没有变化,而数据却修改了。而我们如果需要实现国际化也是一样的。所有需要进行国际化编码显示的内容都不可以硬编码到网页中,那么如何实现呢?下面看看Spring提供的方式:

使用到的标签是上面表格中的<s:message code = “”>

home.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
<%@ taglib prefix="s" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Blog</title>
</head>
<body>

<h1><s:message code="article.welcome"/></h1>
</body>
</html>

配置文件中配置引用文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*查询类路径下的message.properties文件*/
/*@Bean
public MessageSource messageSource(){
ResourceBundleMessageSource messageSource =
new ResourceBundleMessageSource();
messageSource.setBasename("message");
return messageSource;
}*/

@Bean
public MessageSource messageSource(){
ReloadableResourceBundleMessageSource messageSource =
new ReloadableResourceBundleMessageSource();
messageSource.setBasenames("classpath:message");
messageSource.setCacheSeconds(10);
return messageSource;
}

上述配置类中,这两个类都可以使用,但是有一定的区别:ResourceBundleMessageSource是从classpath中查询message文件,但是ReloadableResourceBundleMessageSource既可以从文件系统file:/,也可以使用classpath来指定类路径下引用,或是web应用的根目录下(没有前缀)查找。

1
2
3
4
5
-- zh-CN:
article.welcome = 欢迎来到spring的大世界

-- en-US:
article.welcome = welcome to Spring's world

如上图所示:创建两个properties文件,名称如上,en-US表示英文,zh-CN表示中文,由于浏览器更改语言环境不方便,这里我们使用Postman来测试。如下:

注意:Accept-Language的value值,比如zh-CN,需要将-写成中划线,不能写成下划线,否则不会生效。

创建URL

链接对于HTML页面来说,是一种最寻常不过的元素了,下面我们来了解一下spring URL标签的神奇:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<%@ taglib prefix="s" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Blog</title>
</head>
<body>

<a href="<s:url value="/articles"/>" > 所有文章 </a> |
<a href="<s:url value="/register"/>"> 注册 </a>
<br>

<h2>使用spring的标签库</h2>
<%--定义一个变量,url的作用域默认在页面,但是可以通过scope进行修改--%>
<s:url value="/articles" scope="session" var="articlesUrl"/>
<%--在连接中使用,--%>
<a href="<s:url value="${articlesUrl}"/> ">所有文章</a><br>
<%--传参数--%>
<s:url value="/article" var="article" >
<s:param name="page" value="1"/>
<s:param name="size" value="2"/>
</s:url>
<a href="${article}">分页查询文章,page=1 size=2</a><br/>

<%--路径参数,如果有参数不匹配,则会转为普通参数传递。http://localhost:8080/article/3?name=ouyang--%>
<s:url value="/article/{id}" var="articleId">
<s:param name="id" value="3"/>
<s:param name="name" value="ouyang"/>
</s:url>
<a href="${articleId}">使用路径变量来获取文章{id = 3}</a>

</body>
</html>
  1. 普通使用

<a href=”<s:url value=”/articles”/>” >` ,使用spring url标签,jsp页面渲染时,会将项目名自动与/articles 拼接。当我们将鼠标放在所有文章上面,可以看到下面的提示:

即:单纯普通使用spring的url标签,与jstl标签类似,也会自动拼接上项目名。

  1. 定义成变量,设置scope
1
2
3
4
<%--定义一个变量,url的作用域默认在页面,但是可以通过scope进行修改--%>
<s:url value="/articles" scope="session" var="articlesUrl"/>
<%--在连接中使用,--%>
<a href="<s:url value="${articlesUrl}"/> ">所有文章</a><br>

上述两行代码则说明,可以先将连接独立成一个变量,然后在进行引用,如使用var来定义连接变量。则在需要使用的地方可以使用${}来引用。同时我们可以看到,可以进行scope的设置,即:设置链接的作用域,链接本身的默认的作用域是page,而我们可以设置为request,session,application等作用域。

  1. 查询参数传递
1
2
3
4
5
6
<%--传参数--%>
<s:url value="/article" var="article" >
<s:param name="page" value="1"/>
<s:param name="size" value="2"/>
</s:url>
<a href="${article}">分页查询文章,page=1 size=2</a><br/>

我们直接看效果:

  1. 传递路径参数
1
2
3
4
5
6
<%--路径参数,如果有参数不匹配,则会转为普通参数传递。http://localhost:8080/article/3?name=ouyang--%>
<s:url value="/article/{id}" var="articleId">
<s:param name="id" value="3"/>
<s:param name="name" value="ouyang"/>
</s:url>
<a href="${articleId}">使用路径变量来获取文章{id = 3}</a>

效果:

结合上述说明和效果图可以发现,我们可以使用{}来定义路径参数,如果存在参数匹配不到路径变量,则自动转为查询参数拼接到请求URL上。

转义内容

1
2
3
4
5
<%--内容转义--%>
<s:escapeBody htmlEscape="true">
<h1>这是转义的一级标题标签</h1>
</s:escapeBody>
<h1>这是没有转义的一级标题标签</h1>

我们可以使用<s:escapeBody>来进行内容转义。效果如下:

使用Thymeleaf

如今我们可以发现,jsp慢慢不再使用了,而转向了Thymeleaf,jsp本质来说不是HTML,并且它是依赖于Servlet的,这也就说明,JSP不能独立于Servlet,必须建立于基于Servlet的web容器上。JSP模板不能作为通用的模板(如格式化EMail),也不能用于非Servlet的web应用。

官网:https://www.thymeleaf.org/

Spring整合Thymeleaf

  1. 添加POM依赖。

可以进到Thymeleaf官网:https://www.thymeleaf.org/download.html

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>

此时,项目的完整POM文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.ooyhao.spring</groupId>
<artifactId>spring-in-action-06-01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>

<name>spring-in-action-06-01 Maven Webapp</name>
<url>http://www.example.com</url>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<!--导入SpringMVC依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>

<!--导入Servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>

<!--hibernate参数校验依赖-->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Alpha3</version>
</dependency>

<!--导入Jstl标签依赖-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

<!--导入Jackson依赖-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>

<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>

<!--Junit测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--整合Thymeleaf-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>

<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>
</dependencies>

<build>
<finalName>spring-in-action-06-01</finalName>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

配置整合Thymeleaf

在ServletConfig配置文件中配置相应的信息来整合Thymeleaf模板。

配置Thymeleaf视图解析器:

为了要在Spring中使用Thymeleaf,我们需要配置三个启用Thymeleaf与Spring集成的bean:

  • ThymeleafViewResolver:将逻辑视图名称解析为Thymeleaf模式视图;
  • SpringTemplateEngine:处理模板并渲染结果;
  • TemplateResolver:加载Thymeleaf模板;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*-------------配置Thymeleaf模板引擎------------*/
@Bean
public SpringResourceTemplateResolver templateResolver(){
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setPrefix("/WEB-INF/templates/");
resolver.setSuffix(".html");
resolver.setCharacterEncoding("utf-8");
resolver.setCacheable(true);
return resolver;
}

@Bean
public SpringTemplateEngine templateEngine(){
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
engine.setEnableSpringELCompiler(true);
return engine;
}

@Bean
public ThymeleafViewResolver viewResolver(){
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setCharacterEncoding("utf-8");
return resolver;
}

注意:需要在templateResolver和viewResolver中设置字符编码,否则会出现中文乱码的情况。

此时,ServletConfig.java的完整文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
/*相当于springmvc.xml*/
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.ooyhao.spring.**.controller")
public class ServletConfig implements WebMvcConfigurer {

/*配置JSP视图解析器*/
/*@Bean
public ViewResolver viewResolver(){
InternalResourceViewResolver resolver =
new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
*//*将视图解析为JstlView
* 查看源码可以看出,默认解析为InternalResourceView
* *//*
resolver.setViewClass(JstlView.class);
return resolver;
}*/


/*查询类路径下的message.properties文件*/
/*@Bean
public MessageSource messageSource(){
ResourceBundleMessageSource messageSource =
new ResourceBundleMessageSource();
messageSource.setBasename("message");
return messageSource;
}*/

@Bean
public MessageSource messageSource(){
ReloadableResourceBundleMessageSource messageSource =
new ReloadableResourceBundleMessageSource();
messageSource.setBasenames("classpath:message");
messageSource.setCacheSeconds(10);
return messageSource;
}

/*配置静态资源的处理*/
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}

/*使用Apache Tiles来进行布局,此处不做研究*/
/* @Bean
public TilesConfigurer tilesConfigurer(){
TilesConfigurer tilesConfigurer = new TilesConfigurer();
tilesConfigurer.setDefinitions("/WEB-INF/layout/tiles.xml");
tilesConfigurer.setCheckRefresh(true);
return tilesConfigurer;
}

@Bean
public TilesViewResolver tilesViewResolver(){
return new TilesViewResolver();
}*/

/*-------------配置Thymeleaf模板引擎------------*/
@Bean
public SpringResourceTemplateResolver templateResolver(){
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setPrefix("/WEB-INF/templates/");
resolver.setSuffix(".html");
resolver.setCharacterEncoding("utf-8");
resolver.setCacheable(true);
return resolver;
}

@Bean
public SpringTemplateEngine templateEngine(){
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver());
engine.setEnableSpringELCompiler(true);
return engine;
}

@Bean
public ThymeleafViewResolver viewResolver(){
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setCharacterEncoding("utf-8");
return resolver;
}
}

通过上述配置 Thymeleaf templateResolver 视图解析器,与之前配置JSP视图解析器类似,都是通过逻辑视图名来定位文件,但是这里需要依赖templateEngine。

接下来按照上述代码配置或图中所示的位置编写相应的html页面即可。

Thymeleaf实现表单绑定

html文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<title>Blog</title>
<meta charset="utf-8">
</head>

<style>
span.error{
color:red;
}
div.errors{
background-color: #ffcccc;
border: 2px solid red;
}
label.error{
color:red;
}
input.error{
background-color: #ffcccc;
}
</style>

</head>
<body>

<h1>欢迎加入Spring的大家庭</h1>

<form method="post" th:object="${user}" >

<div class="errors" th:if="${#fields.hasErrors('*')}" >
<ul>
<li th:each="err : ${#fields.errors('*')}" th:text="${err}" >
Input is Incorrect
</li>
</ul>
</div>
<label th:class="${#fields.hasErrors('username')} ? 'error' ">账号:</label>
<input type="text" th:field="*{username}" th:class="${#fields.hasErrors('username')} ? 'error'"/><br>

<label th:class="${#fields.hasErrors('password')} ? 'error' ">密码:</label>
<input type="password" th:field="*{password}" th:class="${#fields.hasErrors('password')} ? 'error'" /><br>

<label th:class="${#fields.hasErrors('age')} ? 'error' ">年龄:</label>
<input th:field="*{age}" th:class="${#fields.hasErrors('age')} ? 'error'"/><br>

<input type="submit" value="提交" ><br>
</form>
</body>
</html>

可以看出:代码中是通过使用fields.hasErrors以及后台的数据校验来判断是否有错误的。后台代码与之前的jsp略有不同:

Controller文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//jsp
//处理表单数据,并验证*//*
@PostMapping("/register")
public String register(@Valid User user, Errors errors){
if (errors.hasErrors()){
return "register";//注册失败,重新返回到注册页面
}
userService.saveUser(user);
return "redirect:/registerSuccess";
}

//thymeleaf
/*处理表单数据,并验证*/
@PostMapping("/register")
public String register(@Valid User user, BindingResult bindingResult,Model model){
if (bindingResult.hasErrors()){
System.out.println("错误数目:" + bindingResult.getErrorCount());
model.addAttribute(user);
return "register";//注册失败,重新返回到注册页面
}
userService.saveUser(user);
return "redirect:/registerSuccess";
}

jsp使用spring的标签时是用Errors来判断,而html使用Thymeleaf时是使用BindingResult来进行判断。上述代码显示了不同之处。

效果图

总结:

​ 至此,本节已经学习了Spring如何整合JSP,使用Spring的标签,同时也接触了当前正在逐渐替代JSP的Thymeleaf,也实现了之前使用JSP同样地表单双向绑定的效果。

使用Thymeleaf实现表单双向绑定参考了

https://blog.csdn.net/z28126308/article/details/54429853

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×