springMVC 配置——viewResolver
在springmvc的配置中,很重要的一个就是viewResolver,中文叫做视图解析器。他的作用就是处理MVC模式下从C到V的连接。视图可以使jsp的,也可以是velocity的,也可以是freemarker的,针对于不同的视图,springmvc定义了不同的视图解析器,所有的这些在org.springframework.web.servlet.view.UrlBasedViewResolver类的源码中都可以找到。今天我就把我对这个类的源码的总结记录下来。
视图解析器有很多个,一般的我们使用jsp作为视图的时候会使用org.springframework.web.servlet.view.InternalResourceViewResolver,使用velocity作为视图时会选择org.springframework.web.servlet.view.velocity.VelocityViewResolver,他们的共同的配置都在这个UrlBasedViewResolver中。
普遍的配置如下:prefix="/WEB-INF/JSP/" suffix=".jsp" 从controller返回的viewname是test,那么最终的跳转到的是/WEB-INF/jsp/test.jsp。
1、redirect:
如果一个路径是以redirect开头的,比如“redirect:myAction.do”,会出发一个重定向的指令,即302,而不是和普通的路径一样找视图。
2、forward:
如果一个路径是以forward开头的,比如forward:myAction.do就会出发一次新的访问,就是重新访问一次springmvc的servlet,这个就像是我们在使用servlet开发时的forward,只不过servlet中的forward是跳转的有jsp生成的servlet。
3、配置多个resolver,
如果配置多个viewResolver的话,可以使用下面的order或者是viewNames,但是不能使用前缀或者是后缀,因为这两个都是用来添加到viewName上的的,不是用来区别viewName的。
在配置多个resolver的时候,还有个需要注意的地方,即当没有找打指定的url对应的资源的时候,除了InternalViewResolver外,都会让停止解析,但是InternalViewResolver不会,所以源码中说要将InternalViewResolver放在最后,不过我觉得最好的办法还是使用viewNames来判断,比如我们有jsp和velocity的,我们可以在jsp的InternalViewResolver中设置suffix为“”,(默认就是“”),viewNames为“*.jsp”,然后再controller中返回的是xxx.jsp,在velocity的viewResolver中设置suffix为.vm,然后再controller中返回的就是xxx.vm,这样就不存在解析顺序的问题两天。
4、contentType属性
设置所有视图的属性,也就是jsp中的<meta Content-Type..../>,源码中的javadoc中说这个可能被忽略,如果视图已经有了这个配置的话,比如jsp中。
5、redirectContextRelative
这个属性很重要,默认是true,表示的是在重定向时,要不要以当前的contextPath以相对路径,如果是在servlet中的重定向时,我们必须写上当前应用的contextPath,但是在springmvc中我们在重定向时不用写,我想起来刚开始学习springmvc时我还烦了错误,现在知道了原来springmvc默认自动给我们加上了contextPath,当然我们是可以通过这个配置为false的。
6、viewNames
是一个字符串的数组
这个属性是我今天第一次发现,他的意思是这样的,在我们配置的viewResolver中,在解析视图时先回调用这个类中的canHandle方法,这个方法内部就是调用正则表达式判断要解析的viewName能不能匹配当前viewResolver的newNames,只有在能解析的时候才会继续解析。
canhandle的源码:
protected boolean canHandle(String viewName, Locale locale) { String[] viewNames = getViewNames(); return (viewNames == null || PatternMatchUtils.simpleMatch(viewNames, viewName)); }
可以发现当viewName是null就会解析,PatternMatchUtils.simpleMath的源码如下:
public static boolean simpleMatch(String[] patterns, String str) { if (patterns != null) { for (int i = 0; i < patterns.length; i++) { if (simpleMatch(patterns[i], str)) { return true; } } } return false; }
这个的判定条件是正则表达式匹配,只要有个匹配即可。 在viewResolver的viewName中可以使用简单的通配符,比如*。
我这里做了一个实验:
我的springmvc的配置文件是这样的:
<!-- 跳转到jsp的viewResolver --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> <property name="viewNames"> <array> <value>a*</value> </array> </property> </bean>
我的意图是这个viewResolver只能解析以a开头的试图,其他的都不能解析,我的controller是这样的
@Controller @RequestMapping("/hello") public class HelloWorldController { @RequestMapping("aaa.do") public String aaa(){ return "aaa"; } @RequestMapping("bbb.do") public String bbb(){ return "bbb"; }
当我访问aaa.do的时候,正常访问,但是当我访问bbb.do的时候就报了一下的错误:
[Could not resolve view with name 'bbb' in servlet with name 'springDispatcherServlet'] with root cause
javax.servlet.ServletException: Could not resolve view with name 'bbb' in servlet with name 'springDispatcherServlet',于是我们的理解得到验证。
7、order属性
int类型的,源码的doc中说的是制定这个resolver的执行顺序,我做了如下的测试
我在springmvc的配置文件中加入了两个viewResolver,然后给他们不同的前缀和order,看一下到底是哪一个执行还是两个都执行
我的配置的截图如下:
<bean name="resolver1" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> <property name="order" value="1"></property> </bean> <bean name="resolver2" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp2/" /> <property name="suffix" value=".jsp" /> <property name="order" value="2"></property> </bean>
然后在/WEB-INF下创建了一个jsp2的文件夹,然后再访问之前的aaa.do,发现是配置的order=1的resolver起作用了,然后我又把原先order=1的resolver改为order=3,发现是仅仅执行了order=2的resolver,可以总结order小的先执行,而且执行完了之后其他的resolver不会执行了。