1. 程式人生 > >SpringMVC檢視解析器和url跳轉問題

SpringMVC檢視解析器和url跳轉問題

轉自:

SpringMVC檢視解析器

前言

   在前一篇部落格中講了SpringMVC的Controller控制器,在這篇部落格中將接著介紹一下SpringMVC檢視解析器。當我們對SpringMVC控制的資源發起請求時,這些請求都會被SpringMVC的DispatcherServlet處理,接著Spring會分析看哪一個HandlerMapping定義的所有請求對映中存在對該請求的最合理的對映。然後通過該HandlerMapping取得其對應的Handler,接著再通過相應的HandlerAdapter處理該Handler。HandlerAdapter在對Handler進行處理之後會返回一個ModelAndView物件。在獲得了ModelAndView物件之後,Spring就需要把該View渲染給使用者,即返回給瀏覽器。在這個渲染的過程中,發揮作用的就是ViewResolver和View。當Handler返回的ModelAndView中不包含真正的檢視,只返回一個邏輯檢視名稱的時候,ViewResolver就會把該邏輯檢視名稱解析為真正的檢視View物件。View是真正進行檢視渲染,把結果返回給瀏覽器的。

ViewResolver和View介紹

SpringMVC用於處理檢視最重要的兩個介面是ViewResolver和View。ViewResolver的主要作用是把一個邏輯上的檢視名稱解析為一個真正的檢視,SpringMVC中用於把View物件呈現給客戶端的是View物件本身,而ViewResolver只是把邏輯檢視名稱解析為物件的View物件。View介面的主要作用是用於處理檢視,然後返回給客戶端。

Spring為我們提供了非常多的檢視解析器,下面將列舉一些檢視解析器。

AbstractCachingViewResolver:這是一個抽象類,這種檢視解析器會把它曾經解析過的檢視儲存起來,然後每次要解析檢視的時候先從快取裡面找,如果找到了對應的檢視就直接返回,如果沒有就建立一個新的檢視物件,然後把它放到一個用於快取的map中,接著再把新建的檢視返回。使用這種檢視快取的方式可以把解析檢視的效能問題降到最低。

UrlBasedViewResolver:它是對ViewResolver的一種簡單實現,而且繼承了AbstractCachingViewResolver,主要就是提供的一種拼接URL的方式來解析檢視,它可以讓我們通過prefix屬性指定一個指定的字首,通過suffix屬性指定一個指定的字尾,然後把返回的邏輯檢視名稱加上指定的字首和字尾就是指定的檢視URL了。如prefix=/WEB-INF/jsps/,suffix=.jsp,返回的檢視名稱viewName=test/indx,則UrlBasedViewResolver解析出來的檢視URL就是/WEB-INF/jsps/test/index.jsp。預設的prefix和suffix都是空串。URLBasedViewResolver支援返回的檢視名稱中包含redirect:字首,這樣就可以支援URL在客戶端的跳轉,如當返回的檢視名稱是”redirect:test.do”的時候,URLBasedViewResolver發現返回的檢視名稱包含”redirect:”字首,於是把返回的檢視名稱字首”redirect:”去掉,取後面的test.do組成一個RedirectView,RedirectView中將把請求返回的模型屬性組合成查詢引數的形式組合到redirect的URL後面,然後呼叫HttpServletResponse物件的sendRedirect方法進行重定向。同樣URLBasedViewResolver還支援forword:字首,對於檢視名稱中包含forword:字首的檢視名稱將會被封裝成一個InternalResourceView物件,然後在伺服器端利用RequestDispatcher的forword方式跳轉到指定的地址。使用UrlBasedViewResolver的時候必須指定屬性viewClass,表示解析成哪種檢視,一般使用較多的就是InternalResourceView,利用它來展現jsp,但是當我們使用JSTL的時候我們必須使用JstlView。下面是一段UrlBasedViewResolver的定義,根據該定義,當返回的邏輯檢視名稱是test的時候,UrlBasedViewResolver將把邏輯檢視名稱加上定義好的字首和字尾,即“/WEB-INF/test.jsp”,然後新建一個viewClass屬性指定的檢視型別予以返回,即返回一個url為“/WEB-INF/test.jsp”的InternalResourceView物件。

