第九篇:基于 XML 配置 SpringMVC

第九篇:基于 XML 配置 SpringMVC

总览

基于 xml 的配置的时候,mvc 命名空间下的标签通常对应着一个 org.springframework.web.servlet.config 包下的 Parser 类。

常见的 Parser

基于 Java 的 SpringMVC 配置同样也可以配置这些选项,请查看《SpringMVC- 第十篇:基于注解配置 SpringMVC》

AnnotationDrivenBeanDefinitionParser - 处理 <mvc:annotation-driven>

在读取 SpringMVC 的 xml 配置中的 <annotation-driven/> 标签的时候,由 AnnotationDrivenBeanDefinitionParser 处理,这个解析类引入了很多 bean,查看此类的注释即可知道,包括

HttpMessageConverter 、Validator

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

ConversionServiceExposingInterceptor

这里我们专注于 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;
}

调试

可以看到, 代理 ConversionServiceExposingInterceptorMappedInterceptor 已经注册进 IOC 容器中了,因为 includePatternsexcludePatterns 皆为空,所以此 MappedInterceptor 默认匹配所有控制器方法。

使用注解配置 SpringMVC 的时候,使用的是 WebMvcConfigurationSupport,也会添加 ConversionServiceExposingInterceptor,这个我们后面再了解

DefaultRequestToViewNameTranslator

    // 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);
}

那么在哪里查看呢?我猜的

HandlerExceptionResolver

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));

最终结果:

DefaultServletHandlerBeanDefinitionParser - 处理 <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

ResourcesBeanDefinitionParser - <mvc:resources>

DefaultServletHandlerBeanDefinitionParser 一样,也是通过注册 SimpleUrlHandlerMapping 类型的控制器方法映射即处理器映射将静态资源请求映射到 ResourceHttpRequestHandler 来处理,当同时注册的时候,处理 <mvc:resources> 的请求的 SimpleUrlHandlerMapping 排在处理 <mvc:default-servlet-handler/> 的前面,<mvc:default-servlet-handler/> 是默认的排在最后的静态资源处理器。

ViewControllerBeanDefinitionParser - <view-controller><redirect-view-controller><status-controller>

基本上也是通过注册 SimpleUrlHandlerMapping 来生效的

InterceptorsBeanDefinitionParser - 处理 <mvc:interceptors>

On this page
第九篇:基于 XML 配置 SpringMVC
  • 总览
  • AnnotationDrivenBeanDefinitionParser - 处理
    1. HttpMessageConverter 、Validator
    2. ConversionServiceExposingInterceptor
    3. DefaultRequestToViewNameTranslator
    4. HandlerExceptionResolver
  • DefaultServletHandlerBeanDefinitionParser - 处理
  • ResourcesBeanDefinitionParser - ViewControllerBeanDefinitionParser - ,, InterceptorsBeanDefinitionParser - 处理