Spring MVC 初始化源码—<mvc:annotation-driven >配置标签的源码解析

Posted L-Java

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring MVC 初始化源码—<mvc:annotation-driven >配置标签的源码解析相关的知识,希望对你有一定的参考价值。

  基于最新Spring 5.x,详细介绍了Spring MVC 初始化流程的源码,主要包括<mvc:annotation-driven >配置标签的源码解析。

  我正在参与CSDN《新程序员》有奖征文,活动地址:https://marketing.csdn.net/p/52c37904f6e1b69dc392234fff425442

  此前我们讲过了DispatcherServlet与子容器的初始化以及MVC组件的初始化,我们说过如果IoC容器中存在给定类型的组件,那么DispatcherServlet#onRefresh()方法中就不会尝试初始化默认组件了。
  而在初始化IoC子容器的时候,将会解析Spring MVC的配置文件,在配置文件中就有可能初始化我们自定义的一些组件bean,也有可能会加载其他MVC配置标签,最常见的就是<mvc:annotation-driven >标签,实际上该标签就会向容器注册一些组件bean,并且还会进行额外的配置。
  <mvc:annotation-driven>标签为基于注解的SpringMVC驱动提供便捷配置,虽然Spring mvc5版本之后Spring MVC会默认加载大部分的默认组件,但是使用该标签仍然能够自动加载某些额外配置,比如自动发现并注册MappingJackson2HttpMessageConverter转换器以支持JSON数据交互的请求和响应。
  mvc系列标签同样属于扩展标签,根据我们此前学过的IoC容器初始化源码,扩展标签将会在IoC容器初始化的parseCustomElement方法中解析,mvc标签的解析器位于MvcNamespaceHandler类中:
在这里插入图片描述
  可以知道<mvc:annotation-driven >标签是由AnnotationDrivenBeanDefinitionParser这个解析器来解析的。

  下面的源码版本基于5.2.8.RELEASE。源码解析基于传统的SSM项目,不适用于Spring Boot项目。

Spring MVC源码 系列文章

Spring MVC 初始化源码(1)—ContextLoaderListener与父上下文容器的初始化

Spring MVC 初始化源码(2)—DispatcherServlet与子容器的初始化以及MVC组件的初始化【一万字】

Spring MVC 初始化源码(3)—<mvc:annotation-driven >配置标签的源码解析

Spring MVC 初始化源码(4)—@RequestMapping注解的源码解析

Spring MVC 请求执行流程的源码深度解析【两万字】

1 AnnotationDrivenBeanDefinitionParser静态块

  首先我们能看到AnnotationDrivenBeanDefinitionParser具有静态代码块,其内部的代码主要是查找是否具有某些依赖,用于后续parse方法中的一些自动配置,比如自动注册MappingJackson2HttpMessageConverter,用于支持application/json请求和响应。

//AnnotationDrivenBeanDefinitionParser的属性

private static final boolean javaxValidationPresent;

private static boolean romePresent;

private static final boolean jaxb2Present;

private static final boolean jackson2Present;

private static final boolean jackson2XmlPresent;

private static final boolean jackson2SmilePresent;

private static final boolean jackson2CborPresent;

private static final boolean gsonPresent;

static {
    ClassLoader classLoader = AnnotationDrivenBeanDefinitionParser.class.getClassLoader();
    //项目是否具有javax.validation.Validator接口,即validation-api的依赖
    javaxValidationPresent = ClassUtils.isPresent("javax.validation.Validator", classLoader);
    romePresent = ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", classLoader);
    //项目是否具有javax.xml.bind.Binder接口,这位于rt.jar核心包中
    jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
    //项目是否具有jackson-databind依赖,主要用于支持application/json请求和响应,实现JSON的序列化和反序列化
    jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
            ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
    //项目是否具有jackson-dataformat-xml依赖,主要用于支持XML的请求和响应,实现XML的序列化和反序列化
    jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
    //项目是否具有jackson-dataformat-smile依赖
    jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
    //项目是否具有jackson-dataformat-cbor依赖
    jackson2CborPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.cbor.CBORFactory", classLoader);
    //项目是否具有gson依赖
    gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
}

