一.將模型數據渲染爲Html
在上一篇文章中,咱們所編寫的控制器方法都沒有直接產生瀏覽器中渲染所需的HTML.這些方法只是將數據填充到模型中,而後將模型傳遞給一個用來渲染的視圖.這些方法會返回一個String類型的值,這個值是視圖的邏輯名稱,不是引用直接的視圖實現.儘管咱們也編寫了幾個簡單的JavaServerPage(JSP)視圖,可是控制器並不關心這些.將控制器中請求處理的邏輯和視圖中的渲染實現解耦是Spring MVC的 一個重要特性。若是控制器中的方法直接負責產生HTML的話,就很 難在不影響請求處理邏輯的前提下,維護和更新視圖。控制器方法和 視圖的實現會在模型內容上達成一致,這是二者的最大關聯,除此之 外,二者應該保持足夠的距離。
可是,若是控制器只經過邏輯視圖名來了解視圖的話,那Spring該如 何肯定使用哪個視圖實現來渲染模型呢?這就是Spring視圖解析器 的任務了。在上一篇文章中,咱們使用名爲InternalResourceViewResolver的 視圖解析器。在它的配置中,爲了獲得視圖的名字,會使用「/WEB- INF/views/」前綴和「.jsp」後綴,從而肯定來渲染模型的JSP文件的物理 位置。如今咱們來了解視圖解析器的基礎知識以及Spring提供的其餘視圖解析器:
在Spring之中,定義了名爲ViewResolver的接口:javascript
1 package org.springframework.web.servlet; 2
3 import java.util.Locale; 4
5 public interface ViewResolver { 6
7 View resolveViewName(String viewName, Locale locale) throws Exception; 8
9 }
--當給resolveViewName()方法傳入一個視圖名和Locale對象時,他會返回一個View實例,View接口定義以下:css
1 /*
2 * Copyright 2002-2012 the original author or authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0
9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */
16
17 package org.springframework.web.servlet; 18
19 import java.util.Map; 20 import javax.servlet.http.HttpServletRequest; 21 import javax.servlet.http.HttpServletResponse; 22
23 import org.springframework.http.MediaType; 24
25 public interface View { 26
27 String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus"; 28
29 String PATH_VARIABLES = View.class.getName() + ".pathVariables"; 30
31 String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType"; 32
33 String getContentType(); 34
35 void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception; 36
37 }
--View接口的任務就是接受模型以及Servlet的request和Response對象,並將輸出結果渲染到response中.進而展示到用戶的瀏覽器中.實際上咱們不須要這麼麻煩,儘管咱們能夠編寫ViewResolver和view的實現,在有些特定的場景下,這樣作也是有必要的,可是通常來說,咱們並不須要關心這些接口.Spring對於這些接口也提供了一些內置的實現:html
--事實上Spring所提供的視圖解析器在實際的應用中,咱們只會用到其中不多的一部分,每一項都對應Java Web應用中 特定的某種視圖技術。InternalResourceViewResolver通常會 用於JSP,TilesViewResolver用於Apache Tiles視圖,而FreeMarkerViewResolver和VelocityViewResolver分別 對應FreeMarker和Velocity模板視圖。java
二.建立JSP視圖
JavaServer Pages做爲Java Web應用程序的視圖技術已經有了很是長的時間.Spring提供了兩種支持JSP視圖的方式:
1.InternalResourceViewResolver會將視圖名解析爲JSP文件.另外,若是你的JSP頁面只使用了JSP標準標籤庫(JavaServer Pages Standard Tag Library,JSTL)的話,InternalResourceViewResolver可以將視圖名解析爲 JstlView形式的JSP文件,從而將JSTL本地化和資源bundle變量暴 露給JSTL的格式化(formatting)和信息(message)標籤。
2.Springg提供了兩個JSP標籤庫,一個用於表單到模型的綁定,另外一 個提供了通用的工具類特性。
例
--無論你使用JSTL,仍是準備使用Spring的JSP標籤庫,配置解析JSP的視圖解析器都是很是重要的。儘管Spring還有其餘的幾個視圖解析器 都能將視圖名映射爲JSP文件,但就這項任務來 講,InternalResourceViewResolver是最簡單和最經常使用的視圖 解析器。jquery
--配置適用於JSP的視圖解析器
有一些視圖解析器,如ResourceBundleViewResolver會直接將 邏輯視圖名映射爲特定的View接口實現, 而InternalResourceViewResolver所採起的方式並不那麼直 接。它遵循一種約定,會在視圖名上添加前綴和後綴,進而肯定一個 Web應用中視圖資源的物理路徑。
考慮一個簡單的場景,假設邏輯視圖名爲home。通用的 實踐是將JSP文件放到Web應用的WEB-INF目錄下,防止對它的直接 訪問。若是咱們將全部的JSP文件都放在「/WEB-INF/views/」目錄下, 而且home頁的JSP名爲home.jsp,那麼咱們能夠肯定物理視圖的路徑 就是邏輯視圖名home再加上「/WEB-INF/views/」前綴和「.jsp」後綴:web
當使用@Bean註解的時候,咱們能夠按照以下的方式配 置InternalResourceView Resolver,使其在解析視圖時,遵循上述的約定:spring
1 @Bean 2 public ViewResolver viewResolver() { 3 //配置JSP視圖解析器
4 InternalResourceViewResolver resolver = new InternalResourceViewResolver(); 5 resolver.setPrefix("/WEB-INF/views/"); //設置jsp所在的目錄
6 resolver.setSuffix(".jsp"); //設置後綴名稱
7 resolver.setExposeContextBeansAsAttributes(true); 8 return resolver; 9 }
做爲替代方案,若是你更喜歡使用基於XML的Spring配置,那麼能夠按照以下的方式配置InternalResourceViewResolver:express
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
5 <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
6 p:prefix="/WEB-INF/views" p:suffix=".jsp"/>
7 </beans>
--當咱們完成了InternalResourceViewResolver的配置以後,他就會將邏輯視圖解析爲JSP文件:
1.home將會解析爲「/WEB-INF/views/home.jsp」;
2.productList將會解析爲「/WEB-INF/views/productList.jsp」;
3.books/detail將會解析爲「/WEB-INF/views/books/detail.jsp」.apache
--解析JSTL視圖
到目前爲止,咱們對InternalResourceViewResolver的配置都 很基礎和簡單。它最終會將邏輯視圖名解析 爲InternalResourceView實例,這個實例會引用JSP文件。可是 若是這些JSP使用JSTL標籤來處理格式化和信息的話,那麼咱們會希 望InternalResourceViewResolver將視圖解析爲JstlView。
JSTL的格式化標籤須要一個Locale對象,以便於恰當地格式化地域 相關的值,如日期和貨幣。信息標籤能夠藉助Spring的信息資源和 Locale,從而選擇適當的信息渲染到HTML之中。經過解析 JstlView,JSTL可以得到Locale對象以及Spring中配置的信息資源。
若是想讓InternalResourceViewResolver將視圖解析 爲JstlView,而不是InternalResourceView的話,那麼咱們只需設置它的viewClass屬性便可:設計模式
1 package com.sky.config; 2
3 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.context.annotation.Configuration; 6 import org.springframework.web.servlet.ViewResolver; 7 import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; 8 import org.springframework.web.servlet.config.annotation.EnableWebMvc; 9 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 10 import org.springframework.web.servlet.view.InternalResourceViewResolver; 11 import org.springframework.web.servlet.view.JstlView; 12
13
14 /**
15 * @author : S K Y 16 * @version :0.0.1 17 */
18 @Configuration 19 @EnableWebMvc //啓用Spring MVC
20 @ComponentScan(basePackages = {"com.sky.*"}) //啓用組件掃描
21 public class WebConfig extends WebMvcConfigurerAdapter { 22 @Bean 23 public ViewResolver viewResolver() { 24 //配置JSP視圖解析器
25 InternalResourceViewResolver resolver = new InternalResourceViewResolver(); 26 resolver.setPrefix("/WEB-INF/views/"); //設置jsp所在的目錄
27 resolver.setSuffix(".jsp"); //設置後綴名稱
28 resolver.setViewClass(JstlView.class); 29 resolver.setExposeContextBeansAsAttributes(true); 30 return resolver; 31 } 32
33 @Override 34 public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { 35 configurer.enable(); //配置靜態資源的處理
36 } 37
38 }
--一樣咱們也能夠在XML中完成這一項配置
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
4 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
5 <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
6 p:prefix="/WEB-INF/views" p:suffix=".jsp"
7 p:viewClass="org.springframework.web.servlet.view.JstlView"/>
8 </beans>
--使用Spring的JSP庫
當爲JSP添加功能時,標籤庫是一種很強大的方式,可以避免在腳本 塊中直接編寫Java代碼。Spring提供了兩個JSP標籤庫,用來幫助定義 Spring MVC Web的視圖。其中一個標籤庫會用來渲染HTML表單標 籤,這些標籤能夠綁定model中的某個屬性。另一個標籤庫包含了 一些工具類標籤,咱們隨時均可以很是便利地使用它們。
咱們能夠將Spittle應用的註冊表單綁定到模型上,這樣表單就能夠預先填充值,而且在表單提交上失敗後,可以展示校驗錯誤.
--Spring的表單綁定JSP標籤庫包含了14個標籤,它們中的大多數都用來 渲染HTML中的表單標籤。可是,它們與原生HTML標籤的區別在於 它們會綁定模型中的一個對象,可以根據模型中對象的屬性填充值。 標籤庫中還包含了一個爲用戶展示錯誤的標籤,它會將錯誤信息渲染 到最終的HTML之中。爲了使用表單綁定庫,須要在JSP頁面中對其進行聲明:<%@taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>;Spring提供了14個相關的標籤:
--咱們能夠在咱們的JSP界面中使用這些標籤:
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2 <%@taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
3 <%--<script src="${pageContext.request.contextPath}/js/jquery-1.11.3.min.js" ></script>--%>
4 <html>
5 <head>
6 <title>Spring標籤庫的使用</title>
7 </head>
8 <style type="text/css">
9 .reg_form {
10 width: 210px;
11 padding: 5px;
12 display: inline-block;
13 border: 1px solid black; /*設置邊框*/
14 }
15
16 .text {
17 display: block;
18 width: 180px;
19 height: 20px;
20 margin: 20px 10px 0 10px;
21 }
22
23 .submit {
24 width: 100px;
25 height: 20px;
26 margin: 20px 50px 0 50px;
27 }
28 </style>
29 <body>
30 <body>
31 <sf:form method="post" commandName="registerForm" cssClass="reg_form">
32 用戶名: <sf:input path="username" cssClass="text"/>
33 密碼: <sf:password path="password" cssClass="text"/>
34 <button class="submit">點擊註冊</button>
35 </sf:form>
36 </body>
37 </html>
--其基本的使用能夠還能夠指定cssClass屬性.其做用至關於咱們所使用的原生HTML標籤中的class屬性.可是,對於咱們來講,此外,在使用<sf:input/>標籤時可以容許咱們指定type屬性,例如HTML5中的date,range,email:
1 <body>
2 <sf:form method="post" commandName="registerForm" cssClass="reg_form">
3 用戶名: <sf:input path="username" cssClass="text"/>
4 密碼: <sf:password path="password" cssClass="text"/>
5 郵箱: <sf:input path="email" cssClass="text" type="email"/>
6 <button class="submit">點擊註冊</button>
7 </sf:form>
8 </body>
--由於咱們使用commandName="registerForm"指定了該屬性對象,因此咱們須要存在一個key爲registerForm的對象,不然的話,咱們的表單將沒法正常渲染(會出現JSP錯誤).所以咱們修改Controller的實現:
1 @RequestMapping(value = "/registerForm", method = RequestMethod.GET) 2 public String showRegistrationForm(Model model) { 3 model.addAttribute("registerForm", new User()); 4 return "registerForm"; 5 } 6
7 @RequestMapping(value = "/registerForm", method = RequestMethod.POST) 8 public String postRegistrationForm(Model model, @Valid User user, Errors errors) throws UnsupportedEncodingException { 9 if (errors.hasErrors()) { 10 model.addAttribute("registerForm", user); 11 return "registerForm"; 12 } else { 13 String encode = URLEncoder.encode(user.getUsername(), "UTF-8"); //對於中文字符,須要進行轉化
14 return "redirect:/homepage?username=" + encode; //重定向到咱們的基本信息頁
15 } 16 }
--此時咱們在GET方法中,直接傳遞了一個空的key爲registerForm的User對象,而在POST方法中咱們則使用了JSR303校驗,此時,若是咱們輸入的信息有錯誤的話,那麼回從新跳轉到當前的註冊JSP中,當順利經過校驗,則會跳轉到咱們的首頁,可是仍是存在必定的缺陷,如今咱們在註冊失敗的時候雖然成功實現了咱們當前的數據回顯功能,可是用戶依然不知道他所填寫的信息在哪一個環節出錯了,咱們但願的是,能夠經過Spring的標籤來完成咱們錯誤信息的回顯功能,所以咱們須要使用到<sf:errors>標籤
--這裏咱們須要特別注意一些問題:若是咱們想要保證成功映射到錯誤信息,那麼我所映射的類的變量名必須是標準化的默認命名(類名首字母小寫),例如咱們所映射的是User類的校驗,那麼咱們必須保證User類的變量名爲user,而且在使用Model傳遞User信息時,也必須指定爲user.(在這裏須要說明使用Spring的form標籤,那麼commandName標籤所尋找的並非請求的地址名稱,而是咱們的變量名稱),將咱們的JSP頁面以及Controller,User類作出以下修改:
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2 <%@taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
3 <%--<script src="${pageContext.request.contextPath}/js/jquery-1.11.3.min.js" ></script>--%>
4
5 <!DOCTYPE html>
6 <html>
7 <head>
8 <title>Spring標籤庫的使用</title>
9 </head>
10 <style type="text/css">
11 .reg_form {
12 width: 210px;
13 padding: 5px;
14 display: inline-block;
15 border: 1px solid black; /*設置邊框*/
16 }
17
18 .text {
19 display: block;
20 width: 180px;
21 height: 20px;
22 margin: 20px 10px 0 10px;
23 }
24
25 .submit {
26 width: 100px;
27 height: 20px;
28 margin: 20px 50px 0 50px;
29 }
30
31 .error {
32 display: block;
33 color: red;
34 }
35 </style>
36 <body>
37 <sf:form method="POST" commandName="user" cssClass="reg_form">
38 用戶名: <sf:input path="username" cssClass="text"/> <sf:errors path="username" cssClass="error"/>
39 密碼: <sf:password path="password" cssClass="text"/> <sf:errors path="password" cssClass="error"/>
40 郵箱: <sf:input path="email" cssClass="text" type="email"/> <sf:errors path="email" cssClass="error"/>
41 <button class="submit">點擊註冊</button>
42 </sf:form>
43 </body>
44 </html>
1 @RequestMapping(value = "/registerForm", method = RequestMethod.GET) 2 public String showRegistrationForm(Model model) { 3 model.addAttribute("user", new User()); 4 return "registerForm"; 5 } 6
7 @RequestMapping(value = "/registerForm", method = RequestMethod.POST) 8 public String postRegistrationForm(Model model, @Validated User user, Errors errors) throws UnsupportedEncodingException { 9 if (errors.hasErrors()) { 10 model.addAttribute("user", user); 11 List<ObjectError> allErrors = errors.getAllErrors(); 12 for (ObjectError error : allErrors) { 13 System.out.println("錯誤信息: " + error.getDefaultMessage()); 14 // errors.rejectValue("password", "Enter password");
15 } 16 return "registerForm"; 17 } else { 18 String encode = URLEncoder.encode(user.getUsername(), "UTF-8"); //對於中文字符,須要進行轉化
19 return "redirect:/homepage?username=" + encode; //重定向到咱們的基本信息頁
20 } 21 }
1 package com.sky.spittle; 2
3 import javax.validation.constraints.Email; 4 import javax.validation.constraints.NotNull; 5 import javax.validation.constraints.Size; 6
7 /**
8 * @author : S K Y 9 * @version :0.0.1 10 */
11 public class User { 12 @NotNull 13 @Size(min = 5, max = 15, message = "用戶名長度必須在5-15之間") //設置當Size標籤沒有經過校驗時默認錯誤信息
14 private String username; 15 @NotNull 16 @Size(min = 5, max = 15, message = "密碼長度必須在5-15之間") 17 private String password; 18 @Email 19 @NotNull 20 @Size(min = 1,message = "email郵箱不能爲空") 21 private String email; 22
23 public String getUsername() { 24 return username; 25 } 26
27 public void setUsername(String username) { 28 this.username = username; 29 } 30
31 public String getPassword() { 32 return password; 33 } 34
35 public void setPassword(String password) { 36 this.password = password; 37 } 38
39 public String getEmail() { 40 return email; 41 } 42
43 public void setEmail(String email) { 44 this.email = email; 45 } 46
47 @Override 48 public String toString() { 49 return "User{" +
50 "username='" + username + '\'' +
51 ", password='" + password + '\'' +
52 ", email='" + email + '\'' +
53 '}'; 54 } 55 }
--咱們能夠發現此時在JSP中咱們並無去定義<sf:from>標籤所訪問的地址信息,而是定義了/registerForm地址所映射的User類的變量名稱,此時咱們直接提交空表單,將有以下的效果:
--可是此時咱們必然會思考一個問題,若是咱們有兩個Controller方法同時都是用了user映射,會使什麼樣的結果:
1 @RequestMapping(value = "/registerForm", method = RequestMethod.POST) 2 public String postRegistrationForm(Model model, @Validated User user, Errors errors) throws UnsupportedEncodingException { 3 if (errors.hasErrors()) { 4 model.addAttribute("user", user); 5 List<ObjectError> allErrors = errors.getAllErrors(); 6 for (ObjectError error : allErrors) { 7 System.out.println("錯誤信息: " + error.getDefaultMessage()); 8 // errors.rejectValue("password", "Enter password");
9 } 10 return "registerForm"; 11 } else { 12 String encode = URLEncoder.encode(user.getUsername(), "UTF-8"); //對於中文字符,須要進行轉化
13 return "redirect:/homepage?username=" + encode; //重定向到咱們的基本信息頁
14 } 15 } 16
17 @RequestMapping(value = "/abc/test", method = RequestMethod.GET) 18 public String secondRegistrationForm(Model model) { 19 model.addAttribute("user", new User()); 20 return "registerForm"; 21 } 22
23 @RequestMapping(value = "/abc/test", method = RequestMethod.POST) 24 public String secondRegistrationForm(Model model, @Validated User user, Errors errors) throws UnsupportedEncodingException { 25 if (errors.hasErrors()) { 26 model.addAttribute("user", user); 27 return "registerForm"; 28 } else { 29 String encode = URLEncoder.encode(user.getUsername(), "UTF-8"); //對於中文字符,須要進行轉化
30 return "redirect:/homepage?username=" + encode; //重定向到咱們的基本信息頁
31 } 32 }
--咱們能夠發現此時這兩個方法只有兩個區別:1.請求映射的地址不一樣,2.沒有進行遍歷錯誤信息的控制檯打印,咱們再次運行查看結果:
--當咱們是經過/abc/test路徑訪問咱們的註冊頁面時,咱們直接點擊提交,能夠發現控制檯不存在打印的信息
--爲了做爲區別,我在調用/abc/test請求地址的POST方法時,增長了如上圖所示的打印信息,那麼咱們再經過/registerFrom地址訪問查看結果:
--那麼如今咱們能夠明白,Spring的form標籤在提交表單的時候,是依據了咱們訪問該地址時所映射的地址來尋找POST請求,這樣一來也就不存在咱們所謂的衝突問題了,可是若是說咱們沒有定義/registerForm的POST請求方法會怎麼樣呢,此時很遺憾,會直接返回給咱們一個錯誤的信息界面405
--固然這個問題咱們在進行開發設計的時候就可以避免出現(除非你是惡意的想要留下bug...)
--除了能夠在每個輸入框以後都追加一個相應的錯誤信息的處理,咱們也能夠在底部,留下一個全部錯誤信息的展現,此時對於<sf:error>標籤的path屬性,咱們應當定義爲"*",同時<sf:error>標籤的默認展現是使用<span>標籤來完成的,咱們也可使用<sf:error>標籤的element屬性來定義使用"div"等形式展示:
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2 <%@taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
3 <%--<script src="${pageContext.request.contextPath}/js/jquery-1.11.3.min.js" ></script>--%>
4
5 <!DOCTYPE html>
6 <html>
7 <head>
8 <title>Spring標籤庫的使用</title>
9 </head>
10 <style type="text/css">
11 .reg_form {
12 width: 210px;
13 padding: 5px;
14 display: inline-block;
15 border: 1px solid black; /*設置邊框*/
16 }
17
18 .text {
19 display: block;
20 width: 180px;
21 height: 20px;
22 margin: 20px 10px 0 10px;
23 }
24
25 .submit {
26 width: 100px;
27 height: 20px;
28 margin: 20px 50px 0 50px;
29 }
30
31 .error {
32 display: block;
33 color: red;
34 }
35 </style>
36 <body>
37 <sf:form method="post" commandName="user" cssClass="reg_form">
38 用戶名: <sf:input path="username" cssClass="text"/>
39 密碼: <sf:password path="password" cssClass="text"/>
40 郵箱: <sf:input path="email" cssClass="text" type="email"/>
41 <sf:errors path="*" element="div" cssClass="error"/>
42 <button class="submit">點擊註冊</button>
43 </sf:form>
44 </body>
45 </html>
--觀察此時的輸出內容以及標籤的組成,咱們能夠發如今<sf:error>標籤進行輸出的時候,會自動的追加<br/>換行標籤,顯然這樣的展現效果,也是咱們所指望的.此外,咱們還能夠着重顯示須要修改的輸入域,爲了實現這一操做咱們可使用CSSErrorClass屬性來完成,同時咱們藉助<sf:label>變遷,對咱們的輸入提示信息也做出處理:
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2 <%@taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
3 <%--<script src="${pageContext.request.contextPath}/js/jquery-1.11.3.min.js" ></script>--%>
4
5 <!DOCTYPE html>
6 <html>
7 <head>
8 <title>Spring標籤庫的使用</title>
9 </head>
10 <style type="text/css">
11 .reg_form {
12 width: 210px;
13 padding: 5px;
14 display: inline-block;
15 border: 1px solid black; /*設置邊框*/
16 }
17
18 .text {
19 display: block;
20 width: 180px;
21 height: 20px;
22 margin: 20px 10px 0 10px;
23 }
24
25 .submit {
26 width: 100px;
27 height: 20px;
28 margin: 20px 50px 0 50px;
29 }
30
31 .error {
32 display: block;
33 color: red;
34 }
35
36 .label_error {
37 display: block;
38 color: red;
39 }
40
41 .input_error {
42 border: 1px solid red;
43 }
44 </style>
45 <body>
46 <sf:form method="post" commandName="user" cssClass="reg_form">
47 <sf:label path="username" cssErrorClass="label_error">用戶名: </sf:label>
48 <sf:input path="username" cssClass="text" cssErrorClass="input_error"/>
49 <sf:label path="password" cssErrorClass="label_error">密碼: </sf:label>
50 <sf:password path="password" cssClass="text" cssErrorClass="input_error"/>
51 <sf:label path="email" cssErrorClass="label_error">郵箱: </sf:label>
52 <sf:input path="email" cssClass="text" type="email" cssErrorClass="input_error"/>
53 <sf:errors path="*" element="div" cssClass="error"/>
54 <button class="submit">點擊註冊</button>
55 </sf:form>
56 </body>
57 </html>
--固然,若是在咱們的項目之中,在不少的地方都使用了相同的驗證錯誤描述信息,那麼咱們在給User類的@Size標籤設置message屬性時,咱們可使用properties文件來完成信息的注入,若是這麼作的話咱們就須要建立一個名爲ValidationMessage.properties的文件放置在咱們的根路徑下:
--想要順利加載咱們的資源配置文件,則須要在WebConfig中配置咱們的MessageResource,一般使用的都是ReloadableResourceBundleMessageSource,同時爲了可以正確的獲取到資源配置文件的信息內容,咱們須要在註解的message屬性中使用{}括號來包裹咱們的key值,不然key值會被解釋爲正常的提示信息進行輸出:
1 @Bean 2 public MessageSource messageSource(){ 3 ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 4 messageSource.setDefaultEncoding("UTF-8"); 5 messageSource.setBasename("classpath:ValidationMessages"); //配置資源文件的名稱
6 return messageSource; 7 }
--能夠發現成功的獲取到了咱們的錯誤信息
--使用資源文件的時候,咱們還可以對一些校驗註解的屬性進行自動的獲取填充,例如如今想要實現咱們資源文件對於長度限制的說明,最好的效果是:用戶名的長度在{min}和{max}之間;咱們但願"min","max"的值可以自動的進行填充,而不是須要咱們去手動設置,那麼咱們能夠這樣作(此時若是認爲每次都去使用JDK的工具進行中文和Unicode的轉化顯得十分麻煩,咱們能夠在idea中在settings中找到File Encodings,將Transparent native-to-ascii conconversion勾選上):
--因爲咱們並無進行email的max長度限制,因此產生的是Integer.MAX_VALUE的值,此外咱們還能夠對資源文件進行國際化配置,在此再也不贅述,咱們所須要作的只是給出全部支持語言的配置文件便可,能夠查看以前的文章(點擊此處跳轉).
--Spring的通用標籤庫
除了表單綁定標籤庫以外,Spring還提供了更爲通用的JSP標籤庫,實際上這個標籤庫是Spring最先的標籤庫,想要使用Spring的通用標籤庫,咱們能夠這樣聲明:<%@taglib prefix="s" uri="http://www.springframework.org/tags" %>,列出Spring的JSP標籤支持:
--在其中例如<s:bind>標籤時已經被淘汰的表單綁定標籤,他的使用相對於<sf:form>來講更加的複雜;在咱們以前的JSP文件之中,包含有不少的硬編碼的文本,若是說咱們的網頁展現有着國際化的問題的話,咱們必定但願這些硬編碼的文本也能夠經過資源文件進行讀取,這樣不至於咱們對於不一樣的語言單獨作出一個咱們的界面實現,例如在歡迎頁中,咱們但願在中國進行訪問的時候,顯示的是"歡迎來到微博",可是在英文國家進行訪問的時候咱們更但願顯示的是"welcome to spittr",咱們咱們能夠這樣進行咱們的資源文件配置:
--然後,咱們須要在MessageResource中聲明咱們的這個資源配置文件:
1 @Bean 2 public MessageSource messageSource() { 3 ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 4 messageSource.setDefaultEncoding("UTF-8"); 5 messageSource.setBasenames("classpath:ValidationMessages", "classpath:HomeMessage");//配置資源文件的名稱
6 return messageSource; 7 }
--在JSP頁面中,咱們可使用<s:message>標籤來引用咱們的資源配置文件的內容信息:
1 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
2 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
3 <%@taglib prefix="s" uri="http://www.springframework.org/tags" %>
4 <html>
5 <head>
6 <title>微博</title>
7 </head>
8 <body>
9 <h1><s:message code="home.welocme"/></h1><c:out value="${username}"/>
10 <button id="register" type="button" onclick="register()" id="a" onz>登陸</button>
11 <button type="button" id="b">註冊</button>
12 </body>
13 <script type="text/javascript" src="<c:url value="/js/jquery-1.11.3.min.js"/>"></script> 14 <script type="text/javascript">
15 function register() { //點擊跳轉到指定路徑
16 //若是咱們不指定pageContext.request.contextPath屬性的話,那麼久沒法成功找到咱們的屬性地址
17 window.location.href = "${pageContext.request.contextPath}/spittles/register"; 18 } 19 </script>
20 </html>
--查看咱們最後的運行結果:
--事實上,咱們在配置MessageSource的時候,還可使用ResourceBundleMessageSource,他會從一個屬性文件中加載信息,這個屬性文件的名稱須要是由所配置的base_name衍生而來的,而咱們選用的ReloadableResourceBundleMessageSource與ResourceBundleMessageSource的區別在於能夠從新加載信息屬性,而沒必要從新編譯或重啓應用.咱們在配置MessageSource的時候,設置的base_name若是是基於類路徑的話,咱們選用的是"classpath"前綴,若是使用的是文件系統中的文件,那麼咱們能夠選中"file"前綴.同時咱們還可使用serCacheSeconds()方法來設置緩存時間,以減少服務器的壓力.
--建立URL
<s:url>是一個很小的標籤。它主要的任務就是建立URL,而後將其 賦值給一個變量或者渲染到響應中。它是JSTL中<c:url>標籤的替 代者,可是它具有幾項特殊的技巧。
--按照其最簡單的形式,<s:url>會接受一個相對於Servlet上下文的 URL,並在渲染的時候,預先添加上Servlet上下文路徑。例如,考慮 以下<s:url>的基本用法:
<script type="text/javascript" src="<s:url value="/js/jquery-1.11.3.min.js"/>"></script>
--另外,咱們還可使用<s:url>建立URL,並將其賦值給一個變量供 模板在稍後使用:
<s:url value="/js/jquery-1.11.3.min.js" var="scriptSrc"/>
<script type="text/javascript" src="${scriptSrc}"></script>
-- 默認狀況下,URL是在頁面做用域內建立的。可是經過設置scope屬 性,咱們可讓<s:url>在應用做用域內、會話做用域內或請求做 用域內建立URL:
1 <%--<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>--%> 2 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
3 <%@taglib prefix="s" uri="http://www.springframework.org/tags" %>
4 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
5 <html>
6 <head>
7 <title>微博</title>
8 </head>
9 <body>
10 <h1><s:message code="home.welocme"/></h1><c:out value="${username}"/>
11 <button id="register" type="button" onclick="register()" id="a" onz>登陸</button>
12 <button type="button" id="b">註冊</button>
13 </body>
14 <s:url value="/js/jquery-1.11.3.min.js" var="scriptSrc"/>
15 <s:url value="/spittles/register" var="register" scope="request"/>
16 <script type="text/javascript" src="${scriptSrc}"></script>
17 <script type="text/javascript">
18 $(function () { 19 alert("hello"); 20 }); 21 function register() { //點擊跳轉到指定路徑
22 //若是咱們不指定pageContext.request.contextPath屬性的話,那麼久沒法成功找到咱們的屬性地址
23 window.location.href = "${register}"; 24 } 25 </script>
26 </html>
--同時咱們還可使用<s:param>子標籤來注入參數:
1 <%--<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>--%> 2 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
3 <%@taglib prefix="s" uri="http://www.springframework.org/tags" %>
4 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
5 <html>
6 <head>
7 <title>微博</title>
8 </head>
9 <body>
10 <s:url value="/homepage" var="homepage">
11 <s:param name="username" value="我愛Java"/>
12 </s:url>
13 <h1><s:message code="home.welocme"/></h1><c:out value="${username}"/>
14 <button id="register" type="button" onclick="register()" id="a" onz>登陸</button>
15 <button type="button" id="b">註冊</button>
16 <button type="button" onclick="registerWithSpringTag()">使用Spring標籤注入參數的訪問</button>
17 </body>
18 <s:url value="/js/jquery-1.11.3.min.js" var="scriptSrc"/>
19 <s:url value="/spittles/register" var="register" scope="request"/>
20 <script type="text/javascript" src="${scriptSrc}"></script>
21 <script type="text/javascript">
22 $(function () { 23 alert("hello"); 24 }); 25
26 function register() { //點擊跳轉到指定路徑
27 //若是咱們不指定pageContext.request.contextPath屬性的話,那麼久沒法成功找到咱們的屬性地址
28 window.location.href = "${register}"; 29 } 30
31 function registerWithSpringTag() { 32 window.location.href = "${homepage}"; 33 } 34 </script>
35 </html>
--以上功能咱們使用<c:url>也可以順利實現,可是若是咱們建立帶有路徑(path)參數的URL,只能使用Spring提供的標籤來完成:
1 <s:url value="/homePath/{username}" var="pathVariable">
2 <s:param name="username">路徑參數:Java</s:param>
3 </s:url>
--當咱們所指定的value屬性中的佔位符匹配<s:param>中的指定參數時,這個參數將會插入到佔位符的位置中.若是<s:param>參數沒法匹配href中的任何佔位符,那麼這個參數就會做爲查詢參數.<s:url>標籤還能夠解決URL的轉義需求。例如,若是你但願將渲染 獲得的URL內容展示在Web頁面上(而不是做爲超連接),那麼你應 該要求<s:url>進行HTML轉義,這須要將htmlEscape屬性設置 爲true。
1 <br/>
2 <s:url value="/homePath/{username}" htmlEscape="true">
3 <s:param name="username">在界面上展示該連接地址</s:param>
4 </s:url>
--能夠發現即便咱們使用了中文字符,也能很好的進行轉義.同時咱們也可使用JS將htmlEscape屬性設置爲true的連接地址進行變量賦值,從而達到某些操做的目的.
--轉義內容
<s:escapeBody>標籤是一個通用的轉義標籤。它會渲染標籤體中 內嵌的內容,而且在必要的時候進行轉義。例如,假設你但願在頁面上展示一個HTML代碼片斷。爲了正確顯 示,咱們須要將「<」和「>」字符替換爲「<」和「>」,不然的話, 瀏覽器將會像解析頁面上其餘HTML那樣解析這段HTML內容。 固然,沒有人禁止咱們手動將其轉義爲「<」和「>」,可是這很 煩瑣,而且代碼難以閱讀。咱們可使用<s:escapeBody>,並讓 Spring完成這項任務:
1 <s:escapeBody htmlEscape="true">
2 <h1>你好</h1>
3 <div>
4 <label>標題內容</label>
5 </div>
6 </s:escapeBody>
--同時咱們還能設置javaScriptEscape屬性進行轉義.
三.使用Apache Tiles視圖定義佈局
到如今爲止,咱們不多關心應用中Web頁面的佈局問題。每一個JSP完 全負責定義自身的佈局,在這方面其實這些JSP也沒有作太多工做。 假設咱們想爲應用中的全部頁面定義一個通用的頭部和底部。最原始 的方式就是查找每一個JSP模板,併爲其添加頭部和底部的HTML。但 是這種方法的擴展性並很差,也難以維護。爲每一個頁面添加這些元素 會有一些初始成本,然後續的每次變動都會耗費相似的成本。更好的方式是使用佈局引擎,如Apache Tiles,定義適用於全部頁面 的通用頁面佈局。Spring MVC以視圖解析器的形式爲Apache Tiles提 供了支持,這個視圖解析器可以將邏輯視圖名解析爲Tile定義。
--配置Tiles視圖解析器
爲了在Spring中使用Tiles,須要配置幾個bean。咱們須要一 個TilesConfigurer bean,它會負責定位和加載Tile定義並協調生 成Tiles。除此以外,還須要TilesViewResolver bean將邏輯視圖 名稱解析爲Tile定義。
這兩個組件又有兩種形式:針對Apache Tiles 2和Apache Tiles 3分別都 有這麼兩個組件。這兩組Tiles組件之間最爲明顯的區別在於包名。針 對Apache Tiles 2的TilesConfigurer/TilesViewResolver位於 org.springframework.web.servlet.view.tiles2包中,而 針對Tiles 3的組件位於 org.springframework.web.servlet.view.tiles3包中。對 於該例子來說,假設咱們使用的是Tiles 3。
--配置TilesConfigurer來解析定義
1 @Bean 2 public TilesConfigurer tilesConfigurer() { 3 TilesConfigurer tilesConfigurer = new TilesConfigurer(); 4 tilesConfigurer.setDefinitions("/WEB-INF/layout/tiles.xml"); 5 tilesConfigurer.setCheckRefresh(true); //啓用刷新功能
6 return tilesConfigurer; 7 }
--當配置TilesConfigurer的時候,所要設置的最重要的屬性就 是definitions。這個屬性接受一個String類型的數組,其中每 個條目都指定一個Tile定義的XML文件。對於Spittr應用來說,咱們讓 它在「/WEB-INF/layout/」目錄下查找tiles.xml。其實咱們還能夠指定多個Tile定義文件,甚至可以在路徑位置上使用 通配符,固然在上例中咱們沒有使用該功能。例如,咱們要 求TilesConfigurer加載「/WEB-INF/」目錄下的全部名字爲tiles.xml 的文件,那麼能夠按照以下的方式設置definitions屬性:
1 @Bean 2 public TilesConfigurer tilesConfigurer() { 3 TilesConfigurer tilesConfigurer = new TilesConfigurer(); 4 tilesConfigurer.setDefinitions("/WEB-INF/**/tiles.xml"); 5 tilesConfigurer.setCheckRefresh(true); //啓用刷新功能
6 return tilesConfigurer; 7 }
--咱們使用了Ant風格的通配符(**),因此 TilesConfigurer會遍歷「WEB-INF/」的全部子目錄來查找Tile定 義。然後咱們能夠定義TilesViewResolver:
1 @Bean 2 public ViewResolver tilesViewResolver() { 3 return new TilesViewResolver(); 4 }
--固然咱們也可使用XML來完成二者的配置
1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns="http://www.springframework.org/schema/beans"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
4 xmlns:util="http://www.springframework.org/schema/util"
5 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
6 <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
7 p:prefix="/WEB-INF/views" p:suffix=".jsp"
8 p:viewClass="org.springframework.web.servlet.view.JstlView"/>
9 <util:list id="list">
10 <value>/WEB-INF/layout/tiles.xml</value>
11 <value>/WEB-INF/**/tiles.xml</value>
12 </util:list>
13 <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
14 <property name="definitions" ref="list"/>
15 </bean>
16 <bean id="tilesViewResolver" class="org.springframework.web.servlet.view.tiles3.TilesViewResolver"/>
17 </beans>
--TilesConfigurer會加載Tile定義並與Apache Tiles協做, 而TilesViewRe-solver會將邏輯視圖名稱解析爲引用Tile定義的 視圖。它是經過查找與邏輯視圖名稱相匹配的Tile定義實現該功能 的。咱們須要建立幾個Tile定義以瞭解它是如何運轉的,定義Apache Tiles所須要的jar包以下(org.apache.tiles):
--定義Tiles
Apache Tiles提供了一個文檔類型定義(document type definition, DTD),用來在XML文件中指定Tile的定義。每一個定義中須要包含一 個<definition>元素,這個元素會有一個或多個<put- attribute>元素。
1 <?xml version="1.0" encoding="UTF-8" ?>
2 <!DOCTYPE tiles-definitions PUBLIC 3 "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" 4 "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">
5 <tiles-definitions>
6 <definition name="base" template="/WEB-INF/layout/page.jsp"> <!--定義基本的頁面-->
7 <put-attribute name="header" value="/WEB-INF/layout/header.jsp"/> <!--定義頁面的頭部-->
8 <put-attribute name="footer" value="/WEB-INF/layout/footer.jsp"/> <!--定義頁面的尾部-->
9 </definition>
10 <definition name="home" extends="base">
11 <put-attribute name="body"
12 value="/WEB-INF/views/home.jsp"/>
13 </definition>
14 <definition name="registerForm" extends="base">
15 <put-attribute name="body" value="/WEB-INF/views/registerForm.jsp"/>
16 </definition>
17 <definition name="spittles" extends="base">
18 <put-attribute name="body" value="/WEB-INF/views/register.jsp"/>
19 </definition>
20 </tiles-definitions>
--每一個<definition>元素都定義了一個Tile,它最終引用的是一個 JSP模板。在名爲base的Tile中,模板引用的是「/WEB- INF/layout/page.jsp」。某個Tile可能還會引用其餘的JSP模板,使這些 JSP模板嵌入到主模板中。對於base Tile來說,它引用的是一個頭部 JSP模板和一個底部JSP模板,page .jsp的定義以下:
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2 <%@taglib prefix="t" uri="http://tiles.apache.org/tags-tiles" %>
3 <html>
4 <head>
5 <title>Title</title>
6 </head>
7 <style type="text/css">
8 * {
9 margin: 0;
10 padding: 0;
11 }
12
13 #footer {
14 width: 1920px;
15 height: 100px;
16 background-color: lightblue;
17 text-align: center;
18 }
19 #header{
20 width: 1920px;
21 height: 100px;
22 background-color: lightgrey;
23 text-align: center;
24 }
25 </style>
26 <body>
27 <div id="header">
28 <t:insertAttribute name="header"/>
29 </div>
30 <div id="content">
31 <t:insertAttribute name="body"/>
32 </div>
33 <div id="footer">
34 <t:insertAttribute name="footer"/>
35 </div>
36 </body>
37 </html>
--在這裏只是爲了演示出效果,因此只是採用了當前的簡單的CSS樣式來做爲區分,在最終咱們能夠看到展示出的效果爲:
--注:在這裏因爲(org.apache.tiles)相關的依賴包實在太多了,因此我將整個項目使用maven從新構建了一下,給出相關的maven依賴配置:
1 <dependencies>
2 <!--單元測試-->
3 <dependency>
4 <groupId>junit</groupId>
5 <artifactId>junit</artifactId>
6 <version>4.11</version>
7 <scope>test</scope>
8 </dependency>
9 <!--servlet aip 運行必須-->
10 <dependency>
11 <groupId>javax.servlet</groupId>
12 <artifactId>javax.servlet-api</artifactId>
13 <version>3.1.0</version>
14 <scope>provided</scope>
15 </dependency>
16 <dependency>
17 <groupId>javax.servlet.jsp.jstl</groupId>
18 <artifactId>jstl-api</artifactId>
19 <version>1.2</version>
20 </dependency>
21 <dependency>
22 <groupId>taglibs</groupId>
23 <artifactId>standard</artifactId>
24 <version>1.1.2</version>
25 </dependency>
26 <!--JSTL-->
27 <dependency>
28 <groupId>javax.servlet.jsp.jstl</groupId>
29 <artifactId>jstl</artifactId>
30 <version>1.2</version>
31 </dependency>
32 <!--spring-->
33 <dependency>
34 <groupId>org.springframework</groupId>
35 <artifactId>spring-webmvc</artifactId>
36 <version>4.3.4.RELEASE</version>
37 </dependency>
38 <!--JSR 303校驗-->
39 <dependency>
40 <groupId>org.hibernate.validator</groupId>
41 <artifactId>hibernate-validator</artifactId>
42 <version>6.0.13.Final</version>
43 </dependency>
44 <!--org.apache.tiles-->
45 <dependency>
46 <groupId>org.apache.tiles</groupId>
47 <artifactId>tiles-jsp</artifactId>
48 <version>3.0.8</version>
49 </dependency>
50 </dependencies>
四.使用Thymeleaf
儘管JSP已經存在了很長的時間,而且在Java Web服務器中無處不 在,可是它卻存在一些缺陷。JSP最明顯的問題在於它看起來像 HTML或XML,但它其實上並非。大多數的JSP模板都是採用HTML 的形式,可是又摻雜上了各類JSP標籤庫的標籤,使其變得很混亂。 這些標籤庫可以以很便利的方式爲JSP帶來動態渲染的強大功能,但 是它也摧毀了咱們想維持一個格式良好的文檔的可能性。
標籤庫和JSP缺少良好格式的一個反作用就是它不多可以與其產生的 HTML相似。因此,在Web瀏覽器或HTML編輯器中查看未經渲染的 JSP模板是很是使人困惑的,並且獲得的結果看上去也很是醜陋。這 個結果是不完整的——在視覺上這簡直就是一場災難!由於JSP並非真正的HTML,不少瀏覽器和編輯器展示的效果都很難在審美上接 近模板最終所渲染出來的效果。
同時,JSP規範是與Servlet規範緊密耦合的。這意味着它只能用在基 於Servlet的Web應用之中。JSP模板不能做爲通用的模板(如格式化 Email),也不能用於非Servlet的Web應用。多年來,在Java應用中,有多個項目試圖挑戰JSP在視圖領域的統治 性地位。最新的挑戰者是Thymeleaf,它展示了一些切實的承諾,是 一項很使人興奮的可選方案。Thymeleaf模板是原生的,不依賴於標 籤庫。它能在接受原始HTML的地方進行編輯和渲染。由於它沒有與 Servlet規範耦合,所以Thymeleaf模板可以進入JSP所沒法涉足的領 域。
--配置Thymeleaf視圖解析器
爲了要在Spring中使用Thymeleaf,咱們須要配置三個啓用Thymeleaf與Spring集成的bean:
1.ThymeleafViewResolver:將邏輯視圖名稱解析爲Thymeleaf 模板視圖;
2.SpringTemplateEngine:處理模板並渲染結果;
3.TemplateResolver:加載Thymeleaf模板。
--使用Java代碼的方式,配置Spring對Thymeleaf的支持,首先咱們須要引入thymeleaf的相關依賴
1 <!--Spring中使用thymeleaf-->
2 <dependency>
3 <groupId>org.thymeleaf</groupId>
4 <artifactId>thymeleaf-spring4</artifactId>
5 <version>3.0.9.RELEASE</version>
6 </dependency>
--進行thymele使用的相關配置
1 package com.sky.config; 2
3 import org.springframework.context.annotation.Bean; 4 import org.springframework.context.annotation.ComponentScan; 5 import org.springframework.context.annotation.Configuration; 6 import org.springframework.web.servlet.ViewResolver; 7 import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; 8 import org.springframework.web.servlet.config.annotation.EnableWebMvc; 9 import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 10 import org.springframework.web.servlet.view.InternalResourceViewResolver; 11 import org.springframework.web.servlet.view.JstlView; 12 import org.springframework.web.servlet.view.tiles3.TilesConfigurer; 13 import org.springframework.web.servlet.view.tiles3.TilesViewResolver; 14 import org.thymeleaf.TemplateEngine; 15 import org.thymeleaf.spring4.SpringTemplateEngine; 16 import org.thymeleaf.spring4.view.ThymeleafViewResolver; 17 import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver; 18 import org.thymeleaf.templateresolver.AbstractTemplateResolver; 19 import org.thymeleaf.templateresolver.ServletContextTemplateResolver; 20
21 import javax.servlet.ServletContext; 22
23 /**
24 * @author : S K Y 25 * @version :0.0.1 26 */
27 @Configuration 28 @EnableWebMvc 29 @ComponentScan(basePackages = {"com.sky.*"}) 30 public class WebConfig extends WebMvcConfigurerAdapter { 31 /*
32 @Bean 33 public ViewResolver viewResolver() { 34 InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); 35 viewResolver.setPrefix("/WEB-INF/views/"); //設置前綴名稱 36 viewResolver.setSuffix(".jsp"); //設置後綴名稱 37 viewResolver.setViewClass(JstlView.class); 38 viewResolver.setExposeContextBeansAsAttributes(true); 39 return viewResolver; 40 } 41 */
42
43 @Override 44 public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { 45 configurer.enable(); //配置靜態資源的處理
46 } 47
48 /* //tiles模板使用設置 49 @Bean 50 public TilesConfigurer tilesConfigurer() { 51 TilesConfigurer tilesConfigurer = new TilesConfigurer(); 52 tilesConfigurer.setDefinitions("/WEB-INF/layout/tiles.xml"); 53 tilesConfigurer.setCheckRefresh(true); //啓用刷新功能 54 return tilesConfigurer; 55 } 56
57 //tiles模板使用設置 58 @Bean 59 public TilesViewResolver tilesViewResolver() { 60 return new TilesViewResolver(); 61 }*/
62
63 @Bean 64 public ViewResolver thymeleafViewResolver(TemplateEngine templateEngine) { //Thymeleaf視圖解析器
65 ThymeleafViewResolver thymeleafViewResolver = new ThymeleafViewResolver(); 66 thymeleafViewResolver.setTemplateEngine(templateEngine); 67 thymeleafViewResolver.setCharacterEncoding("UTF-8"); //須要設置編碼格式,不然使用中文亂碼
68 return thymeleafViewResolver; 69 } 70
71 @Bean 72 public TemplateEngine templateEngine(AbstractTemplateResolver templateResolver) { //模板引擎
73 SpringTemplateEngine templateEngine = new SpringTemplateEngine(); 74 templateEngine.setTemplateResolver(templateResolver); 75 return templateEngine; 76 } 77
78 @Bean 79 public AbstractConfigurableTemplateResolver templateResolver(ServletContext servletContext) { //模板解析器
80 AbstractConfigurableTemplateResolver templateResolver =
81 new ServletContextTemplateResolver(servletContext); 82 templateResolver.setPrefix("/WEB-INF/thymeleaves/"); 83 templateResolver.setSuffix(".html"); 84 templateResolver.setTemplateMode("HTML5"); 85 templateResolver.setCharacterEncoding("UTF-8"); //須要設置編碼格式,不然使用中文亂碼
86 return templateResolver; 87 } 88 }
--ThymeleafViewResolver是Spring MVC中ViewResolver的一個 實現類。像其餘的視圖解析器同樣,它會接受一個邏輯視圖名稱,並 將其解析爲視圖。不過在該場景下,視圖會是一個Thymeleaf模板。須要注意的是ThymeleafViewResolver bean中注入了一個對 SpringTemplate Engine bean的引 用。SpringTemplateEngine會在Spring中啓用Thymeleaf引擎,用 來解析模板,並基於這些模板渲染結果。能夠看到,咱們爲其注入了一個TemplateResolver bean的引用。
--TemplateResolver會最終定位和查找模板。與以前配 置InternalResourceViewResolver相似,它使用了prefix和 suffix屬性。前綴和後綴將會與邏輯視圖名組合使用,進而定位 Thymeleaf引擎。它的templateMode屬性被設置成了HTML5,這表 明咱們預期要解析的模板會渲染成HTML5輸出。
--定義Thymeleaf模板
Thymeleaf在很大程度上就是HTML文件,與JSP不一樣,它沒有什麼特 殊的標籤或標籤庫。Thymeleaf之因此可以發揮做用,是由於它經過 自定義的命名空間,爲標準的HTML標籤集合添加Thymeleaf屬性。如 下的程序清單展示了home.html,也就是使用Thymeleaf命名空間的首頁模板。
1 <!DOCTYPE html>
2 <html lang="en" xmlns="http://www.w3.org/1999/xhtml"
3 xmlns:th="http://www.thymeleaf.org">
4 <head>
5 <meta charset="UTF-8">
6 <title>title</title>
7 <!--到樣式表的th:href連接-->
8 <!-- <link rel="stylesheet" type="text/css" 9 th:href="@{/resources/style.css}"/>-->
10 </head>
11 <body>
12 <h1>歡迎來到微博</h1>
13 <a th:href="@{/thyme/register}">點擊註冊</a>
14 </body>
15 </html>
--首頁模板相對來說很簡單,只使用了th:href屬性。這個屬性與對 應的原生HTML屬性很相似,也就是href屬性,而且能夠按照相同的 方式來使用。th:href屬性的特殊之處在於它的值中能夠包含 Thymeleaf表達式,用來計算動態的值。它會渲染成一個標準的href 屬性,其中會包含在渲染時動態建立獲得的值。這是Thymeleaf命名 空間中不少屬性的運行方式:它們對應標準的HTML屬性,而且具備 相同的名稱,可是會渲染一些計算後獲得的值。在本例中,使 用th:href屬性的三個地方都用到了「@{}」表達式,用來計算相對於 URL的路徑(就像在JSP頁面中,咱們可能會使用的JSTL<c:url>標 籤或Spring<s:url>標籤相似)。
--Thymeleaf可以按照原始的方式進行編輯 甚至渲染,而沒必要通過任何類型的處理器。固然,咱們須要 Thymeleaf來處理模板並渲染獲得最終指望的輸出。即使如此,若是 沒有任何特殊的處理,home.html也可以加載到Web瀏覽器中,而且看 上去與完整渲染的效果很相似。同時,咱們還能在Thymeleaf中使用Spring表達式語言,咱們親切的能夠成爲Thymeleaf的Spring方言.
1 <!DOCTYPE html>
2 <html lang="en" xmlns="http://www.w3.org/1999/xhtml"
3 xmlns:th="http://www.thymeleaf.org">
4 <head>
5 <meta charset="UTF-8">
6 <title>title</title>
7 <!--到樣式表的th:href連接-->
8 <!-- <link rel="stylesheet" type="text/css" 9 th:href="@{/resources/style.css}"/>-->
10 </head>
11 <style type="text/css">
12 .error {
13 color: red;
14 }
15
16 .error_text {
17 border: 1px solid red;
18 }
19
20 .title {
21 color: green;
22 width: 20px;
23 height: 10px;
24 border: 1px solid black;
25 }
26 </style>
27 <body>
28 <form method="post" th:object="${user}">
29 <label class="title" th:class="${#fields.hasErrors('username')} ? 'error'">用戶名:</label>
30 <input type="text" th:class="${#fields.hasErrors('username')}?'error_text'"
31 th:field="*{username}"/>
32 <label th:text="${#fields.errors('username')}"
33 th:class="${#fields.hasErrors('username') }? 'error'"
34 th:if="${#fields.hasErrors('username')}">XXX</label>
35 <br/>
36 <label class="title" th:class="${#fields.hasErrors('password')} ? 'error'">密碼:</label>
37 <input type="password" th:class="${#fields.hasErrors('password')}?'error_text'"
38 value="123" th:value="*{password}"/>
39 <label th:text="${#fields.errors('password')}"
40 th:class="${#fields.hasErrors('password')} ? 'error'"
41 th:if="${#fields.hasErrors('password')}"></label> <br/>
42 <label class="title" th:class="${#fields.hasErrors('email')} ? 'error'">郵箱:</label>
43 <input type="email" th:class="${#fields.hasErrors('email')}?'error_text'"
44 th:field="*{email}"/>
45 <label th:text="${#fields.errors('email')}"
46 th:class="${#fields.hasErrors('email')} ? 'error'"
47 th:if="${#fields.hasErrors('email')}"></label><br/>
48 <button>提交</button>
49 </form>
50 </body>
51 </html>
--在上述的例子中咱們使用了較多的表達式完成了總體的實際,在<form>標籤之中咱們可使用th:object屬性在${user}中指定咱們接受的傳遞參數,這裏須要注意的是,thymeleaf在解析咱們的html頁面的時候,若是咱們沒有傳遞${user}中所指定的內容,那麼就不會去顯示咱們在form表單中所定義的信息,因此和以前同樣咱們須要在get請求中給出一個空的user對象做爲初始的傳值:
1 @RequestMapping(value = "/register", method = RequestMethod.GET) 2 public String register(Model model) { 3 model.addAttribute("user", new User()); 4 return "register"; 5 } 6
7 @RequestMapping(value = "/register", method = RequestMethod.POST) 8 public String registerPost(@Validated User user, Errors errors, Model model) { 9 System.out.println(user); 10 if (errors.hasErrors()) { 11 return "register"; 12 } else { 13 return "home"; 14 } 15 }
--基本的controller設計用JSP中的設計模式是同樣的,在thymeleaf中咱們可使用 th:class="${#fields.hasErrors('password')} ? 'error'"來指定當獲取到相對應的錯誤信息的時候所顯示的class屬性,使用th:text="${#fields.errors('password')}"來指定當錯誤信息存在時所展示的內容,用 th:if="${#fields.hasErrors('password')}"來指定只有當存在該錯誤信息的時候纔會顯示出咱們所定義的錯誤信息.固然咱們也能夠將全部的錯誤信息都在一個指定的位置顯示出來,同時使用遍歷循環的方式來展示咱們的數據信息:
1 <!DOCTYPE html>
2 <html lang="en" xmlns="http://www.w3.org/1999/xhtml"
3 xmlns:th="http://www.thymeleaf.org">
4 <head>
5 <meta charset="UTF-8">
6 <title>title</title>
7 <!--到樣式表的th:href連接-->
8 <!-- <link rel="stylesheet" type="text/css" 9 th:href="@{/resources/style.css}"/>-->
10 </head>
11 <style type="text/css">
12 .error {
13 color: red;
14 }
15
16 .error_text {
17 border: 1px solid red;
18 }
19
20 .title {
21 color: green;
22 width: 20px;
23 height: 10px;
24 border: 1px solid black;
25 }
26 </style>
27 <body>
28 <form method="post" th:object="${user}">
29 <label class="title" th:class="${#fields.hasErrors('username')} ? 'error'">用戶名:</label>
30 <input type="text" th:class="${#fields.hasErrors('username')}?'error_text'"
31 th:field="*{username}"/>
32 <!-- <label th:text="${#fields.errors('username')}" 33 th:class="${#fields.hasErrors('username') }? 'error'" 34 th:if="${#fields.hasErrors('username')}">XXX</label>-->
35 <br/>
36 <label class="title" th:class="${#fields.hasErrors('password')} ? 'error'">密碼:</label>
37 <input type="password" th:class="${#fields.hasErrors('password')}?'error_text'"
38 th:field="*{password}"/>
39 <!--<label th:text="${#fields.errors('password')}" 40 th:class="${#fields.hasErrors('password')} ? 'error'" 41 th:if="${#fields.hasErrors('password')}"></label> --><br/>
42 <label class="title" th:class="${#fields.hasErrors('email')} ? 'error'">郵箱:</label>
43 <input type="email" th:class="${#fields.hasErrors('email')}?'error_text'"
44 th:field="*{email}"/>
45 <!--<label th:text="${#fields.errors('email')}" 46 th:class="${#fields.hasErrors('email')} ? 'error'" 47 th:if="${#fields.hasErrors('email')}"></label>--><br/>
48 <div th:if="${#fields.hasErrors('*')}">
49 <ul>
50 <li th:each="error:${#fields.errors('*')}"
51 th:text="${error}"
52 class="error"
53 ></li>
54 </ul>
55 </div>
56 <button>提交</button>
57 </form>
58 </body>
59 </html>
--相比於JSP的JSTL標籤庫的使用,thymeleaf的標籤庫顯得更加的簡潔,<div>元素使用th:if屬性來檢查是否有校驗錯 誤。若是有的話,會渲染<div>,不然的話,它將不會渲染。在<div>中,會使用一個無順序的列表來展示每項錯誤。<li>標籤 上的th:each屬性將會通知Thymeleaf爲每項錯誤都渲染一個<li>, 在每次迭代中會將當前錯誤設置到一個名爲err的變量中。 <li>標籤還有一個th:text屬性。這個命令會通知Thymeleaf計算某 一個表達式(在本例中,也就是error變量)並將它的值渲染爲<li> 標籤的內容體。實際上的效果就是每項錯誤對應一個<li>元素,並 展示錯誤的文本。--你可能會想知道「${}」和「*{}」括起來的表達式到底有什麼區 別。「${}」表達式(如${spitter})是變量表達式(variable expression)。通常來說,它們會是對象圖導航語言(Object-Graph Navigation Language,OGNL)表達式.但在使用Spring 的時候,它們是SpEL表達式。在${spitter}這個例子中,它會解 析爲key爲spitter的model屬性。而對於「*{}」表達式,它們是選擇表達式(selection expression)。變 量表達式是基於整個SpEL上下文計算的,而選擇表達式是基於某一 個選中對象計算的。在本例的表單中,選中對象就是<form>標籤中 th:object屬性所設置的對象:模型中的Spitter對象。所以,「* {firstName}」表達式就會計算爲Spitter對象的firstName屬性。