第三节:<mvc:annotationdriven />标签解析

一、<mvc:annotation-driven />配置在什么时候必须配置?

  1、配置了视图映射 view-controller

    直接配置响应的页面:无需经过控制器来执行结果 ;但会导致其他请求路径失效,需要配置mvc:annotation-driven标签

<mvc:view-controller  path="/success" view-name="success"/>  

  

  2、配置了静态资源

    RESTful-CRUD操作,删除时,通过jQuery执行delete请求时,找不到静态资源,需要配置mvc:annotation-driven标签

    < mvc:default-servlet-handler /> 将在 SpringMVC 上下文中定义一个 DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由 WEB 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由 DispatcherServlet 继续处理。

  3、配置了类型转换器

    配置类型转换器服务时,需要指定转换器服务引用

<mvc:annotation-driven conversion-service=“conversionService”/> 
会将自定义的ConversionService 注册到 Spring MVC 的上下文中

  

  4、完成 JSR 303 数据验证,也需要配置

二、关于 <mvc:annotation-driven /> 作用

  1、<mvc:annotation-driven /> 会自动注册下面的组件:

RequestMappingHandlerMapping
RequestMappingHandlerAdapter
ExceptionHandlerExceptionResolver

    还将提供以下支持:

    ① 支持使用 ConversionService 实例对表单参数进行类型转换

    ② 支持使用 @NumberFormat 注解@DateTimeFormat 注解完成数据类型的格式化

    ③ 支持使用 @Valid 注解对 JavaBean 实例进行 JSR 303 验证

    ④ 支持使用 @RequestBody@ResponseBody 注解

  2、源码参考:

    BeanDefinitionParse 用于解析 xml 文件中的标签

    继承树结构:

    

     AnnotationDrivenBeanDefinitionParser 用于解析 <mvc: annotation-driven /> 标签

     AnnotationDrivenBeanDefinitionParser 源码参考:

class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {

    public static final String CONTENT_NEGOTIATION_MANAGER_BEAN_NAME = "mvcContentNegotiationManager";

    private static final boolean javaxValidationPresent =
            ClassUtils.isPresent("javax.validation.Validator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());

    private static boolean romePresent =
            ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());

    private static final boolean jaxb2Present =
            ClassUtils.isPresent("javax.xml.bind.Binder", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());

    private static final boolean jackson2Present =
            ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", AnnotationDrivenBeanDefinitionParser.class.getClassLoader()) &&
                    ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());

    private static final boolean jackson2XmlPresent =
            ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());

    private static final boolean gsonPresent =
            ClassUtils.isPresent("com.google.gson.Gson", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());


    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        Object source = parserContext.extractSource(element);

        CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
        parserContext.pushContainingComponent(compDefinition);

        RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext);

        RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
        handlerMappingDef.setSource(source);
        handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        handlerMappingDef.getPropertyValues().add("order", 0);
        handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
        String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(handlerMappingDef);

        if (element.hasAttribute("enable-matrix-variables")) {
            Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
            handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
        }
        else if (element.hasAttribute("enableMatrixVariables")) {
            Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enableMatrixVariables"));
            handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
        }

        configurePathMatchingProperties(handlerMappingDef, element, parserContext);

        RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
        RuntimeBeanReference validator = getValidator(element, source, parserContext);
        RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);

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

        ManagedList<?> messageConverters = getMessageConverters(element, source, parserContext);
        ManagedList<?> argumentResolvers = getArgumentResolvers(element, parserContext);
        ManagedList<?> returnValueHandlers = getReturnValueHandlers(element, parserContext);
        String asyncTimeout = getAsyncTimeout(element);
        RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);
        ManagedList<?> callableInterceptors = getCallableInterceptors(element, source, parserContext);
        ManagedList<?> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);

        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);
        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);
        }
        else if (element.hasAttribute("ignoreDefaultModelOnRedirect")) {
            // "ignoreDefaultModelOnRedirect" spelling is deprecated
            Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignoreDefaultModelOnRedirect"));
            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);
        String handlerAdapterName = parserContext.getReaderContext().registerWithGeneratedName(handlerAdapterDef);

        String uriCompContribName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
        RootBeanDefinition uriCompContribDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
        uriCompContribDef.setSource(source);
        uriCompContribDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
        uriCompContribDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
        parserContext.getReaderContext().getRegistry().registerBeanDefinition(uriCompContribName, uriCompContribDef);

        RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
        csInterceptorDef.setSource(source);
        csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
        RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
        mappedCsInterceptorDef.setSource(source);
        mappedCsInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
        mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
        String mappedInterceptorName = parserContext.getReaderContext().registerWithGeneratedName(mappedCsInterceptorDef);

        RootBeanDefinition exceptionHandlerExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
        exceptionHandlerExceptionResolver.setSource(source);
        exceptionHandlerExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        exceptionHandlerExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
        exceptionHandlerExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
        exceptionHandlerExceptionResolver.getPropertyValues().add("order", 0);
        addResponseBodyAdvice(exceptionHandlerExceptionResolver);

        String methodExceptionResolverName =
                parserContext.getReaderContext().registerWithGeneratedName(exceptionHandlerExceptionResolver);

        RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
        responseStatusExceptionResolver.setSource(source);
        responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        responseStatusExceptionResolver.getPropertyValues().add("order", 1);
        String responseStatusExceptionResolverName =
                parserContext.getReaderContext().registerWithGeneratedName(responseStatusExceptionResolver);

        RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
        defaultExceptionResolver.setSource(source);
        defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        defaultExceptionResolver.getPropertyValues().add("order", 2);
        String defaultExceptionResolverName =
                parserContext.getReaderContext().registerWithGeneratedName(defaultExceptionResolver);

        parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, methodMappingName));
        parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, handlerAdapterName));
        parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName));
        parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));
        parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
        parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
        parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));

        // Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
        MvcNamespaceUtils.registerDefaultComponents(parserContext, source);

        parserContext.popAndRegisterContainingComponent();

        return null;
    }
