问题描述
问题大概是这样的:我配置了两个viewResovler: <bean id="velocityConfigurer" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer"> <property name="resourceLoaderPath" value="/WEB-INF/velo/"/> <property name= "velocityProperties"> <props> <prop key="input.encoding">utf-8</prop> <prop key="output.encoding">utf-8</prop> </props> </property> </bean><bean id="viewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver"> <property name="cache" value="false"/> <property name="prefix" value=""/> <property name="suffix" value=".vm"/> <property name="contentType"><value>text/html;charset=UTF-8</value></property> <property name="exposeSpringMacroHelpers" value="true"/> <property name="order"><value>0</value></property></bean> <bean id="JSPviewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="cache" value="false"/> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> <property name="contentType"><value>text/html;charset=UTF-8</value></property> <property name="order"><value>1</value></property></bean>目的是为了,请求多种视图,viewResovler的order如上图使用@RequestMapping(value ="/welcome/user")public ModelAndView welcome(HttpServletRequest request,HttpServletResponse response,ModelMap modelMap) {return new ModelAndView("welcome",modelMap);//普通转向}第一次http://localhost:8080/Prj/welcome/user 请求/WEB-INF/velo/目录底下的welcome.vm文件可以正常访问当我把java代码修改成这样@RequestMapping(value ="/welcome/user")public ModelAndView welcome(HttpServletRequest request,HttpServletResponse response,ModelMap modelMap) {return new ModelAndView("welcome",modelMap);//普通转向}并且把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的时候,也是可以找到的
解决方案
public boolean checkResource(Locale locale) throws Exception {return true;}罪魁祸首 就是spring实现该方法时返回true,这样相当于view总是存在,从而导致其余视图解析器无法得到解析机会。 覆盖该方法,应该就可以了。 public class IcomJstlView extends JstlView {public boolean checkResource(Locale locale) throws Exception {File file = new File(this.getServletContext().getRealPath("/")+getUrl());return file.exists();//判断该jsp页面是否存在}}
解决方案二:
不错
解决方案三:
我没有用过两个视图,所以看了一下源码,看代码: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;@Overrideprotected 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) {// logcontinue;}} catch (Exception e) {// logcontinue;}}return view;}}return null;}}当然你要初始化viewResolvers 因为DispatchServlet中该属性为私有的网上找到的:如果为DispatcherServlet指定多个ViewResolver的话,不要给予InternalResour- ceViewResolver以及其他UrlBasedViewResolver子类过高的优先级,因为这些ViewResolver即使找不到相应的视图,也不会返回null以给我们轮询下一个ViewResolver的机会,这样,我们所指定的其他ViewResolver实际上就形同虚设。合理的处理方式是,给予ResourceBundleView- Resolver或者XmlViewResolver这种能够通过返回null以表明无法找到相应视图的ViewResolver较高的优先级,而只是将InternalResourceViewResolver(或者其他类似行为的ViewResolver)添加为最低优先级ViewResolver,以作为DispatcherServlet的后备查找对象。