问题大概是这样的:我配置了两个viewResovler:
[code="java"]
utf-8
utf-8
text/html;charset=UTF-8
0
text/html;charset=UTF-8
1
[/code]
目的是为了,请求多种视图,viewResovler的order如上图使用
[code="java"]
@RequestMapping(value ="/welcome/user")
public ModelAndView welcome(HttpServletRequest request,HttpServletResponse response,
ModelMap modelMap) {
return new ModelAndView("welcome",modelMap);
//普通转向
}
[/code]
第一次http://localhost:8080/Prj/welcome/user 请求/WEB-INF/velo/目录底下的welcome.vm文件可以正常访问
当我把java代码修改成这样
[code="java"]
@RequestMapping(value ="/welcome/user")
public ModelAndView welcome(HttpServletRequest request,HttpServletResponse response,
ModelMap modelMap) {
return new ModelAndView("welcome",modelMap);
//普通转向
}
[/code]
并且把jspresovler的order改成0,把velocity的resovler改成1
目的就是为了在/WEB-INF/jsp/目录下找不到welcome.jsp文件,按理说servlet会继续在另外一个resovler里面来查找.vm文件
应该是去找 /WEB-INF/veclo/目录底下寻找 welcome.vm文件,但是
当我请求上面的请求的时候,tomcat提示404错误,后台打印 说找不到welcome.jsp文件
是什么原因?
各位大虾?
补充:当我把两个order都设为0的时候,也是可以找到的
[code="java"]public boolean checkResource(Locale locale) throws Exception {
return true;
}[/code]
罪魁祸首 就是spring实现该方法时返回true,这样相当于view总是存在,从而导致其余视图解析器无法得到解析机会。
覆盖该方法,应该就可以了。
[code="java"]public class IcomJstlView extends JstlView {
public boolean checkResource(Locale locale) throws Exception {
File file = new File(this.getServletContext().getRealPath("/")+getUrl());
return file.exists();//判断该jsp页面是否存在
}
}[/code]
我没有用过两个视图,所以看了一下源码,看代码:
[code="java"]
DispatchServlet.java中 doDispatch最后是调用render方法做视图渲染的:
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
throws Exception {
// Determine locale for request and apply it to the response.
Locale locale = this.localeResolver.resolveLocale(request);
response.setLocale(locale);
View view = null;
if (mv.isReference()) {
// We need to resolve the view name.
view = resolveViewName(mv.getViewName(), mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isDebugEnabled()) {
logger.debug("Rendering view [" + view + "] in DispatcherServlet with name '" + getServletName() + "'");
}
view.render(mv.getModelInternal(), request, response);
}
mv.isReference() 是说你是不是用名字代替传递一个View实例给ModelAndView对象,你传递的都是名字,所以再看
protected View resolveViewName(String viewName, Map model, Locale locale, HttpServletRequest request)
throws Exception {
for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {
ViewResolver viewResolver = (ViewResolver) it.next();
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
return view;
}
}
return null;
}
this.viewResolvers 这个就是你配的两个viewResolvers,分别试着去创建View,再看看创建View的代码
public View resolveViewName(String viewName, Locale locale) throws Exception {
if (!isCache()) {
logger.warn("View caching is SWITCHED OFF -- DEVELOPMENT SETTING ONLY: This can severely impair performance");
return createView(viewName, locale);
}
else {
Object cacheKey = getCacheKey(viewName, locale);
synchronized (this.viewCache) {
View view = (View) this.viewCache.get(cacheKey);
if (view == null) {
// Ask the subclass to create the View object.
view = createView(viewName, locale);
this.viewCache.put(cacheKey, view);
if (logger.isTraceEnabled()) {
logger.trace("Cached view [" + cacheKey + "]");
}
}
return view;
}
}
}
最终落实到 createView,因为你的两个viewResolvers都是实现的 UrlBasedViewResolver,所以看UrlBasedViewResolver这里面的buildView方法就行了
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
AbstractUrlBasedView view = (AbstractUrlBasedView) BeanUtils.instantiateClass(getViewClass());
view.setUrl(getPrefix() + viewName + getSuffix());
String contentType = getContentType();
if (contentType != null) {
view.setContentType(contentType);
}
view.setRequestContextAttribute(getRequestContextAttribute());
view.setAttributesMap(getAttributesMap());
return view;
}
并没有看到有试图去找(getPrefix() + viewName + getSuffix())对应的文件,所以并不会返回null,所以只查找了jsp就没有查找fm了
Spring并不会去检查你这个文件(welcome.jsp)是否存在,只是最后渲染的时候交给给自处理 VelocityViewResolver交给Velocity处理,
InternalResourceViewResolver交给 servlet request 处理
也可以说UrlBasedViewResolver找出来的View没有被Spring容器管理,所以他不知道有没有
如果想实现你的功能,可以扩展DispatchServlet的resolveViewName方法,例如:
class MyDispatcherServlet extends DispatcherServlet {
private List viewResolvers;
@Override
protected View resolveViewName(String viewName, Map model, Locale locale,
HttpServletRequest request) throws Exception {
WebApplicationContext context = getWebApplicationContext();
for (Iterator it = this.viewResolvers.iterator(); it.hasNext();) {
ViewResolver viewResolver = (ViewResolver) it.next();
View view = viewResolver.resolveViewName(viewName, locale);
if (view != null) {
if (view instanceof AbstractUrlBasedView) {
try {
Resource resource = context.getResource(((AbstractUrlBasedView) view).getUrl());
if (resource == null) {
// log
continue;
}
} catch (Exception e) {
// log
continue;
}
}
return view;
}
}
return null;
}
}
当然你要初始化viewResolvers 因为DispatchServlet中该属性为私有的
[/code]
网上找到的:
如果为DispatcherServlet指定多个ViewResolver的话,不要给予InternalResour- ceViewResolver以及其他UrlBasedViewResolver子类过高的优先级,因为这些ViewResolver即使找不到相应的视图,也不会返回null以给我们轮询下一个ViewResolver的机会,这样,我们所指定的其他ViewResolver实际上就形同虚设。合理的处理方式是,给予ResourceBundleView- Resolver或者XmlViewResolver这种能够通过返回null以表明无法找到相应视图的ViewResolver较高的优先级,而只是将InternalResourceViewResolver(或者其他类似行为的ViewResolver)添加为最低优先级ViewResolver,以作为DispatcherServlet的后备查找对象。
不错