Xml程式碼  收藏程式碼
  1. <bean  
  2.    class="org.springframework.web.servlet.view.UrlBasedViewResolver">  
  3.    <property name="prefix" value="/WEB-INF/" />  
  4.    <property name="suffix" value=".jsp" />  
  5.    <property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceView"/>  
  6. </bean>  

InternalResourceViewResolver:它是URLBasedViewResolver的子類,所以URLBasedViewResolver支援的特性它都支援。在實際應用中InternalResourceViewResolver也是使用的最廣泛的一個檢視解析器。那麼InternalResourceViewResolver有什麼自己獨有的特性呢?單從字面意思來看,我們可以把InternalResourceViewResolver解釋為內部資源檢視解析器,這就是InternalResourceViewResolver的一個特性。InternalResourceViewResolver會把返回的檢視名稱都解析為InternalResourceView物件,InternalResourceView會把Controller處理器方法返回的模型屬性都存放到對應的request屬性中,然後通過RequestDispatcher在伺服器端把請求forword重定向到目標URL。比如在InternalResourceViewResolver中定義了prefix=/WEB-INF/,suffix=.jsp,然後請求的Controller處理器方法返回的檢視名稱為test,那麼這個時候InternalResourceViewResolver就會把test解析為一個InternalResourceView物件,先把返回的模型屬性都存放到對應的HttpServletRequest屬性中,然後利用RequestDispatcher在伺服器端把請求forword到/WEB-INF/test.jsp。這就是InternalResourceViewResolver一個非常重要的特性,我們都知道存放在/WEB-INF/下面的內容是不能直接通過request請求的方式請求到的,為了安全性考慮,我們通常會把jsp檔案放在WEB-INF目錄下,而InternalResourceView在伺服器端跳轉的方式可以很好的解決這個問題。下面是一個InternalResourceViewResolver的定義,根據該定義當返回的邏輯檢視名稱是test的時候,InternalResourceViewResolver會給它加上定義好的字首和字尾,組成“/WEB-INF/test.jsp”的形式,然後把它當做一個InternalResourceView的url新建一個InternalResourceView物件返回。

Xml程式碼  收藏程式碼
  1. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
  2.    <property name="prefix" value="/WEB-INF/"/>  
  3.    <property name="suffix" value=".jsp"></property>  
  4. </bean>  

XmlViewResolver:它繼承自AbstractCachingViewResolver抽象類,所以它也是支援檢視快取的。XmlViewResolver需要給定一個xml配置檔案,該檔案將使用和Spring的bean工廠配置檔案一樣的DTD定義,所以其實該檔案就是用來定義檢視的bean物件的。在該檔案中定義的每一個檢視的bean物件都給定一個名字,然後XmlViewResolver將根據Controller處理器方法返回的邏輯檢視名稱到XmlViewResolver指定的配置檔案中尋找對應名稱的檢視bean用於處理檢視。該配置檔案預設是/WEB-INF/views.xml檔案,如果不使用預設值的時候可以在XmlViewResolver的location屬性中指定它的位置。XmlViewResolver還實現了Ordered介面,因此我們可以通過其order屬性來指定在ViewResolver鏈中它所處的位置,order的值越小優先順序越高。以下是使用XmlViewResolver的一個示例:

(1)在SpringMVC的配置檔案中加入XmlViewResolver的bean定義。使用location屬性指定其配置檔案所在的位置,order屬性指定當有多個ViewResolver的時候其處理檢視的優先順序。關於ViewResolver鏈的問題將在後續內容中講到。

Xml程式碼  收藏程式碼
  1. <bean class="org.springframework.web.servlet.view.XmlViewResolver">  
  2.    <property name="location" value="/WEB-INF/views.xml"/>  
  3.    <property name="order" value="1"/>  
  4. </bean>  

(2)在XmlViewResolver對應的配置檔案中配置好所需要的檢視定義。在下面的程式碼中我們就配置了一個名為internalResource的InternalResourceView,其url屬性為“/index.jsp”。

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"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans  
  5.      http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  6.     <bean id="internalResource" class="org.springframework.web.servlet.view.InternalResourceView">  
  7.        <property name="url" value="/index.jsp"/>  
  8.     </bean>  
  9. </beans>  

(3)定義一個返回的邏輯檢視名稱為在XmlViewResolver配置檔案中定義的檢視名稱——internalResource。

Java程式碼  收藏程式碼
  1. @RequestMapping("/xmlViewResolver")  
  2. public String testXmlViewResolver() {  
  3.    return "internalResource";  
  4. }  

