SpringMVC檢視解析器InternalResourceViewResolver
今天在搭建SpringMVC開發框架的時候,出現freemarker的檢視沒有找到,報404錯誤。我的配置程式碼如下:
<!--freemarker --> <mvc:view-controller path="/" view-name="homepage/index”/> <!-- jsp --> <mvc:view-controller path="/login" view-name="login”/> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> <property name="contentType" value="text/html;charset=UTF-8"/> <property name="order" value="2"/> </bean> <bean class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> <property name="prefix" value="" /> <property name="suffix" value=".ftl" /> <property name="contentType" value="text/html;charset=UTF-8"/> <property name="order" value="3"/> </bean>
我配置了兩個檢視解析器,所以檢視解析是鏈式的,如果一個檢視解析器沒有找到對應的view-name,則開始找第二的解析器。InternalResourceViewResolver的property中的order值是2,FreeMarkerViewResolver的property的order為3,我們知道order,從小到大,越小的優先順序越高,那麼InternalResourceViewResolver一定是先執行的。
InternalResourceViewResolver的父類UrlBasedViewResolver中有一個方法loadView用於建立載入檢視,原始碼如下:
/** * Delegates to {@code buildView} for creating a new instance of the * specified view class, and applies the following Spring lifecycle methods * (as supported by the generic Spring bean factory): * <ul> * <li>ApplicationContextAware's {@code setApplicationContext} * <li>InitializingBean's {@code afterPropertiesSet} * </ul> * * @param viewName the name of the view to retrieve * @return the View instance * @throws Exception if the view couldn't be resolved * @see #buildView(String) * @see org.springframework.context.ApplicationContextAware#setApplicationContext * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet */ @Override protected View loadView(String viewName, Locale locale) throws Exception { AbstractUrlBasedView view = buildView(viewName); View result = applyLifecycleMethods(viewName, view); return (view.checkResource(locale) ? result : null); }
最後一行用到了view的checkResource方法。
我們看到InternalResourceViewResolver對應的view是InternalResourceView,它的子類只有JstlView,下面是view的類圖關係。
檢視InternalResourceView的父類AbstractUrlBasedView,可以找到它裡面的checkResource方法,原始碼如下: ---------------------
** * Check whether the underlying resource that the configured URL points to * actually exists. * * @param locale the desired Locale that we're looking for * @return {@code true} if the resource exists (or is assumed to exist); * {@code false} if we know that it does not exist * @throws Exception if the resource exists but is invalid (e.g. could not be parsed) */ public boolean checkResource(Locale locale) throws Exception { return true; }
可以看到這個方法直接返回true,根本就沒有check。
至此我們就找到問題的原因了,InternalResourceView是不會check資原始檔是否存在,當InternalResourceViewResolver先執行的時候,遇到其他的view-name如本例的freemarker的檔案根本不會做check,導致最終出現404的情況。
如何解決這個問題呢?
第一種方法:把order的值修改下,把InternalResourceViewResolver的order改成最大的,即最後解析讓其他的會check檔案是否存在的解析器先執行。
第二種方法:自定義一個view類繼承JstlView,自己寫一個checkResource將父類的的checkResource override掉。程式碼如下:
---------------------
package com.tonitech.ancestor.common;
import org.springframework.web.servlet.view.JstlView;
import java.io.File;
import java.util.Locale;
/**
* 解決多個ViewResolver時jsp獲取不到時,跳轉到下一個ViewResolver
*/
public class DefaultJstlView extends JstlView {
@Override
public boolean checkResource(Locale locale) throws Exception {
File file = new File(this.getServletContext().getRealPath("/") + getUrl());
return file.exists();//判斷該jsp頁面是否存在
}
}
package com.common;
import org.springframework.web.servlet.view.JstlView;
import java.io.File;
import java.util.Locale;
public class DefaultJstlView extends JstlView {
public boolean checkResource(Locale locale) throws Exception {
File file = new File(this.getServletContext().getRealPath("/") + getUrl());
return file.exists();// 判斷該jsp頁面是否存在
}
}
然後在InternalResourceViewResolver的bean配置里加一行:
<property name="viewClass" value="com.tonitech.ancestor.common.DefaultJstlView"/>