2 parse执行解析

  AnnotationDrivenBeanDefinitionParser的方法就是解析<mvc:annotation-driven/>标签的核心方法。
  在该方法中,将会创建并配置多个mvc组件的bean定义到Spring容器中,这样的话,在后续的DispatcherServlet#onRefresh()方法中就不会初始化对应类型的默认组件了。
  从源码中可以看到,则将会注册RequestMappingHandlerMapping、RequestMappingHandlerAdapter、CompositeUriComponentsContributorFactoryBean、MappedInterceptor、ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver、 BeanNameUrlHandlerMapping、HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、HandlerMappingIntrospector11个组件的bean定义到容器中。
  如果只存在该标签而没有其他子标签配置,则还会注册ContentNegotiationManagerFactoryBean、FormattingConversionServiceFactoryBean、OptionalValidatorFactoryBean、LinkedHashMap(用于CORS配置)4个bean定义到容器中。
  该方法的源码很多,我们拆分开来讲解。最重要最常见的就是RequestMappingHandlerMapping和RequestMappingHandlerAdapter这两个bean定义了,因为它们用于支持基于注解的Spring MVC处理。

/**
 * 自动配置的RequestMappingHandlerMapping的beanName
 */
public static final String HANDLER_MAPPING_BEAN_NAME = RequestMappingHandlerMapping.class.getName();

/**
 * 自动配置的RequestMappingHandlerAdapter的beanName
 */
public static final String HANDLER_ADAPTER_BEAN_NAME = RequestMappingHandlerAdapter.class.getName();

public static final String CONTENT_NEGOTIATION_MANAGER_BEAN_NAME = "mvcContentNegotiationManager";