(4)這樣當我們訪問到上面定義好的testXmlViewResolver處理器方法的時候返回的邏輯檢視名稱為“internalResource”,這時候Spring就會到定義好的views.xml中尋找id或name為“internalResource”的bean物件予以返回,這裡Spring找到的是一個url為“/index.jsp”的InternalResourceView物件。

BeanNameViewResolver:這個檢視解析器跟XmlViewResolver有點類似,也是通過把返回的邏輯檢視名稱去匹配定義好的檢視bean物件。不同點有二,一是BeanNameViewResolver要求檢視bean物件都定義在Spring的application context中,而XmlViewResolver是在指定的配置檔案中尋找檢視bean物件,二是BeanNameViewResolver不會進行檢視快取。看一個例子,在SpringMVC的配置檔案中定義了一個BeanNameViewResolver檢視解析器和一個id為test的InternalResourceview bean物件。

Xml程式碼  收藏程式碼
  1. <bean class="org.springframework.web.servlet.view.BeanNameViewResolver">  
  2.    <property name="order" value="1"/>  
  3. </bean>  
  4. <bean id="test" class="org.springframework.web.servlet.view.InternalResourceView">  
  5.    <property name="url" value="/index.jsp"/>  
  6. </bean>  

這樣當返回的邏輯檢視名稱是 test的時候,就會解析為上面定義好id為test的InternalResourceView。

ResourceBundleViewResolver:它和XmlViewResolver一樣,也是繼承自AbstractCachingViewResolver,但是它快取的不是檢視,這個會在後面有說到。和XmlViewResolver一樣它也需要有一個配置檔案來定義邏輯檢視名稱和真正的View物件的對應關係,不同的是ResourceBundleViewResolver的配置檔案是一個屬性檔案,而且必須是放在classpath路徑下面的,預設情況下這個配置檔案是在classpath根目錄下的views.properties檔案,如果不使用預設值的話,則可以通過屬性baseName或baseNames來指定。baseName只是指定一個基名稱,Spring會在指定的classpath根目錄下尋找以指定的baseName開始的屬性檔案進行View解析,如指定的baseName是base,那麼base.properties、baseabc.properties等等以base開始的屬性檔案都會被Spring當做ResourceBundleViewResolver解析檢視的資原始檔。ResourceBundleViewResolver使用的屬性配置檔案的內容類似於這樣:

Properties程式碼  收藏程式碼
  1. resourceBundle.(class)=org.springframework.web.servlet.view.InternalResourceView  
  2. resourceBundle.url=/index.jsp  
  3. test.(class)=org.springframework.web.servlet.view.InternalResourceView  
  4. test.url=/test.jsp  

在這個配置檔案中我們定義了兩個InternalResourceView物件,一個的名稱是resourceBundle,對應URL是/index.jsp,另一個名稱是test,對應的URL是/test.jsp。從這個定義來看我們可以知道resourceBundle是對應的檢視名稱,使用resourceBundle.(class)來指定它對應的檢視型別,resourceBundle.url指定這個檢視的url屬性。會思考的讀者看到這裡可能會有這樣一個問題:為什麼resourceBundle的class屬性要用小括號包起來,而它的url屬性就不需要呢?這就需要從ResourceBundleViewResolver進行檢視解析的方法來說了。ResourceBundleViewResolver還是通過bean工廠來獲得對應檢視名稱的檢視bean物件來解析檢視的。那麼這些bean從哪裡來呢?就是從我們定義的properties屬性檔案中來。在ResourceBundleViewResolver第一次進行檢視解析的時候會先new一個BeanFactory物件,然後把properties檔案中定義好的屬性按照它自身的規則生成一個個的bean物件註冊到該BeanFactory中,之後會把該BeanFactory物件儲存起來,所以ResourceBundleViewResolver快取的是BeanFactory,而不是直接的快取從BeanFactory中取出的檢視bean。然後會從bean工廠中取出名稱為邏輯檢視名稱的檢視bean進行返回。接下來就講講Spring通過properties檔案生成bean的規則。它會把properties檔案中定義的屬性名稱按最後一個點“.”進行分割,把點前面的內容當做是bean名稱,點後面的內容當做是bean的屬性。這其中有幾個特別的屬性,Spring把它們用小括號包起來了,這些特殊的屬性一般是對應的attribute,但不是bean物件所有的attribute都可以這樣用。其中(class)是一個,除了(class)之外,還有(scope)、(parent)、(abstract)、(lazy-init)。而除了這些特殊的屬性之外的其他屬性,Spring會把它們當做bean物件的一般屬性進行處理,就是bean物件對應的property。所以根據上面的屬性配置檔案將生成如下兩個bean物件:

