Spring自帶了13個視圖解析器,能夠將邏輯視圖名轉換為物理實現
首先將會介紹 InternalResourceViewResolver,這個視圖解析器一般會用來 解析JSP視圖。
1. Spring提供了兩種支持JSP視圖的方式:
- InternalResourceViewResolver會將視圖名解析為JSP文 件。另外,如果在你的JSP頁面中使用了JSP標准標簽庫 (JavaServer Pages Standard Tag Library,JSTL)的 話,InternalResourceViewResolver能夠將視圖名解析為 JstlView形式的JSP文件,從而將JSTL本地化和資源bundle變量暴 露給JSTL的格式化(formatting)和信息(message)標簽。
- Spring提供了兩個JSP標簽庫,一個用於表單到模型的綁定,另一 個提供了通用的工具類特性。
考慮一個簡單的場景,假設邏輯視圖名為home。通用的 實踐是將JSP文件放到Web應用的WEB-INF目錄下,防止對它的直接 訪問。如果我們將所有的JSP文件都放在“/WEB-INF/views/”目錄下, 並且home頁的JSP名為home.jsp,那么我們可以確定物理視圖的路徑 就是邏輯視圖名home再加上“/WEB-INF/views/”前綴和“.jsp”后綴。如下圖所示:
當使用@Bean注解的時候,我們可以按照如下的方式配 置Internal-ResourceView Resolver,使其在解析視圖時,遵 循上述的約定。
1 @Bean // 配置JSP視圖解析器 2 public ViewResolver viewResolver() { 3 InternalResourceViewResolver resolver = new InternalResourceViewResolver(); 4 resolver.setPrefix("/WEB-INF/views/"); 5 resolver.setSuffix(".jsp"); 6 resolver.setExposeContextBeansAsAttributes(true); 7 return resolver; 8 }
2. 解析JSTL視圖
如果在JSP中使用JSTL標簽來處理格式化和信息的話,那么就會讓InternalResourceViewResolver將視圖解析為JstlView。 JSTL的格式化標簽需要一個Locale對象,以便於恰當地格式化地域 相關的值,如日期和貨幣。信息標簽可以借助Spring的信息資源和 Locale,從而選擇適當的信息渲染到HTML之中。通過解析 JstlView,JSTL能夠獲得Locale對象以及Spring中配置的信息資 源。
如果想讓InternalResourceViewResolver將視圖解析 為JstlView,而不是InternalResourceView的話,那么我們只需設置它的viewClass屬性即可:
1 @Bean // 配置JSP視圖解析器 2 public ViewResolver viewResolver() { 3 InternalResourceViewResolver resolver = new InternalResourceViewResolver(); 4 resolver.setPrefix("/WEB-INF/views/"); 5 resolver.setSuffix(".jsp"); 6 resolver.setViewClass(org.springframework.web.servlet.view.JstlView.class); 7 resolver.setExposeContextBeansAsAttributes(true); 8 return resolver; 9 }
3. Spring的JSP庫
Spring提供了兩個JSP標簽庫,用來幫助定義 Spring MVC Web的視圖。其中一個標簽庫會用來渲染HTML表單標 簽,這些標簽可以綁定model中的某個屬性。另外一個標簽庫包含了 一些工具類標簽
3.1 將表單綁定到模型上
Spring的表單綁定JSP標簽庫包含了14個標簽,它們中的大多數都用來 渲染HTML中的表單標簽。但是,它們與原生HTML標簽的區別在於 它們會綁定模型中的一個對象,能夠根據模型中對象的屬性填充值。 標簽庫中還包含了一個為用戶展現錯誤的標簽,它會將錯誤信息渲染 到最終的HTML之中。
為了使用表單綁定庫,需要在JSP頁面中對其進行聲明:
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf"%>
需要注意,將前綴指定為“sf”,但通常也可能使用“form”前綴。 在聲明完表單綁定標簽庫之后,就可以使用14個相關的標簽了。如下圖所示:
重寫registerForm.jsp,程序如下:
1 <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> 2 <%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf"%> 3 <% 4 String path = request.getContextPath(); 5 String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/"; 6 %> 7 8 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> 9 <html> 10 <head> 11 <base href="<%=basePath%>"> 12 13 <title>注冊</title> 14 15 <meta http-equiv="pragma" content="no-cache"> 16 <meta http-equiv="cache-control" content="no-cache"> 17 <meta http-equiv="expires" content="0"> 18 <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"> 19 <meta http-equiv="description" content="This is my page"> 20 <!-- 21 <link rel="stylesheet" type="text/css" href="styles.css"> 22 --> 23 24 </head> 25 26 <body> 27 <sf:form method="POST" modelAttribute="spitter"> 28 First Name:<sf:input path="firstName"/> 29 Last Name:<sf:input path="lastName"/> 30 Email:<sf:input path="email"/> 31 UserName:<sf:input path="username"/> 32 Password:<sf:password path="password"/> 33 <input type="submit" value="注冊"> 34 </sf:form> 35 </body> 36 </html>
<sf:form>會渲染會一個HTML <form>標簽,但它也會通過 commandName屬性構建針對某個模型對象的上下文信息。在其他的 表單綁定標簽中,會引用這個模型對象的屬性。
將commandName屬性設置為spitter。因 此,在模型中必須要有一個key為spitter的對象,否則的話,表單 不能正常渲染(會出現JSP錯誤)。這意味着需要修改一 下SpitterController,以確保模型中存在以spitter為key的 Spitter對象:
1 @RequestMapping(value = "/register", method = RequestMethod.GET) // 處理對“/spitter/register”的GET請求 2 public String showRegistrationForm(Model model) { 3 model.addAttribute(new Spitter()); 4 return "registerForm"; 5 }
修改后的showRegistrationForm()方法中,新增了一 個Spitter實例到模型中。
3.2 展現錯誤
相對於標准的HTML標簽,使用Spring的表單綁定標簽能夠帶來一定 的功能提升,在校驗失敗后,表單中會預先填充之前輸入的值。但 是,這依然沒有告訴用戶錯在什么地方。為了指導用戶矯正錯誤,需要使用<sf:errors>。
如果存在校驗錯誤的話,請求中會包含錯誤的詳細信息,這些信息是 與模型數據放到一起的。我們所需要做的就是到模型中將這些數據抽 取出來,並展現給用戶。<sf:errors>能夠讓這項任務變得很簡 單。
1 <sf:errors path="firstName"> 2 <span id="firstName.errors">必須大於2個字符</span> 3 </sf:errors>
將<sf:errors>用到First Name輸入域的場景,它的path屬性設置成了firstName,也就是指定了要顯示Spitter 模型對象中哪個屬性的錯誤。如果firstName屬性沒有錯誤的話, 那么<sf:errors>不會渲染任何內容。但如果有校驗錯誤的話,那 么它將會在一個HTML <span>標簽中顯示錯誤信息。
4. Spring通用的標簽庫
除了表單綁定標簽庫之外,Spring還提供了更為通用的JSP標簽庫。要使用Spring通用的標簽庫,我們必須要在頁面上對其進行聲明:
1 <%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
標簽庫聲明之后,我們就可以使用下表中的十個JSP標簽了。
4.1 展現國際化信息
對於渲染文本來說,是很好的方案,文本能夠位於一個或多個屬性文 件中。借助<s:message>,我們可以將硬編碼的歡迎信息替換為如 下的形式:
按照這里的方式,<s:message>將會根據key為spittr.welcome 的信息源來渲染文本。因此,如果我們希望<s:message>能夠正常 完成任務的話,就需要配置一個這樣的信息源。
Spring有多個信息源的類,它們都實現了MessageSource接口。在 這些類中,更為常見和有用的 是ResourceBundleMessageSource。它會從一個屬性文件中加 載信息,這個屬性文件的名稱是根據基礎名稱(base name)衍生而來 的。如下的@Bean方法配置了 ResourceBundleMessageSource:
1 @Bean 2 public MessageSource messageSource() { 3 ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); 4 messageSource.setBasename("message"); 5 return messageSource; 6 }
在這個bean聲明中,核心在於設置basename屬性。你可以將其設置 為任意的值,在這里,我將其設置為message。將其設置 為message后,ResourceBundle-MessageSource就會試圖在根 路徑的屬性文件中解析信息,這些屬性文件的名稱是根據這個基礎名 稱衍生得到的。
另外的可選方案是使 用ReloadableResourceBundleMessageSource,它的工作方 式與ResourceBundleMessageSource非常類似,但是它能夠重 新加載信息屬性,而不必重新編譯或重啟應用。如下是配 置ReloadableResourceBundle-MessageSource的樣例:
1 @Bean 2 public MessageSource messageSource2() { 3 ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); 4 messageSource.setBasename("file:///etc/spittr/message"); 5 messageSource.setCacheSeconds(10); 6 return messageSource; 7 }
這里的關鍵區別在於basename屬性設置為在應用的外部查找(而不 是像ResourceBundleMessageSource那樣在類路徑下查 找)。basename屬性可以設置為在類路徑下(以“classpath:”作 為前綴)、文件系統中(以“file:”作為前綴)或Web應用的根路徑 下(沒有前綴)查找屬性。在這里,我將其配置為在服務器文件系統 的“/etc/spittr”目錄下的屬性文件中查找信息,並且基礎的文件名 為“message”。
這兩個bean放在WebConfig.java中
創建屬性文件:message.properties 位置:src下,類的根目錄
1 spittr.welcome=Welcome to Spittr\!
4.2 創建URL
<s:url>是一個很小的標簽。它主要的任務就是創建URL,然后將其 賦值給一個變量或者渲染到響應中。它是JSTL中<c:url>標簽的替 代者,但是它具備幾項特殊的技巧。
按照其最簡單的形式,<s:url>會接受一個相對於Servlet上下文的 URL,並在渲染的時候,預先添加上Servlet上下文路徑。例如,考慮 如下<s:url>的基本用法:
如果應用的Servlet上下文名為spittr,那么在響應中將會渲染如下 的HTML:
這樣,在創建URL的時候,就不必再擔心Servlet上下文路徑是什 么了,<s:url>將會負責這件事。
另外,還可以使用<s:url>創建URL,並將其賦值給一個變量供 模板在稍后使用:
默認情況下,URL是在頁面作用域內創建的。但是通過設置scope屬 性,我們可以讓<s:url>在應用作用域內、會話作用域內或請求作 用域內創建URL:
如果希望在URL上添加參數的話,那么可以使用<s:param>標 簽。比如,如下的<s:url>使用兩個內嵌的<s:param>標簽,來設 置“/spittles”的max和count參數:
重點:創建帶有路徑 (path)參數的URL
當href屬性中的占位符匹配<s:param>中所指定的參數時,這個參 數將會插入到占位符的位置中。如果<s:param>參數無法匹配href 中的任何占位符,那么這個參數將會作為查詢參數。