基于 xml 的配置的时候,mvc 命名空间下的标签通常对应着一个 org.springframework.web.servlet.config
包下的 Parser
类。
常见的 Parser
类
AnnotationDrivenBeanDefinitionParser
处理 <mvc:annotation-driven>
CorsBeanDefinitionParser
处理 <mvc:cors>
DefaultServletHandlerBeanDefinitionParser
处理 <mvc:default-servlet-handler/>
FreeMarkerConfigurerBeanDefinitionParser
处理 <mvc:freemarker-configurer>
GroovyMarkupConfigurerBeanDefinitionParser
处理 <mvc:groovy-configurer>
InterceptorsBeanDefinitionParser
处理 <mvc:interceptors>
ResourcesBeanDefinitionParser
处理 <mvc:resources>
ScriptTemplateConfigurerBeanDefinitionParser
处理 <mvc:script-template-configurer>
TilesConfigurerBeanDefinitionParser
处理 <mvc:tiles-configurer>
ViewControllerBeanDefinitionParser
处理 <view-controller>
处理 <redirect-view-controller>
处理 <status-controller>
ViewResolversBeanDefinitionParser
处理 <mvc:view-resolvers>
基于 Java 的 SpringMVC 配置同样也可以配置这些选项,请查看《SpringMVC- 第十篇:基于注解配置 SpringMVC》
<mvc:annotation-driven>
在读取 SpringMVC 的 xml 配置中的 <annotation-driven/>
标签的时候,由 AnnotationDrivenBeanDefinitionParser
处理,这个解析类引入了很多 bean,查看此类的注释即可知道,包括
HandlerMappings
RequestMappingHandlerMapping
BeanNameUrlHandlerMapping
HandlerAdapters
RequestMappingHandlerAdapter
HttpRequestHandlerAdapter
SimpleControllerHandlerAdapter
HandlerExceptionResolvers
ExceptionHandlerExceptionResolver
ResponseStatusExceptionResolver
DefaultHandlerExceptionResolver
org.springframework.util.AntPathMatcher
springframework.web.util.UrlPathHelper
ContentNegotiationManager
DefaultFormattingConversionService
org.springframework.validation.beanvalidation.LocalValidatorFactoryBean
,根据 classpath 中是否由相关第三方类来决定是否添加
HttpMessageConverter
,根据 classpath 中是否由相关第三方类来决定是否添加
在 AnnotationDrivenBeanDefinitionParser
的静态代码块中,根据 classpath 中是否存在相关类来判断是否引入第三方 jar 包,然后决定是否添加加载响应的转换器(HttpMessageConverter)、验证器(Validator)等。
static {
ClassLoader classLoader = AnnotationDrivenBeanDefinitionParser.class.getClassLoader();
// Bean Validation 校验器
javaxValidationPresent = ClassUtils.isPresent("javax.validation.Validator", classLoader);
romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
// 检查是否导入 jackson-databind
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
// 检查是否导入 jackson-dataformat-xml
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
}
跟 JSON 和 XML 解析相关的 HttpMessageConverter,也是在这里添加,具体看代码即可,这一点,在《SpringMVC-ContentNegotiation 内容协商》中也有所提及。
根据 Bean Validation 相关的 Validator,在这里添加,具体代码看《SpringMVC 整合 JavaBeanValidation 及拓展》中,的自定义全局 Validator
这里我们专注于 parse
方法,看 ConversionServiceExposingInterceptor
相关的操作代码。
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext context) {
Object source = context.extractSource(element);
XmlReaderContext readerContext = context.getReaderContext();
......
// 默认为 DefaultFormattingConversionService
RuntimeBeanReference conversionService = getConversionService(element, source, context);
......
// 创建 ConversionServiceExposingInterceptor BeanDefinition 留作 构建 MappedInterceptor 使用
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
csInterceptorDef.setSource(source);
// 对应 ConversionServiceExposingInterceptor 的构造函数 ConversionServiceExposingInterceptor(ConversionService conversionService)
csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
// 创建 MappedInterceptor BeanDefinition 这就是我们最终注册的 MappedInterceptor
RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
mappedInterceptorDef.setSource(source);
mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 对应 MappedInterceptor 的构造函数 MappedInterceptor(@Nullable String[] includePatterns, HandlerInterceptor interceptor)
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
String mappedInterceptorName = readerContext.registerWithGeneratedName(mappedInterceptorDef);
......
context.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, mappedInterceptorName));
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(context, source);
context.popAndRegisterContainingComponent();
return null;
}
调试
可以看到, 代理 ConversionServiceExposingInterceptor
的 MappedInterceptor
已经注册进 IOC 容器中了,因为 includePatterns
和 excludePatterns
皆为空,所以此 MappedInterceptor
默认匹配所有控制器方法。
使用注解配置 SpringMVC 的时候,使用的是
WebMvcConfigurationSupport
,也会添加ConversionServiceExposingInterceptor
,这个我们后面再了解
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(context, source);
MvcNamespaceUtils.registerDefaultComponents(context, source);
添加一些默认的策略 bean,其中就包括 DefaultRequestToViewNameTranslator
public static void registerDefaultComponents(ParserContext context, @Nullable Object source) {
registerBeanNameUrlHandlerMapping(context, source);
registerHttpRequestHandlerAdapter(context, source);
registerSimpleControllerHandlerAdapter(context, source);
registerHandlerMappingIntrospector(context, source);
registerLocaleResolver(context, source);
registerThemeResolver(context, source);
registerViewNameTranslator(context, source);
registerFlashMapManager(context, source);
}
那么在哪里查看呢?我猜的
parse
方法中,
// ExceptionHandlerExceptionResolver
RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
methodExceptionResolver.setSource(source);
methodExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
methodExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
methodExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
methodExceptionResolver.getPropertyValues().add("order", 0);
addResponseBodyAdvice(methodExceptionResolver);
if (argumentResolvers != null) {
methodExceptionResolver.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}
if (returnValueHandlers != null) {
methodExceptionResolver.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
}
String methodExResolverName = readerContext.registerWithGeneratedName(methodExceptionResolver);
// ResponseStatusExceptionResolver
RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
statusExceptionResolver.setSource(source);
statusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
statusExceptionResolver.getPropertyValues().add("order", 1);
String statusExResolverName = readerContext.registerWithGeneratedName(statusExceptionResolver);
// DefaultHandlerExceptionResolver
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
defaultExceptionResolver.setSource(source);
defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
defaultExceptionResolver.getPropertyValues().add("order", 2);
String defaultExResolverName = readerContext.registerWithGeneratedName(defaultExceptionResolver);
context.registerComponent(new BeanComponentDefinition(methodExceptionResolver, methodExResolverName));
context.registerComponent(new BeanComponentDefinition(statusExceptionResolver, statusExResolverName));
context.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExResolverName));
最终结果:
ExceptionHandlerExceptionResolver
ResponseStatusExceptionResolver
DefaultHandlerExceptionResolver
<mvc:default-servlet-handler/>
class DefaultServletHandlerBeanDefinitionParser implements BeanDefinitionParser {
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
String defaultServletName = element.getAttribute("default-servlet-name");
RootBeanDefinition defaultServletHandlerDef = new RootBeanDefinition(DefaultServletHttpRequestHandler.class);
defaultServletHandlerDef.setSource(source);
defaultServletHandlerDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
if (StringUtils.hasText(defaultServletName)) {
defaultServletHandlerDef.getPropertyValues().add("defaultServletName", defaultServletName);
}
String defaultServletHandlerName = parserContext.getReaderContext().generateBeanName(defaultServletHandlerDef);
parserContext.getRegistry().registerBeanDefinition(defaultServletHandlerName, defaultServletHandlerDef);
parserContext.registerComponent(new BeanComponentDefinition(defaultServletHandlerDef, defaultServletHandlerName));
Map<String, String> urlMap = new ManagedMap<>();
urlMap.put("/**", defaultServletHandlerName);
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(SimpleUrlHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerMappingDef.getPropertyValues().add("urlMap", urlMap);
String handlerMappingBeanName = parserContext.getReaderContext().generateBeanName(handlerMappingDef);
parserContext.getRegistry().registerBeanDefinition(handlerMappingBeanName, handlerMappingDef);
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, handlerMappingBeanName));
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
return null;
}
}
本质上,是注册一个 SimpleUrlHandlerMapping
类型的控制器方法映射即处理器映射,来对请求静态资源的请求映射到 DefaultServletHttpRequestHandler
进行单独的处理,在 DispatcherServlet#doDispatch
中通过调用 DispatcherServlet#getHandler
来调用。
DispatcherServlet#doDispatch
这种写法,表示DispatcherServlet
类中的doDispatch
方法,这是 Java 的命名规范
其中 MvcNamespaceUtils.registerDefaultComponents
注册了很多默认的 bean
public static void registerDefaultComponents(ParserContext context, @Nullable Object source) {
registerBeanNameUrlHandlerMapping(context, source);
registerHttpRequestHandlerAdapter(context, source);
registerSimpleControllerHandlerAdapter(context, source);
registerHandlerMappingIntrospector(context, source);
registerLocaleResolver(context, source);
registerThemeResolver(context, source);
registerViewNameTranslator(context, source);
registerFlashMapManager(context, source);
}
其中就包括我们熟知的 registerViewNameTranslator
,默认注册的是 DefaultRequestToViewNameTranslator
<mvc:resources>
跟 DefaultServletHandlerBeanDefinitionParser
一样,也是通过注册 SimpleUrlHandlerMapping
类型的控制器方法映射即处理器映射将静态资源请求映射到 ResourceHttpRequestHandler
来处理,当同时注册的时候,处理 <mvc:resources>
的请求的 SimpleUrlHandlerMapping
排在处理 <mvc:default-servlet-handler/>
的前面,<mvc:default-servlet-handler/>
是默认的排在最后的静态资源处理器。
<view-controller>
,<redirect-view-controller>
,<status-controller>
基本上也是通过注册 SimpleUrlHandlerMapping
来生效的
<mvc:interceptors>