Spring提供了两种支持JSP视图的方式:
1.InternalResourceViewResolver会将视图名解析为JSP文 件。另外,如果在你的JSP页面中使用了JSP标准标签库 (JavaServer Pages Standard Tag Library,JSTL)的 话,InternalResourceViewResolver能够将视图名解析为 JstlView形式的JSP文件,从而将JSTL本地化和资源bundle变量暴 露给JSTL的格式化(formatting)和信息(message)标签。
2.Spring提供了两个JSP标签库,一个用于表单到模型的绑定,另一 个提供了通用的工具类特性。1. <%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>:绑定标签库。如:<sf:checkbox>、<sf:checkboxes>、<sf:errors>、<sf:form>、<sf:input>、<sf:select>...等等2. <%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>:通用标签库。<s:bind>、<s:escapeBody>、<s:htmlEscape>、<s:message>、<s:url>、<s:url>...等等
sltViewResolver
将视图名解析为一个指定XSLT样式表的URL文件。比如解析成Excel表格形式、 World形式等等。此处略~
AbstractTemplateViewResolver
继承自UrlBasedViewResolver,重写了buildView方法,主要就是构造AbstractTemplateView以及为它设置相应的属性。从命名中也能看出,它提供的是一种模版技术
// 模板视图解析程序的抽象基类,尤其是FreeMarker视图的抽象基类 // @since 1.1 对应的View是AbstractTemplateView public class AbstractTemplateViewResolver extends UrlBasedViewResolver { // 是否吧所有热request里面的attributes都加入合并到模版的Model,默认是false private boolean exposeRequestAttributes = false; // 是否允许request里面的属性,当name相同的时候,复写model里面的 默认是false private boolean allowRequestOverride = false; // session相关,语义同上 private boolean exposeSessionAttributes = false; private boolean allowSessionOverride = false; // Set whether to expose a RequestContext for use by Spring's macro library 默认值是true private boolean exposeSpringMacroHelpers = true; // 它只会处理AbstractTemplateView 比如FreeMarkerView是它的实现类 @Override protected Class<?> requiredViewClass() { return AbstractTemplateView.class; } // 模版操作:其实就是多设置了一些开关属性~~~~ @Override protected AbstractUrlBasedView buildView(String viewName) throws Exception { AbstractTemplateView view = (AbstractTemplateView) super.buildView(viewName); view.setExposeRequestAttributes(this.exposeRequestAttributes); view.setAllowRequestOverride(this.allowRequestOverride); view.setExposeSessionAttributes(this.exposeSessionAttributes); view.setAllowSessionOverride(this.allowSessionOverride); view.setExposeSpringMacroHelpers(this.exposeSpringMacroHelpers); return view; } }
VelocityViewResolver也是继承自此AbstractTemplateViewResolver
ThymeleafViewResolver并没有继承自AbstractTemplateViewResolver,而是直接继承AbstractCachingViewResolver的
GroovyMarkupViewResolver
略
FreeMarkerViewResolver
@Override protected Class<?> requiredViewClass() { return FreeMarkerView.class; }
逻辑很简单。
FreeMarker是个老牌的模版引擎,整体性能也还不错,所以一直以来口碑还不错。但在新时代的发展下,显然还是有点乏力了的~~~
BeanNameViewResolver
它是对ViewResolver的一个比较简单的实现,在Spring第一个版本就推出了。通过把返回的逻辑视图名称去匹配定义好的视图 bean 对象。(也就是说如果你返回的逻辑视图名称为test,那么它就会去容器内找到这个View,然后返回)
public class BeanNameViewResolver extends WebApplicationObjectSupport implements ViewResolver, Ordered { // 默认排序最小值~~~ private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered public void setOrder(int order) { this.order = order; } @Override public int getOrder() { return this.order; } @Override @Nullable public View resolveViewName(String viewName, Locale locale) throws BeansException { // 可见它和容器强关联,若容器里没有这个Bean,他就直接返回null了~~~ ApplicationContext context = obtainApplicationContext(); if (!context.containsBean(viewName)) { // Allow for ViewResolver chaining... return null; } // 可见不仅仅要含有此Bean,还必须是view类型的~~~~ 否则也是返回null if (!context.isTypeMatch(viewName, View.class)) { if (logger.isDebugEnabled()) { logger.debug("Found bean named '" + viewName + "' but it does not implement View"); } return null; } // 拿出这个View就这直接返回了~~~ return context.getBean(viewName, View.class); } }
ViewResolverComposite
看过直接一篇文章:
【小家Spring】Spring MVC容器的web九大组件之—HandlerAdapter源码详解—一篇文章带你读懂返回值处理器HandlerMethodReturnValueHandler
里面讲过HandlerMethodReturnValueHandlerComposite,这个类就无需多说了。
// @since 4.1 public class ViewResolverComposite implements ViewResolver, Ordered, InitializingBean, ApplicationContextAware, ServletContextAware { private final List<ViewResolver> viewResolvers = new ArrayList<>(); private int order = Ordered.LOWEST_PRECEDENCE; // 若直接set了,就以自己的set为主 public void setViewResolvers(List<ViewResolver> viewResolvers) { this.viewResolvers.clear(); if (!CollectionUtils.isEmpty(viewResolvers)) { this.viewResolvers.addAll(viewResolvers); } } // 为每一个实现了接口ApplicationContextAware的 都设置一个 下面还有其它的 @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { for (ViewResolver viewResolver : this.viewResolvers) { if (viewResolver instanceof ApplicationContextAware) { ((ApplicationContextAware)viewResolver).setApplicationContext(applicationContext); } } } ... // 这是核心 遍历所有的viewResolvers,第一个不返回null的,就标出处理了~~~~ @Override @Nullable public View resolveViewName(String viewName, Locale locale) throws Exception { for (ViewResolver viewResolver : this.viewResolvers) { View view = viewResolver.resolveViewName(viewName, locale); if (view != null) { return view; } } return null; } }
它用于WebMvcConfigurationSupport
配置的时候,会配置上这个ViewResolverComposite
用于对所有的View解析器做聚合。
Demo:使用BeanNameViewResolver
做一个自定义的视图
@Component public class HelloView implements View { @Override public String getContentType() { return MediaType.TEXT_HTML_VALUE; } // 这里渲染,就向控制台写一句话即可~~~~ @Override public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { response.getWriter().print("Welcome to View:" + new Date()); } } @Controller public class MyView { @RequestMapping(value="/testBeanNameViewResolver") public String testView(){ System.out.println("testBeanNameViewResolver"); return "helloView"; } } // 把BeanNameViewResolver 配置进容器 @Configuration @EnableWebMvc public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void configureViewResolvers(ViewResolverRegistry registry) { BeanNameViewResolver viewResolver = new BeanNameViewResolver(); viewResolver.setOrder(10); // 这样能保证在InternalResourceViewResolver之前执行 registry.viewResolver(viewResolver); } // 这是错误的注册方式~~~会让ViewResolverComposite可能失效~~ //@Bean //public ViewResolver viewResolver() { // BeanNameViewResolver viewResolver = new BeanNameViewResolver(); // viewResolver.setOrder(10); // 这样能保证在InternalResourceViewResolver之前执行 // return viewResolver; //}
这样我们访问:http://localhost:8080/demo_war_war/testBeanNameViewResolver就会自动到我们自定义的view上去。从现实页面:
备注:这个视图解析器的使用场景:一般用于自定义视图,然后通过这个视图解析器指过去
最后需要注意的是,这么多处理器,都实现了Order接口,因此自己向Spring MVC注册view解析器的时候,务必注意他们的顺序问题~~~(因为DispatcherServlet初始化的时候,会根据Order排序的)
Spring MVC默认装配的视图解析器们
开启注解:@EnableWebMvc。如下截图可以看到默认只会装配InternalResourceViewResolver这一个视图解析器,且是直接new InternalResourceViewResolver()的,都是默认值~~
不开启注解:@EnableWebMvc。默认装配的也是它(在DispatcherServlet.properties配置文件里)
由此可见默认情况下,它是支持jsp文件解析、访问的。若你想扩展一些别的视图解析,可以自己扩展注册~~
总结
Spring MVC很优秀的之一,就是把视图解析、渲染这块完全隔离了。同一份数据,若想改变暂展示的方式,只需要改配置即可,完全做到了模块化、可插拔化~
本篇讲解了几乎所有的解析器(除了ContentNegotiatingViewResolver、ResourceBundleViewResolver没讲,因为我认为可以把它单独抽取抽来讲解,因为还挺好玩的),然后我认为更重要的是了解View视图、渲染方面,而本文就做了一个非常好的铺垫作用~~~