Xml程式碼  收藏程式碼
  1. <bean id="resourceBundle" class="org.springframework.web.servlet.view.InternalResourceView">  
  2.    <property name="url" value="/index.jsp"/>  
  3. </bean>  
  4. <bean id="test" class="org.springframework.web.servlet.view.InternalResourceView">  
  5.    <property name="url" value="/test.jsp"/>  
  6. </bean>  

從ResourceBundleViewResolver使用的配置檔案我們可以看出,它和XmlViewResolver一樣可以解析多種不同型別的View,因為它們的View是通過配置的方式指定的,這也就意味著我們可以指定A檢視是InternalResourceView,B檢視是JstlView。

來看下面這個一個例子,我在SpringMVC的配置檔案中定義了一個ResourceBundleViewResolver物件,指定其baseName為views,然後order為1。

Xml程式碼  收藏程式碼
  1. <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">  
  2.    <property name="basename" value="views"/>  
  3.    <property name="order" value="1"/>  
  4. </bean>  

我在classpath的根目錄下有兩個屬性檔案,一個是views.properties,一個是views_abc.properties,它們的內容分別如下:

views.properties

Properties程式碼  收藏程式碼
  1. resourceBundle.(class)=org.springframework.web.servlet.view.InternalResourceView  
  2. resourceBundle.url=/index.jsp  
  3. test.(class)=org.springframework.web.servlet.view.InternalResourceView  
  4. test.url=/test.jsp  

views_abc.properties

Properties程式碼  收藏程式碼
  1. abc.(class)=org.springframework.web.servlet.view.InternalResourceView  
  2. abc.url=/abc.jsp  

定義瞭如下這樣一個Controller,它有三個處理器方法。

Java程式碼  收藏程式碼
  1. @Controller  
  2. @RequestMapping("/mytest")  
  3. public class MyController {  
  4.     @RequestMapping("resourceBundle")  
  5.     public String resourceBundle() {  
  6.        return "resourceBundle";  
  7.     }  
  8.     @RequestMapping("testResourceBundle")  
  9.     public String testResourceBundle() {  
  10.        return "test";  
  11.     }  
  12.     @RequestMapping("abc")  
  13.     public String abc() {  
  14.        return "abc";  
  15.     }  
  16. }  

那麼當我們請求/mytest/resourceBundle.do的時候,ResourceBundleViewResolver會首先嚐試著來解析該檢視,這裡Controller處理器方法返回的邏輯檢視名稱是resourceBundle,ResourceBundleViewResolver按照上面提到的解析方法進行解析,這個時候它發現它是可以解析的,然後就返回了一個url為/index.jsp的InternalResourceView物件。同樣,請求/mytest/testResourceBundle.do返回的邏輯檢視test和/mytest/abc.do返回的邏輯檢視abc它都可以解析。當我們把basename指定為包的形式,如“com.tiantian.views”,的時候Spring會按照點“.”劃分為目錄的形式,到classpath相應目錄下去尋找basename開始的配置檔案,如上面我們指定basename為“com.tiantian.views”,那麼spring就會到classpath下的com/tiantian目錄下尋找檔名以views開始的properties檔案作為解析檢視的配置檔案。

FreeMarkerViewResolver、VolocityViewResolver:這兩個檢視解析器都是UrlBasedViewResolver的子類。FreeMarkerViewResolver會把Controller處理方法返回的邏輯檢視解析為FreeMarkerView,而VolocityViewResolver會把返回的邏輯檢視解析為VolocityView。因為這兩個檢視解析器類似,所以這裡我就只挑FreeMarkerViewResolver來做一個簡單的講解。FreeMarkerViewResolver和VilocityViewResolver都繼承了UrlBasedViewResolver。

對於FreeMarkerViewResolver而言,它會按照UrlBasedViewResolver拼接URL的方式進行檢視路徑的解析。但是使用FreeMarkerViewResolver的時候不需要我們指定其viewClass,因為FreeMarkerViewResolver中已經把viewClass定死為FreeMarkerView了。

