本章簡介
視圖(View)和視圖解析器(ViewResolver)的工作流程:
當請求處理方法處理完請求之后,會返回String、ModelAndView或View對象,如return “success”;但返回值最終都會被SpringMVC統一轉為ModelAndView對象並返回;隨后Spring就會用ViewResolver,把返回的ModelAndView對象中的View渲染給用戶看(即返回給瀏覽器),如圖,
圖28-01
28.1 視圖View
視圖View的作用是渲染數據,將數據以JSP、PDF、EXCEL等形式呈現給用戶。SpringMVC通過View接口來支持視圖,該接口提供了各種各樣的視圖,並且可以讓用戶自定義視圖。
在客戶端的每一次請求時,視圖解析器ViewResolver都會產生一個新的視圖View對象。
視圖View接口的實現類及部分簡介如下
圖28-02
視圖類型 | 簡介 | |
URL視圖資源圖 | InternalResourceView | 將JSP或其他資源封裝成一個視圖。被視圖解析器InternalResourceViewResolver默認使用。 |
JstlView | InternalResourceView的子類。如果JSP中使用了JSTL的國際化標簽,就需要使用該視圖類。 | |
文檔視圖 | AbstractExcelView | Excel文檔視圖的抽象類。 |
AbstractPdfView | PDF文檔視圖的抽象類 | |
報表視圖 | ConfigurableJasperReportsView | 常用的JasperReports報表視圖 |
JasperReportsHtmlView | ||
JasperReportsPdfView | ||
JasperReportsXlsView | ||
JSON視圖 | MappingJackson2JsonView | 將數據通過Jackson框架的ObjectMapper對象,以JSON方式輸出 |
28.2 視圖解析器ViewResolver
SpringMVC提供了一個視圖解析器的上級接口ViewResolver,所有具體的視圖解析器必須實現該接口。
常用的視圖解析器實現類及簡介如下
圖28-03
視圖解析器類型 | 簡介 | |
解析為bean | BeanNameViewResolver | 將視圖解析后,映射成一個bean,視圖的名字就是bean的id。 |
解析為映射文件 | InternalResourceViewResolver | 將視圖解析后,映射成一個資源文件。例如將一個視圖名為字符串“success.jsp”的視圖解析后,映射成一個名為success的JSP文件。 |
JasperReportsViewResolver | 將視圖解析后,映射成一個報表文件。 | |
解析為模板文件 | FreeMarkerViewResolver | 將視圖解析后,映射成一個FreeMarker模板文件。 |
VelocityViewResolver | 將視圖解析后,映射成一個Velocity模板文件。 | |
VelocityLayoutViewResolver |
InternalResourceViewResolver是JSP最常用的視圖解析器,可以通過prefix
給響應字符串加上前綴,通過suffix
加上后綴。例如我們之前曾在springMVC的配置文件中配置了一個視圖解析器InternalResourceViewResolver,如下:
springmvc.xml
<beans …>
…
<!-- 配置視圖解析器:把handler處理類的返回值,加工成最終的視圖路徑-->
<bean class="org.springframework.web.servlet.view
.InternalResourceViewResolver">
<property name="prefix" value="/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
此外,視圖解析器還可以通過解析JstlView進而實現國際化、通過解析<mvc:view-controller>
進而指定請求的跳轉路徑、通過“redirect:”和“forward:”指定跳轉方式等等。
(1)通過解析JstlView
實現國際化
JstlView
是InternalResourceView
的子類。如果在JSP中使用了JSTL,那么InternalResourceViewResolver就會自動將默認使用的InternalResourceView
視圖類型轉變為JstlView
類型。
以下,在SpringMVC中使用JSTL的fmt標簽來實現國際化:
所謂“國際化”,就是指同一個程序,對於不同地區/國家的訪問,提供相應的、符合來訪者閱讀習慣的頁面或數據。例如,同一個用JSP開發的歡迎頁面,中國地區訪問時顯示“歡迎您”,而美國地區訪問時則顯示“Welcome”。以下是實現國際化的具體步驟:
①對於不同地區/國家,創建不同的資源文件
將程序中的提示信息、錯誤信息等放在資源文件中,為不同地區/國家編寫對應資源文件。這些資源文件使用共同的基名,通過在基名后面添加語言代碼、國家及地區代碼來區分不同地域的訪問者。如下是一些常見的資源文件命名方式及簡介:
資源文件名 | 簡介 |
基名_en.properties | 所有英文語言的資源 |
基名_en_US.properties | 針對美國地區、英文語言的資源 |
基名_zh.properties | 所有的中文語言的資源 |
基名_zh_CN.properties | 針對中國大陸的、中文語言的資源 |
基名_zh_HK.properties | 針對中國香港的、中文語言的資源 |
基名.properties | 默認資源文件。如果請求相應語言的資源文件不存在,將使用此資源文件。例如,若是中國大陸地區用戶,應該訪問“基名_zh_CN.properties”,而如果不存在此文件,就會去訪問默認的“基名.properties”。 |
例如,如果訪問此項目的用戶來自美國和中國兩個國家,就需要創建美國和中國兩個地區的資源文件:在項目的src目錄中,新建美國地區的資源文件i18n_en_US.properties,和中國內地的資源文件i18n_ zh_CN.properties,如下:
美國地區的資源文件:i18n_en_US.properties
resource.welcome=WELCOME
resource.exist=EXIST
中國內地的資源文件:i18n_ zh_CN.properties
resource.welcome=\u6B22\u8FCE\u60A8
resource.exist=\u9000\u51FA
說明:
在中文使用的i18n_ zh_CN.properties中,用戶輸入的原文是:
resource.welcome=歡迎您
resource.exist=退出
但Eclipse會自動將“歡迎您”等漢字自動轉為相應的ASCII,供屬性文件使用。 如果讀者使用的Eclipse版本不能自動的將漢字轉為ASCII,也可以使用JDK安裝目錄中bin中的native2ascii.exe工具進行轉換。
不同資源文件的基名必須保持一致(如i18n),並且資源文件的內容是由很多key-value
對組成,key
必須一致(如resource.welcome),value
隨語言/國家地區不同而不同(如美國是“WELCOME”,中國是“歡迎您”)。
本例使用的基名i18n是internationalization(國際化)的縮寫。internationalization的首尾字母i和n中間有18個字母,所以簡稱i18n。
②在SpringMVC的配置文件中,加載國際化資源文件
springmvc.xml
<beans …>
…
<bean id="messageSource" class="org.springframework
.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n" />
</bean>
</beans>
Spring容器在初始化時,會自動加載id
為“messageSource”
,且類型為org.springframework.context.MessageSource
的Bean
,並加載該Bean
中通過basename
屬性指定基名的國際化資源文件。
③使用JSTL標簽實現國際化顯示
先導入JSTL依賴的2個JAR:jstl.jar
和standard.jar
,再在顯示頁success.jsp中導入JSTL用於支持國際化的庫,並使用<fmt:message …>
實現國際化顯示,如下:
顯示頁success.jsp
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
…
<body>
…
<fmt:message key="resource.welcome"></fmt:message>
<br><br>
<fmt:message key="resource.exist"></fmt:message>
<br><br>
</body>
…
fmt
標簽的key
值會根據瀏覽器的語言環境去匹配資源文件中的key
,若匹配則會顯示相應資源文件中key
對應的value
值。例如,中國內陸地區下載的火狐瀏覽器默認的語言是中文,所以會在i18n_zh_CN.properties中尋找key
為“resource.welcome”、“resource.exist”的value
值,並顯示到頁面上,如下:
發送請求頁index.jsp
<a href="FirstSpringDemo/testI18n">testI18n</a><br/>
請求處理類:FirstSpringDemo.java
@Controller
@RequestMapping(value = "/FirstSpringDemo")
public class FirstSpringDemo
{
@RequestMapping("/testI18n")
public String testI18n(){
return "success";
}
…
}
顯示頁面:success.jsp
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
…
<body>
…
<fmt:message key="resource.welcome"></fmt:message>
<br>
<fmt:message key="resource.exist"></fmt:message>
</body>
…
執行index.jsp中的超鏈接,運行結果:
圖28-04
如果將火狐瀏覽器的語言切換為英文,如下:
圖28-05
再次執行index.jsp中的超鏈接,運行結果:
圖28-06
以上就實現了國際化的顯示操作,JSP頁面會根據瀏覽器的語言環境自動尋找相應的資源文件,並依據key
值進行顯示。
注意:就本例來說,國際化顯示標簽必須在success.jsp中才會起作用,而如果將放在index.jsp中則無法實現國際化。這是因為在springmvc.xml中配置的MessageSource(具體是ResourceBundleMessageSource實現類)是用來處理響應的,也就是說只有在請求處理方法返回String、View或ModelAndView對象以后執行響應時,才會通過MessageSource來執行國際化操作。而如果直接在index.jsp中執行<fmt:message ..>,因為此時還沒有“處理響應”這一過程,所以就不會涉及到MessageSource,也就不會根據basename屬性來指定的資源文件基名,因此本例的index.jsp中不能直接實現國際化顯示。
(2)<mvc:view-controller>
我們之前使用SpringMVC的流程大致都是:
①請求頁index.jsp—>②使用@RequestMapping
標識的請求處理類FirstSpringDemo.java
中的方法–>③結果顯示頁success.jsp。
除此以外,我們還可以省略②,讓流程簡化為①—>③,即省略請求處理方法。簡化的方法是在springmvc.xml中配置<mvc:view-controller…/>
,如下:
springmvc.xml
<beans …>
<mvc:view-controller path="/testViewController"
view-name="success"/>
</beans>
其中path用來匹配請求路徑(類似於@RequestMapping
的value
值),view-name
用來指定響應跳轉的頁面(類似於請求處理方法中的return “success”)。以上配置,就表示凡是請求路徑為“testViewController”的,就會被直接跳轉到“success”頁面(“success”會加上InternalResourceViewResolver中設置的前綴和后綴)。如下:
index.jsp(請求路徑是:<mvc:view-controller…/>
中path
指定的testViewController)
<a href="testViewController">testViewController</a><br/>
點擊此超鏈接后,就會直接跳轉到view-name指定的success頁面(/view/success.jsp),即略過了@RequestMapping
標識的請求處理方法。
但是,此時如果我們再點擊index.jsp中之前編寫的其他超鏈接,就都會報“HTTP Status 404”異常。解決辦法就是再在springmvc.xml中加入<mvc:annotation-driven></mvc:annotation-driven>
,如下:
springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
<mvc:view-controller path="/testViewController"
view-name="success"/>
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
一般情況,在使用<mvc:view-controller…/>
的同時,也需要加入<mvc:annotation-driven…/>
標簽。
(3)頁面跳轉方式
當請求處理方法的返回值是字符串時,視圖解析器InternalResourceViewResolver會給返回值加上前綴、后綴,然后默認以請求轉發的方式進行頁面跳轉。此外,我們還可以通過給返回值加上“forward:”
或“redirect:”
來指定跳轉方式為請求轉發或重定向。
①通過“forward:”
指定跳轉方式為請求轉發
@RequestMapping("/testForward")
public String testForward(){
return "forward:/views/success.jsp";
}
②通過“redirect:”
指定跳轉方式為重定向
@RequestMapping("/testRedirect")
public String testRedirect(){
return "redirect:/views/success.jsp";
}
需要注意,加上“forward:”
或“redirect:”
后,視圖解析器將不會再給返回值加上前綴、后綴,需要我們自己寫上完整的響應地址。
28.3 處理靜態資源
如果我們在項目的WebContent目錄下新建imgs
目錄,並存放一張圖片logo.png,如圖,
圖28-07
然后啟動tomcat服務來訪問此圖片http://localhost:8888/SpringMVCDemo/imgs/logo.png,就會看到瀏覽器顯示“HTTP Status 404”異常。這是因為我們之前在web.xml
中配置了DispatcherServlet
,如下:
web.xml
…
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
…
DispatcherServlet的url-pattern是“/”,表示會攔截所有請求。因此,當訪問圖片、js文件、視頻等靜態資源時,也會被DispatcherServlet所攔截並去嘗試匹配相應的@RequestMapping
方法,但靜態資源一般不會有相應的@RequestMapping
,因此會報404異常。為了解決此異常,以便能夠訪問靜態資源,可以在springmvc.xml中加上<mvc:default-servlet-handler/>
和<mvc:annotation-driven></mvc:annotation-driven>
,如下:
springmvc.xml
<beans …>
…
<mvc:default-servlet-handler/>
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
之后,就可以成功訪問到項目中的靜態資源。
<mvc:default-servlet-handler/>
標簽的作用是:此標簽會在SpringMVC上下文中定義一個DefaultServletHttpRequestHandler
,它會對所有DispatcherServlet處理的請求進行篩查,如果發現某個請求沒有相應的@RequestMapping
進行處理(如請求的是圖片等靜態資源),就會將該請求交給WEB服務器(如Tomcat)默認的Servlet處理,而默認Servlet就會直接去訪問該靜態資源。
說明:
Tomcat默認的Servlet是在“tomcat安裝目錄\conf\web.xml”中定義的,如下
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>
org.apache.catalina.servlets.DefaultServlet
</servlet-class>
…
</servlet>
配置了<mvc:default-servlet-handler/>
之后,就可以解決訪問靜態資源時產生的異常。而加入<mvc:annotation-driven></mvc:annotation-driven>
的目的是為了在訪問靜態資源的同時,也能正常的訪問其他非靜態資源。如果只加<mvc:default-servlet-handler/>
而不加<mvc:annotation-driven></mvc:annotation-driven>
,就會造成只能訪問靜態資源,而無法訪問非靜態資源。