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=================