我們先在SpringMVC的配置檔案裡面定義一個FreeMarkerViewResolver檢視解析器,並定義其解析檢視的order順序為1。

Xml程式碼  收藏程式碼
  1. <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">  
  2.    <property name="prefix" value="fm_"/>  
  3.    <property name="suffix" value=".ftl"/>  
  4.    <property name="order" value="1"/>  
  5. </bean>  

那麼當我們請求的處理器方法返回一個邏輯檢視名稱viewName的時候,就會被該檢視處理器加上前後綴解析為一個url為“fm_viewName.ftl”的FreeMarkerView物件。對於FreeMarkerView我們需要給定一個FreeMarkerConfig的bean物件來定義FreeMarker的配置資訊。FreeMarkerConfig是一個介面,Spring已經為我們提供了一個實現,它就是FreeMarkerConfigurer。我們可以通過在SpringMVC的配置檔案裡面定義該bean物件來定義FreeMarker的配置資訊,該配置資訊將會在FreeMarkerView進行渲染的時候使用到。對於FreeMarkerConfigurer而言,我們最簡單的配置就是配置一個templateLoaderPath,告訴Spring應該到哪裡尋找FreeMarker的模板檔案。這個templateLoaderPath也支援使用“classpath:”和“file:”字首。當FreeMarker的模板檔案放在多個不同的路徑下面的時候,我們可以使用templateLoaderPaths屬性來指定多個路徑。在這裡我們指定模板檔案是放在“/WEB-INF/freemarker/template”下面的。

Xml程式碼  收藏程式碼
  1. <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">  
  2.    <property name="templateLoaderPath" value="/WEB-INF/freemarker/template"/>  
  3. </bean>  

接下來我們定義如下一個Controller:

Java程式碼  收藏程式碼
  1. @Controller  
  2. @RequestMapping("/mytest")  
  3. public class MyController {  
  4.     @RequestMapping("freemarker")  
  5.     public ModelAndView freemarker() {  
  6.        ModelAndView mav = new ModelAndView();  
  7.        mav.addObject("hello""andy");  
  8.        mav.setViewName("freemarker");  
  9.        return mav;  
  10.     }  
  11. }  

由上面的定義我們可以看到這個Controller的處理器方法freemarker返回的邏輯檢視名稱是“freemarker”。那麼如果我們需要把該freemarker檢視交給FreeMarkerViewResolver來解析的話,我們就需要根據上面的定義,在模板路徑下定義檢視對應的模板,即在“/WEB-INF/freemarker/template”目錄下建立fm_freemarker.ftl模板檔案。這裡我們定義其內容如下:

Ftl程式碼  收藏程式碼
  1. <html>  
  2.     <head>  
  3.        <title>FreeMarker</title>  
  4.     </head>  
  5.     <body>  
  6.        <b>Hello World</b>  
  7.        <font color="red">Hello World!</font>  
  8.        ${hello}  
  9.     </body>  
  10. </html>  

經過上面的定義當我們訪問/mytest/freemarker.do的時候就會返回一個邏輯檢視名稱為“freemarker”的ModelAndView物件,根據定義好的檢視解析的順序,首先進行檢視解析的是FreeMarkerViewResolver,這個時候FreeMarkerViewResolver會試著解析該檢視,根據它自身的定義,它會先解析到該檢視的URL為fm_freemarker.ftl,然後它會看是否能夠例項化該檢視物件,即在定義好的模板路徑下是否有該模板存在,如果有則返回該模板對應的FreeMarkerView。在這裡的話/WEB-INF/freemarker/template目錄下是存在模板檔案fm_freemarker.ftl的,所以會返回一個url為fm_freemarker.ftl的FreeMarkerView物件。接著FreeMarkerView就可以利用該模板檔案進行檢視的渲染了。所以訪問結果應該如下所示:

 

