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用于创建加载视图,源码如下:
@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方法,源码如下:
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页面是否存在
}
}
然后在InternalResourceViewResolver的bean配置里加一行:
<
property
name
=
"viewClass"
value
=
"com.tonitech.ancestor.common.DefaultJstlView"
/>
通过这两种方法都可以解决问题。