zoukankan      html  css  js  c++  java
  • SpingMVC之<mvc:annotation-driven/>标签

    对于SpringMVC的探索已经接近尾声,本篇笔记主要记录下SpringMVC为我们提供的一个神奇标签 <mvc:annotation-driven/>,这个标签会帮我们注入很多关键而实用的bean,但是用它也得小心跟自己手动注入的bean重复,会造成不必要的麻烦。所以今天来了解下这个标签。
    本篇笔记主要分析SpringMVC 5.1.1 这个版本。


    为了弄清楚这些问题,我们先找到它的解析类,所有的自定义命名空间(像mvc,context等)下的标签解析都是由BeanDefinitionParser 接口的实现类来完成的。我们今天研究的是<mvc:annotation-driven/>标签,所以我们找到对应的实现类是org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser


    1 简单了解下功能

    AnnotationDrivenBeanDefinitionParser,为 <annotation-driven /> MVC名称空间元素提供配置。

    1.1 注册以下HandlerMappings (映射器们):

    • RequestMappingHandlerMapping 的排序为0,用于将请求映射到带@RequestMapping注释的控制器方法。
    • BeanNameUrlHandlerMapping 在排序为2,以将URL路径映射到控制器bean名称。

    1.2 注册以下HandlerAdapters (适配器们):

    • RequestMappingHandlerAdapter 用于使用带@RequestMapping注解的控制器方法处理请求。
    • HttpRequestHandlerAdapter 用于使用HttpRequestHandlers处理请求。
    • SimpleControllerHandlerAdapter 用于使用基于接口的控制器处理请求。

    1.3 注册以下HandlerExceptionResolvers (异常处理解析器们):

    • ExceptionHandlerExceptionResolver,用于通过 org.springframework.web.bind.annotation.ExceptionHandler 方法处理异常。
    • ResponseStatusExceptionResolver 用于使用 org.springframework.web.bind.annotation.ResponseStatus 注释的异常。
    • DefaultHandlerExceptionResolver 用于解析已知的Spring异常类型

    1.4 其他

    注册 org.springframework.util.AntPathMatcherorg.springframework.web.util.UrlPathHelper 以供 RequestMappingHandlerMappingViewControllersHandlerMappingHandlerMapping 服务资源是使用。

    对于JSR-303实现,会检测 javax.validation.Validator 路径是否有效,有效则会帮我们创建对应的实现类并注入。

    最后帮我们检测一些列 HttpMessageConverter 的实现类们,这些主要是用作直接对请求体里面解析出来的数据进行转换。俗称 http 消息转换器,与参数转换器不一样。
    在 SpringMVC 5.1.1 中有以下几个检测:

    检测路径注入消息转换器对应请求类型
    com.rometools.rome.feed.WireFeed RssChannelHttpMessageConverter application/atom+xml
    javax.xml.bind.Binder Jaxb2RootElementHttpMessageConverter application/xml
    com.fasterxml.jackson.databind.ObjectMapper & com.fasterxml.jackson.core.JsonGenerator MappingJackson2HttpMessageConverter application/json
    com.fasterxml.jackson.dataformat.xml.XmlMapper MappingJackson2XmlHttpMessageConverter application/xml
    com.fasterxml.jackson.dataformat.smile.SmileFactory MappingJackson2SmileHttpMessageConverter application/x-jackson-smile
    com.fasterxml.jackson.dataformat.cbor.CBORFactory MappingJackson2CborHttpMessageConverter application/cbor
    com.google.gson.Gson GsonHttpMessageConverter application/json

    除了会帮我们注入以上检测有效的 http 消息转换器外,还会帮我们注入SpringMVC自带的几个 http 消息转换器,上面检测的转换器是由上到下顺序加入的,也就是说解析的时候回根据 ContentType 从上到下找合适的。

    2 源码简介

    该标签的解释是在 org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser 类的 parse(..) 方法中

    2.1 HandlerMappings 注册

    2.1.1 RequestMappingHandlerMapping 映射器的注册
    //生成RequestMappingHandlerMapping组件对象
    RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
    handlerMappingDef.setSource(source);
    handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    //优先级设置为最高
    handlerMappingDef.getPropertyValues().add("order", 0);
    //添加contentNegotiationManager属性,处理media type
    handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
    
    if (element.hasAttribute("enable-matrix-variables")) {
        Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
        handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
    }
    
    //配置路径匹配解析器等属性
    configurePathMatchingProperties(handlerMappingDef, element, context);
    readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);
    
    //将RequestMappingHandlerMapping注册为bean对象放置bean工厂中
    RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, context, source);
    handlerMappingDef.getPropertyValues().add("corsConfigurations", corsRef);
    2.1.1 BeanNameUrlHandlerMapping 映射器注册就比较随意
    // Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
    MvcNamespaceUtils.registerDefaultComponents(context, source);

    放在这里跟 HttpRequestHandlerAdapterSimpleControllerHandlerAdapterHandlerMappingIntrospector 一起注册的。

    2.2 HandlerAdapters 注册

    2.2.1 RequestMappingHandlerAdapter 适配器的注册
    //从该标签的 "conversion-service" 属性中获取注入到容器里面的参数转换服务器, 没有则重新创建
    RuntimeBeanReference conversionService = getConversionService(element, source, context);
    //从该标签的 "validator" 属性中获取注入到容器里面的参数验证服务, 没有则创建
    RuntimeBeanReference validator = getValidator(element, source, context);
    //从该标签的 "message-codes-resolver" 属性中获取错误码解析器, 没有则不创建
    RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);
    
    //创建 `WebDataBinder` 初始化使用到的记录器, 并将上面的参数转换和验证相关绑定在其上面
    RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
    bindingDef.setSource(source);
    bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    bindingDef.getPropertyValues().add("conversionService", conversionService);
    bindingDef.getPropertyValues().add("validator", validator);
    bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);
    
    //从该标签的 `message-converters` 属性上获取记录请求体转换器的集合,没有则创建默认的
    ManagedList<?> messageConverters = getMessageConverters(element, source, context);
    //从该标签的 `argument-resolvers` 属性上获取记录自定义参数转换器的集合
    ManagedList<?> argumentResolvers = getArgumentResolvers(element, context);
    //从该标签的 `return-value-handlers` 属性上获取记录自定义返回值转换器的集合
    ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, context);
    //从该标签的 "async-support" 子节点解析,获取其中的 "default-timeout" 属性,作为异步处理超时时间,默认null
    String asyncTimeout = getAsyncTimeout(element);
    //从该标签的 "async-support" 子节点解析,获取其中的 "task-executor" 属性,异步任务线程池
    RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);
    //从该标签的 "async-support" 子节点解析,获取其中的 "callable-interceptors"节点,异步处理callable类型拦截器
    ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, context);
    ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, context);
    
    //生成RequestMappingHandlerAdapter组件对象,并将上面获取的相关绑定在该映射器上
    RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
    handlerAdapterDef.setSource(source);
    handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
    handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
    handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
    addRequestBodyAdvice(handlerAdapterDef);
    addResponseBodyAdvice(handlerAdapterDef);
    
    if (element.hasAttribute("ignore-default-model-on-redirect")) {
        Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));
        handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
    }
    if (argumentResolvers != null) {
        handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
    }
    if (returnValueHandlers != null) {
        handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
    }
    if (asyncTimeout != null) {
        handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
    }
    if (asyncExecutor != null) {
        handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
    }
    
    handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
    handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
    readerContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME , handlerAdapterDef);
    2.2.2 HttpRequestHandlerAdapter 和 SimpleControllerHandlerAdapter 适配器的注册就比较随意
    registerHttpRequestHandlerAdapter(parserContext, source);
    registerSimpleControllerHandlerAdapter(parserContext, source);

    2.3 HandlerExceptionResolvers 组件注册

    默认采用 ExceptionHandlerExceptionResolver(处理 @ExceptionHandler 方法注解)、ResponseStatusExceptionResolver(处理 @ResponseStatus 类型、方法注解)、DefaultHandlerExceptionResolver(处理普通的 Spring 异常) 作为异常处理类

    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);
    
    RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
    statusExceptionResolver.setSource(source);
    statusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    statusExceptionResolver.getPropertyValues().add("order", 1);
    String statusExResolverName = readerContext.registerWithGeneratedName(statusExceptionResolver);
    
    RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
    defaultExceptionResolver.setSource(source);
    defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    defaultExceptionResolver.getPropertyValues().add("order", 2);
    String defaultExResolverName = readerContext.registerWithGeneratedName(defaultExceptionResolver);

    2.4 其他的根据检测有效路径注册

    static {
        ClassLoader classLoader = AnnotationDrivenBeanDefinitionParser.class.getClassLoader();
        javaxValidationPresent = ClassUtils.isPresent("javax.validation.Validator", classLoader);
        romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
        jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
        jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
        ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
        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);
    }

    后面会根据上面 Class 路径的检测结果注入相关的验证器和转换器。

    下一章介绍  <mvc:annotation-driven>新增标签

    转 : https://www.jianshu.com/p/fc3bc70b9ed3

  • 相关阅读:
    博客搬家申请CSDN至博客园
    关于java8新特性的一些总结
    mysql数据库插入时更改数据
    2017总结
    java中单链表的操作
    工厂设计模式及理解
    筛选法求素数
    tcp粘包,断包问题及处理
    理解B树,B+树
    jvm中锁的优化
  • 原文地址:https://www.cnblogs.com/fps2tao/p/12843768.html
Copyright © 2011-2022 走看看