/**
 * AnnotationDrivenBeanDefinitionParser的方法
 * 解析<mvc:annotation-driven/>标签
 *
 * @param element 当前<mvc:annotation-driven/>标签元素
 * @param context 解析上下文,从中可以获取各种配置
 * @return
 */
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext context) {
    Object source = context.extractSource(element);
    XmlReaderContext readerContext = context.getReaderContext();
    /*
     * 为<mvc:annotation-driven/>标签创建一个CompositeComponentDefinition类型的bean定义组件
     * 该类型的bean定义,内部可以包含多个bean定义,因此又称为复合bean定义
     */
    CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
    //存入解析上下文内部的containingComponents集合中,入栈顶
    context.pushContainingComponent(compDefinition);

    /*内容协商处理,默认会注册ContentNegotiationManagerFactoryBean的bean定义*/
    RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, context);

    /*
     * 1 向容器注册RequestMappingHandlerMapping的bean定义
     * 该类可以处理基于注解的映射
     */
    RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
    handlerMappingDef.setSource(source);
    handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    //添加order属性,使其位于handlerMappings集合链首位
    handlerMappingDef.getPropertyValues().add("order", 0);
    handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);

    /*
     * 路径矩阵变量的配置
     */
    //如果具有enable-matrix-variables属性,即是否支持矩阵变量的属性
    if (element.hasAttribute("enable-matrix-variables")) {
        //获取该属性的值
        Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
        //添加removeSemicolonContent属性
        handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
    }
    /*
     * 一系列路径匹配参数的配置
     */
    configurePathMatchingProperties(handlerMappingDef, element, context);
    //当前的RequestMappingHandlerMapping的bean定义注册到子容器中
    //注册此RequestMappingHandlerMapping的bean定义到容器中,名为"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"
    readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME, handlerMappingDef);

    /*
     * cors跨域解决的配置
     * 默认会注册LinkedHashMap的bean定义
     */
    RuntimeBeanReference corsRef = MvcNamespaceUtils.registerCorsConfigurations(null, context, source);
    handlerMappingDef.getPropertyValues().add("corsConfigurations", corsRef);

    /*
     * 2 创建ConfigurableWebBindingInitializer的bean定义
     * 该类用于在Spring应用程序上下文中基于声明式的配置WebDataBinder,允许与多个控制器/处理程序一起重用预配置的初始化程序。
     */

    /*
     * 解析"conversion-service"属性获取转换服务,
     * 默认会注册FormattingConversionServiceFactoryBean的bean定义,这是一个FactoryBean,实际是DefaultFormattingConversionService类型
     */
    RuntimeBeanReference conversionService = getConversionService(element, source, context);
    /*
     * 解析"validator"属性获取校验器
     * 默认会注册OptionalValidatorFactoryBean的bean定义,内部使用一种provider实现,通常是hibernate-validator依赖中的HibernateValidator
     */
    RuntimeBeanReference validator = getValidator(element, source, context);
    /*解析"message-codes-resolver"属性获取响应码解析器,默认是null*/
    RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);

    /*新建ConfigurableWebBindingInitializer的bean定义*/
    RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
    bindingDef.setSource(source);
    bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    //设置conversionService属性
    bindingDef.getPropertyValues().add("conversionService", conversionService);
    //设置validator属性
    bindingDef.getPropertyValues().add("validator", validator);
    //设置messageCodesResolver属性
    bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);
    /*
     * 解析<mvc:message-converters/>子标签,获取并配置消息转换器HttpMessageConverter
     * 主要用于处理基于ContentType的请求参数转换、封装
     *
     * 默认情况下会注册一系列的HttpMessageConverter
     * 在这其中,就会尝试自动配置配置MappingJackson2HttpMessageConverter转换器(前提是项目具有jackson-databind依赖)
     */
    ManagedList<?> messageConverters = getMessageConverters(element, source, context);
    /*
     * 解析<mvc:argument-resolvers/>子标签,获取并配置方法参数解析器HandlerMethodArgumentResolver
     * 用于将给定请求的上下文中的数据解析为HandlerMethod方法的参数,相比于HttpMessageConverter,它更加上层
     * 即它负责处理Handler方法里的所有入参:包括自动封装、自动赋值、校验等等,
     * 具体的实现交给不同的子类来做,比如基于Name、基于ContentType
     *
     * 默认情况下为null
     */
    ManagedList<?> argumentResolvers = getArgumentResolvers(element, context);
    /*
     * 解析<mvc:return-value-handlers/>子标签,获取并配置返回值处理器HandlerMethodReturnValueHandler
     * 返回值处理器用于处理从处理程序方法调用返回的值
     *
     * 默认情况下为null
     */
    ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, context);
    /*
     * 解析<mvc:async-support/>子标签,获取异步处理的超时时间
     * 就是解析此<mvc:annotation-driven/>标签内部的<mvc:async-support/>子标签的default-timeout属性
     *
     * 默认情况下为null
     */
    String asyncTimeout = getAsyncTimeout(element);
    /*
     * 解析<mvc:async-support/>子标签,获取异步处理的线程池
     * 就是解析此<mvc:annotation-driven/>标签内部的<mvc:async-support/>子标签的task-executor属性
     *
     * 默认情况下为null
     */
    RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);
    /*
     * 解析<mvc:async-support/>子标签,获取异步处理CallableProcessingInterceptor类型拦截器
     * 就是解析此<mvc:annotation-driven/>标签内部的<mvc:async-support/>子标签的<mvc:callable-interceptors/>子标签
     *
     * 默认情况下为空集合
     */
    ManagedList<?> callableInterceptors = getInterceptors(element, source, context, "callable-interceptors");
    /*
     * 解析<mvc:async-support/>子标签,获取异步处理DeferredResultProcessingInterceptor类型拦截器
     * 就是解析此<mvc:annotation-driven/>标签内部的<mvc:async-support/>子标签的<mvc:deferred-result-interceptors>子标签
     *
     * 默认情况下为空集合
     */
    ManagedList<?> deferredResultInterceptors = getInterceptors(element, source, context, "deferred-result-interceptors");

    /*
     * 3 向容器注册RequestMappingHandlerAdapter的bean定义
     * 该类可以帮助调用找到的Handler
     */
    RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
    handlerAdapterDef.setSource(source);
    handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    //内容协商处理
    handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
    //WebDataBinder的初始化器
    handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
    //消息转换器
    handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
    /*
     * 如果具有jackson-databind依赖
     * 那么新建一个JsonViewRequestBodyAdvice、JsonViewResponseBodyAdvice类型的bean定义
     * 作为RequestMappingHandlerAdapter的requestBodyAdvice、responseBodyAdvice属性
     *
     * 用于支持具有Spring MVC的 @RequestMapping 或者 @ExceptionHandler的 方法上的@JsonView注解的解析
     */
    addRequestBodyAdvice(handlerAdapterDef);
    addResponseBodyAdvice(handlerAdapterDef);

    /*
     * 解析"ignore-default-model-on-redirect"属性,设置给RequestMappingHandlerAdapter的ignoreDefaultModelOnRedirect属性
     * 该属性用于确定在重定向中是否永远不会使用默认model属性,即使控制器方法中未声明RedirectAttributes参数也是如此。
     * 将其设置为 false 意味着如果控制器方法不声明RedirectAttributes参数,则默认model可能在重定向中使用,默认值为false。
     */
    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);
    //注册此RequestMappingHandlerAdapter的bean定义到容器中,名为"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"
    readerContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME, handlerAdapterDef);

    /*
     * 4 向容器注册CompositeUriComponentsContributorFactoryBean的bean定义
     *
     * 该类是一个FactoryBean,可以获取CompositeUriComponentsContributor
     * 用于辅助MvcUriComponentsBuilder构建UriComponentsBuilder
     */
    RootBeanDefinition uriContributorDef =
            new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
    uriContributorDef.setSource(source);
    uriContributorDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
    uriContributorDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
    String uriContributorName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
    readerContext.getRegistry().registerBeanDefinition(uriContributorName, uriContributorDef);

    /*
     * 5 新建一个ConversionServiceExposingInterceptor的bean定义
     *
     * 这是一个Spring MVC的拦截器HandlerInterceptor的实现
     * 该拦截器用于在preHandle方法中将已配置的ConversionService放置在请求request的属性中(request.setAttribute)
     * 以便在请求处理期间可用。请求属性名称是"org.springframework.core.convert.ConversionService"
     */
    RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
    csInterceptorDef.setSource(source);
    //加入转换服务
    csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
    /*
     * 5 向容器注册一个MappedInterceptor的bean定义
     *
     * 这是一个Spring MVC的拦截器HandlerInterceptor的实现
     * 该拦截器内部还包含一个拦截器,并利用内部的拦截器真正的执行功能
     * MappedInterceptor它仅仅提供了匹配的功能以测试拦截器是否适用于给定的请求路径。
     */
    RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
    mappedInterceptorDef.setSource(source);
    mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    //设置构造器参数
    //第一个参数includePatterns为null
    mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
    //第二个参数HandlerInterceptor为上面定义的ConversionServiceExposingInterceptor
    mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
    /*将MappedInterceptor注册到IoC容器中*/
    String mappedInterceptorName = readerContext.registerWithGeneratedName(mappedInterceptorDef);

    /*
     * 6 向容器注册一个ExceptionHandlerExceptionResolver的bean定义
     *
     * 这是一个Spring MVC的异常处理器HandlerExceptionResolver的实现
     * 该异常处理器可以解析基于@ExceptionHandler注解的异常处理方法
     */
    RootBeanDefinition methodExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
    methodExceptionResolver.setSource(source);
    methodExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    //内容协商处理
    methodExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
    //消息转换器
    methodExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
    //期望排在handlerExceptionResolvers异常处理器集合链的首位
    methodExceptionResolver.getPropertyValues().add("order", 0);
    /*
     * 如果具有jackson-databind依赖
     * 那么新建一个JsonViewResponseBodyAdvice类型的bean定义
     * 作为RequestMappingHandlerAdapter的responseBodyAdvice属性
     *
     * 用于支持具有Spring MVC的 @RequestMapping 或者 @ExceptionHandler的 方法上的@JsonView注解的解析
     *
     * 这些通知应用于在使用@ResponseBody注释的控制器方法执行或返回ReturnEntity之后,
     * 但在将主体使用选定的HttpMessageConverter写入响应之前,添加一个或多个要调用的组件。
     */
    addResponseBodyAdvice(methodExceptionResolver);
    if (argumentResolvers != null) {
        methodExceptionResolver.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
    }
    //返回值处理器
    if (returnValueHandlers != null) {
        methodExceptionResolver.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
    }
    /*将ExceptionHandlerExceptionResolver注册到IoC容器中*/
    String methodExResolverName = readerContext.registerWithGeneratedName(methodExceptionResolver);

    /*
     * 7 向容器注册一个ResponseStatusExceptionResolver的bean定义
     *
     * 这是一个Spring MVC的异常处理器HandlerExceptionResolver的实现
     * 该异常处理器可以解析使用@ResponseStatus注解表示的异常,将注解中的值映射到HTTP状态代码并返回给客户端。
     */
    RootBeanDefinition statusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
    statusExceptionResolver.setSource(source);
    statusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    //期望排在handlerExceptionResolvers异常处理器集合链的第二位
    statusExceptionResolver.getPropertyValues().add("order", 1);
    /*将ResponseStatusExceptionResolver注册到IoC容器中*/
    String statusExResolverName = readerContext.registerWithGeneratedName(statusExceptionResolver);

    /*
     * 7 向容器注册一个DefaultHandlerExceptionResolver的bean定义
     *
     * 这是一个Spring MVC的异常处理器HandlerExceptionResolver的实现
     * 该异常处理器可以解析Spring MVC 引发的异常,将它们映射到HTTP状态代码并返回给客户端。
     */
    RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
    defaultExceptionResolver.setSource(source);
    defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    //期望排在handlerExceptionResolvers异常处理器集合链的第三位
    defaultExceptionResolver.getPropertyValues().add("order", 2);
    /*将ResponseStatusExceptionResolver注册到IoC容器中*/
    String defaultExResolverName = readerContext.registerWithGeneratedName(defaultExceptionResolver);

    context.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME)Spring MVC 初始化源码—@RequestMapping注解的源码解析

Spring MVC源码——Root WebApplicationContext

Spring6源码・MVC请求处理流程源码解析

Spring MVC 初始化源码—DispatcherServlet与子容器的初始化以及MVC组件的初始化一万字

一文彻底解密Spring 源码之Spring MVC

一文彻底解密Spring 源码之Spring MVC