Spring MVC <mvc:view-resolvers> 标签解析
<mvc:view-resolvers>的解析类是 ViewResolversBeanDefinitionParser。关键部分的代码:
public BeanDefinition parse(Element element, ParserContext context) { Object source = context.extractSource(element); context.pushContainingComponent(new CompositeComponentDefinition(element.getTagName(), source)); ManagedList<Object> resolvers = new ManagedList<Object>(4); resolvers.setSource(context.extractSource(element)); String[] names = new String[] {"jsp", "tiles", "bean-name", "freemarker", "velocity", "groovy", "bean", "ref"}; for (Element resolverElement : DomUtils.getChildElementsByTagName(element, names)) { String name = resolverElement.getLocalName(); if ("bean".equals(name) || "ref".equals(name)) { resolvers.add(context.getDelegate().parsePropertySubElement(resolverElement, null)); continue; } RootBeanDefinition resolverBeanDef = null; if ("jsp".equals(name)) { resolverBeanDef = new RootBeanDefinition(InternalResourceViewResolver.class); resolverBeanDef.getPropertyValues().add("prefix", "/WEB-INF/"); resolverBeanDef.getPropertyValues().add("suffix", ".jsp"); addUrlBasedViewResolverProperties(resolverElement, resolverBeanDef); } else if ("tiles".equals(name)) { resolverBeanDef = new RootBeanDefinition(TilesViewResolver.class); addUrlBasedViewResolverProperties(resolverElement, resolverBeanDef); } else if ("freemarker".equals(name)) { resolverBeanDef = new RootBeanDefinition(FreeMarkerViewResolver.class); resolverBeanDef.getPropertyValues().add("suffix", ".ftl"); addUrlBasedViewResolverProperties(resolverElement, resolverBeanDef); } else if ("velocity".equals(name)) { resolverBeanDef = new RootBeanDefinition(VelocityViewResolver.class); resolverBeanDef.getPropertyValues().add("suffix", ".vm"); addUrlBasedViewResolverProperties(resolverElement, resolverBeanDef); } else if ("groovy".equals(name)) { resolverBeanDef = new RootBeanDefinition(GroovyMarkupViewResolver.class); resolverBeanDef.getPropertyValues().add("suffix", ".tpl"); addUrlBasedViewResolverProperties(resolverElement, resolverBeanDef); } else if ("bean-name".equals(name)) { resolverBeanDef = new RootBeanDefinition(BeanNameViewResolver.class); } else { // Should never happen throw new IllegalStateException("Unexpected element name: " + name); } resolverBeanDef.setSource(source); resolverBeanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); resolvers.add(resolverBeanDef); } String beanName = VIEW_RESOLVER_BEAN_NAME; RootBeanDefinition compositeResolverBeanDef = new RootBeanDefinition(ViewResolverComposite.class); compositeResolverBeanDef.setSource(source); compositeResolverBeanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); names = new String[] {"content-negotiation"}; List<Element> contentnNegotiationElements = DomUtils.getChildElementsByTagName(element, names); if (contentnNegotiationElements.isEmpty()) { compositeResolverBeanDef.getPropertyValues().add("viewResolvers", resolvers); } else if (contentnNegotiationElements.size() == 1) { BeanDefinition beanDef = createContentNegotiatingViewResolver(contentnNegotiationElements.get(0), context); beanDef.getPropertyValues().add("viewResolvers", resolvers); ManagedList<Object> list = new ManagedList<Object>(1); list.add(beanDef); compositeResolverBeanDef.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); compositeResolverBeanDef.getPropertyValues().add("viewResolvers", list); } else if (contentnNegotiationElements.size() > 1) { throw new IllegalArgumentException("Only one <content-negotiation> element is allowed."); } if (element.hasAttribute("order")) { compositeResolverBeanDef.getPropertyValues().add("order", element.getAttribute("order")); } context.getReaderContext().getRegistry().registerBeanDefinition(beanName, compositeResolverBeanDef); context.registerComponent(new BeanComponentDefinition(compositeResolverBeanDef, beanName)); context.popAndRegisterContainingComponent(); return null; }
如下的<mvc:view-resolvers>标签的配置:
<!-- 配置视图解析器--> <mvc:view-resolvers> <mvc:content-negotiation> <mvc:default-views> <bean class="org.springframework.web.servlet.view.json.MappingJackson2JsonView"> <property name="jsonpParameterNames"> <set> <value>jsonp</value> <value>callback</value> </set> </property> </bean> </mvc:default-views> </mvc:content-negotiation> <!-- JSP的视图解析器--> <mvc:jsp prefix="/WEB-INF/views/"/> </mvc:view-resolvers>
那么他是如何解析的呢?通过debug代码可以很清晰的了解到。
关键的流程如下,首先解析<mvc:view-resolvers>标签下的子标签,包括以下子标签:
String[] names = new String[] {"jsp", "tiles", "bean-name", "freemarker", "velocity", "groovy", "bean", "ref"};
对于这些子标签的处理,如下
if ("jsp".equals(name)) { resolverBeanDef = new RootBeanDefinition( InternalResourceViewResolver.class); resolverBeanDef.getPropertyValues().add("prefix", "/WEB-INF/"); resolverBeanDef.getPropertyValues().add("suffix", ".jsp"); addUrlBasedViewResolverProperties(resolverElement, resolverBeanDef); } else if ("tiles".equals(name)) { resolverBeanDef = new RootBeanDefinition(TilesViewResolver.class); addUrlBasedViewResolverProperties(resolverElement, resolverBeanDef); } else if ("freemarker".equals(name)) { resolverBeanDef = new RootBeanDefinition( FreeMarkerViewResolver.class); resolverBeanDef.getPropertyValues().add("suffix", ".ftl"); addUrlBasedViewResolverProperties(resolverElement, resolverBeanDef); } else if ("velocity".equals(name)) { resolverBeanDef = new RootBeanDefinition(VelocityViewResolver.class); resolverBeanDef.getPropertyValues().add("suffix", ".vm"); addUrlBasedViewResolverProperties(resolverElement, resolverBeanDef); } else if ("groovy".equals(name)) { resolverBeanDef = new RootBeanDefinition( GroovyMarkupViewResolver.class); resolverBeanDef.getPropertyValues().add("suffix", ".tpl"); addUrlBasedViewResolverProperties(resolverElement, resolverBeanDef); } else if ("bean-name".equals(name)) { resolverBeanDef = new RootBeanDefinition(BeanNameViewResolver.class); } else { // Should never happen throw new IllegalStateException("Unexpected element name: " + name); }
如何配置了这几种常见的视图解析器, 那么在这里就会实例化(注入)相应的视图解析器。
比较常见的视图解析器有:
InternalResourceViewResolver:UrlBasedViewResolver 的子类,通常用于查找 JSP(类 InternalResourceView)和 JSTL(类 JstlView,InternalResourceView 的子类)等视图。
TilesViewResolver:(一种动态模板视图解析器)
FreeMarkerViewResolver:(FreeMarker的视图解析器)
VelocityViewResolver:(Velocity的视图解析器)
GroovyMarkupViewResolver:(基于Groovy 的一种DSL风格的模板视图解析器)
BeanNameViewResolver:
然后是处理其他的子标签,只有content-negotiation子标签:
names = new String[] {"content-negotiation"};
处理content-negotiation子标签的代码如下,
String beanName = VIEW_RESOLVER_BEAN_NAME; RootBeanDefinition compositeResolverBeanDef = new RootBeanDefinition( ViewResolverComposite.class); compositeResolverBeanDef.setSource(source); compositeResolverBeanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); names = new String[] { "content-negotiation" }; List<Element> contentnNegotiationElements = DomUtils .getChildElementsByTagName(element, names); if (contentnNegotiationElements.isEmpty()) { compositeResolverBeanDef.getPropertyValues().add("viewResolvers", resolvers); } else if (contentnNegotiationElements.size() == 1) { BeanDefinition beanDef = createContentNegotiatingViewResolver( contentnNegotiationElements.get(0), context); beanDef.getPropertyValues().add("viewResolvers", resolvers); ManagedList<Object> list = new ManagedList<Object>(1); list.add(beanDef); compositeResolverBeanDef.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); compositeResolverBeanDef.getPropertyValues().add("viewResolvers", list); } else if (contentnNegotiationElements.size() > 1) { throw new IllegalArgumentException( "Only one <content-negotiation> element is allowed."); }
再来看他是怎么创建 ContentNegotiatingViewResolver 的 。
BeanDefinition beanDef = createContentNegotiatingViewResolver(contentnNegotiationElements.get(0), context);
方法createContentNegotiatingViewResolver
private BeanDefinition createContentNegotiatingViewResolver(Element resolverElement, ParserContext context) { RootBeanDefinition beanDef = new RootBeanDefinition(ContentNegotiatingViewResolver.class); beanDef.setSource(context.extractSource(resolverElement)); beanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); MutablePropertyValues values = beanDef.getPropertyValues(); //<content-negotiation>标签下的子标签 default-views List<Element> elements = DomUtils.getChildElementsByTagName(resolverElement, new String[] {"default-views"}); if (!elements.isEmpty()) { ManagedList<Object> list = new ManagedList<Object>(); for (Element element : DomUtils.getChildElementsByTagName(elements.get(0), "bean", "ref")) { list.add(context.getDelegate().parsePropertySubElement(element, null)); } values.add("defaultViews", list); } if (resolverElement.hasAttribute("use-not-acceptable")) { values.add("useNotAcceptableStatusCode", resolverElement.getAttribute("use-not-acceptable")); } String beanName = AnnotationDrivenBeanDefinitionParser.CONTENT_NEGOTIATION_MANAGER_BEAN_NAME; if (context.getRegistry().containsBeanDefinition(beanName)) { values.add("contentNegotiationManager", new RuntimeBeanReference(beanName)); } return beanDef; }
至此 ,<mvc:view-resolvers>标签就处理完了。
要注意到,如果没有配置content-negotiation子标签,那么只有ViewResolverComposite的实例对象包含了所有配置的视图解析器。
compositeResolverBeanDef.getPropertyValues().add("viewResolvers",resolvers);
否则,那么创建ContentNegotiatingViewResolver,并设置ContentNegotiatingViewResolver的viewResolvers的集合,同时设置 ViewResolverComposite 的viewResolvers集合,
但只包含一个ContentNegotiatingViewResolver的实例。
BeanDefinition beanDef = createContentNegotiatingViewResolver( contentnNegotiationElements.get(0), context); beanDef.getPropertyValues().add("viewResolvers", resolvers); ManagedList<Object> list = new ManagedList<Object>(1); list.add(beanDef); compositeResolverBeanDef.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); compositeResolverBeanDef.getPropertyValues().add("viewResolvers", list);
=================================END=================================