...
}

三、以访问静态和动态资源为例

  只要请求不好使就可以配置 <mvc:annotation-driven /> 标签

  1、既不配置 <mvc:default-servlet-handler>,也不配置 <mvc:annotation-driven />

    效果@RequestMapping映射的资源能访问,静态资源.html/.js/.img 不能访问

    通过 DispatcherServlet 中 dispatch 方法追踪源码:

    handlerMappings:

      

       当发送请求的时候,会按照顺序依次执行 handlerMappings;

       BeanNameUrlHandlerMapping 里面没有保存任何映射信息;

       DefaultAnnotationHandlerMapping 里面保存了映射信息:

       

      动态资源能访问:DefaultAnnotationHandlerMapping 里面的 handlerMap 中保存了每一个资源的映射信息;
      静态资源不能访问:就是 handlerMap 中没有保存静态资源映射的请求;

    handlerAdapters:执行适配器

      

         与此同时 AnnotationMethodHandlerAdapter 帮我们执行目标方法;(过时的)

      另外:conversionService是null(类型转换器是不起作用的)

  2、只配置 <mvc:default-servlet-handler>,不配置 <mvc:annotation-driven />

    效果:静态资源.html/.js/.img 能访问,动态资源不能访问

    handlerMappings

      

      

      动态资源不能访问原因:DefaultAnnotationHandlerMapping  没有了,用 SimpleUrlHandlerMapping 替换了,将所有请求直接交给 Tomcat;
      静态资源能访问: SimpleUrlHandlerMapping 把所有请求都映射给 Tomcat;

    handlerAdapter:

      

       AnnotationMethodHandlerAdapter 没有了,替换成了 SimpleControllerHandlerAdapter。

  3、既配置 <mvc:default-servlet-handler>,也配置 <mvc:annotation-driven />

    效果:动态资源和静态资源都能访问

    handlerMappings

      

    

       

       RequestMappingHandlerMapping:用于处理动态请求;

      SimpleUrlHandlerMapping :将请求直接交给Tomcat,有他,静态资源就没问题;

      先用 RequestMappingHandlerMapping 处理动态请求,如果能找到映射信息,进行处理;如果找不到,使用 SimpleUrlHandlerMapping 交给Tomcat 处理。

    handlerAdapters

      

      原来的 AnnotationMethodHandlerAdapter 被换成 RequestMappingHandlerAdapter 

    AnnotationMethodHandlerAdapter已经过时,Spring3.2推荐RequestMappingHandlerAdapter来替代。所以说,默认情况下,没有配置这两个配置时,HelloWorld 程序可以正常运行,但是,涉及到静态资源查找的时候,就必须配置这个 < mvc:annotation-driven /> 配置了。

原文地址:https://www.cnblogs.com/niujifei/p/15629637.html