檢視解析器鏈

       在SpringMVC中可以同時定義多個ViewResolver檢視解析器,然後它們會組成一個ViewResolver鏈。當Controller處理器方法返回一個邏輯檢視名稱後,ViewResolver鏈將根據其中ViewResolver的優先順序來進行處理。所有的ViewResolver都實現了Ordered介面,在Spring中實現了這個介面的類都是可以排序的。在ViewResolver中是通過order屬性來指定順序的,預設都是最大值。所以我們可以通過指定ViewResolver的order屬性來實現ViewResolver的優先順序,order屬性是Integer型別,order越小,對應的ViewResolver將有越高的解析檢視的權利,所以第一個進行解析的將是ViewResolver鏈中order值最小的那個。當一個ViewResolver在進行檢視解析後返回的View物件是null的話就表示該ViewResolver不能解析該檢視,這個時候如果還存在其他order值比它大的ViewResolver就會呼叫剩餘的ViewResolver中的order值最小的那個來解析該檢視,依此類推。當ViewResolver在進行檢視解析後返回的是一個非空的View物件的時候,就表示該ViewResolver能夠解析該檢視,那麼檢視解析這一步就完成了,後續的ViewResolver將不會再用來解析該檢視。當定義的所有ViewResolver都不能解析該檢視的時候,Spring就會丟擲一個異常。

       基於Spring支援的這種ViewResolver鏈模式,我們就可以在SpringMVC應用中同時定義多個ViewResolver,給定不同的order值,這樣我們就可以對特定的檢視特定處理,以此來支援同一應用中有多種檢視型別。注意:像InternalResourceViewResolver這種能解析所有的檢視,即永遠能返回一個非空View物件的ViewResolver一定要把它放在ViewResolver鏈的最後面。

Xml程式碼  收藏程式碼
  1. <bean class="org.springframework.web.servlet.view.XmlViewResolver">  
  2.    <property name="location" value="/WEB-INF/views.xml"/>  
  3.    <property name="order" value="1"/>  
  4. </bean>  
  5. <bean  
  6.    class="org.springframework.web.servlet.view.UrlBasedViewResolver">  
  7.    <property name="prefix" value="/WEB-INF/" />  
  8.    <property name="suffix" value=".jsp" />  
  9.    <property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceView"/>  
  10. </bean>  

-----------------------------------------------------------------------華麗分割線----------------------------------------------------------------------------------

最近,在網上找了個html模板回來,整個資料夾複製到springMvc專案裡,於是想訪問某個action就渲染到該html,發現,html效果沒出來,佈局錯亂;

如圖:


html能正常訪問,可是,該html裡的js、css、image都不能正常顯示出來。路徑不匹配。

經除錯,該css和js都是顯示跟目錄下的。問題是,我的html放在一個資料夾html下的。


xml配置的檢視解析器如下:

   <bean id="freemarkerConfig"
	    class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
	    <!-- 根目錄下的html資料夾  -->
	    <property name="templateLoaderPath" value="/html"/><!--我們最簡單的配置就是配置一個templateLoaderPath,告訴Spring應該到哪裡尋找FreeMarker的模板檔案 -->
	    <property name="freemarkerSettings">
			<props>
				<prop key="tag_syntax">auto_detect</prop>
				<prop key="template_update_delay">5</prop>
				<prop key="defaultEncoding">UTF-8</prop>
				<prop key="url_escaping_charset">UTF-8</prop>
				<prop key="locale">zh_CN</prop>
				<prop key="boolean_format">true,false</prop>
				<prop key="datetime_format">yyyy-MM-dd HH:mm:ss</prop>
				<prop key="date_format">yyyy-MM-dd</prop>
				<prop key="time_format">HH:mm:ss</prop>
				<prop key="number_format">0.######</prop>
				<prop key="whitespace_stripping">true</prop>
			</props>
		</property>
	</bean>
    <!-- 模型檢視解析器 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
	    <property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView"/>
        <property name="cache" value="true"/>
        <property name="prefix" value="/"/>
        <property name="suffix" value=".html"/>
        <property name="contentType" value="text/html;charset=utf-8"/>
        <property name="exposeRequestAttributes" value="true"/>
        <property name="exposeSessionAttributes" value="true"/>
        <property name="exposeSpringMacroHelpers" value="true"/>
        <property name="order" value="0"/>
    </bean>
    <bean id="viewResolver2" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass"><value>org.springframework.web.servlet.view.JstlView</value></property>
        <property name="prefix" value="/"></property>
        <property name="suffix" value=".jsp" ></property>
        <property name="order" value="1"></property>
    </bean>

如何才能把路徑更改正確呢?

錯誤:http://localhost:8080/TestSpringMVC/style.css

正確:http://localhost:8080/TestSpringMVC/html/style.css

更改兩個配置檔案都還是不行,最後,在action里加入如圖:

解決