Spring MVC ——mvc:annotation-driven

Spring MVC ——<mvc:annotation-driven/>


SpringMVC配置文件中添加:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="
			http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
			http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
			
	<import resource="classpath:applicationContext.xml" />

	<!-- 开启注解扫描 -->
	<mvc:annotation-driven />
</beans>

一、查看SpringMVC包目录结构 

1、查看:spring.handlers

http\://www.springframework.org/schema/mvc=org.springframework.web.servlet.config.MvcNamespaceHandler

2、查看:spring.schemas

http\://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd=org/springframework/web/servlet/config/spring-mvc-3.0.xsd
http\://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd=org/springframework/web/servlet/config/spring-mvc-3.1.xsd
http\://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd=org/springframework/web/servlet/config/spring-mvc-3.2.xsd
http\://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd=org/springframework/web/servlet/config/spring-mvc-4.0.xsd
http\://www.springframework.org/schema/mvc/spring-mvc.xsd=org/springframework/web/servlet/config/spring-mvc-4.0.xsd

 3、文件解析

Spring在对配置文件进行解析的时,会读取META-INF/spring.handlers这个文件来获取一个

org.springframework.beans.factory.xml.NamespaceHandler 

的实现类。这个解析过程比较复杂,我们先不展开(想了解的可以先看一下这个方法:org.springframework.beans.factory.xml.DefaultNamespaceHandlerResolver#resolve)

Spring在解析xml配置文件的时候可以读取到org.springframework.web.servlet.config.MvcNamespaceHandler这个类就行了。我们先看一下NamespaceHandler这个类的UML类图:

在上面这张图中我们不仅看到了MvcNamespaceHandler、还有AopNamespaceHandler、TxNamespaceHandler、ContextNamespaceHandler等等(套路都是一样的)。

spring.schemas中的xsd文件描述了xml文档的一些信息,比如有哪些根节点、根节点下面有哪些子节点、有哪些属性信息、数据类型等等。

4、MvcNamespaceHandler 

package org.springframework.web.servlet.config;

import org.springframework.beans.factory.xml.NamespaceHandler;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;

/**
 * {@link NamespaceHandler} for Spring MVC configuration namespace.
 *
 * @author Keith Donald
 * @author Jeremy Grelle
 * @since 3.0
 */
public class MvcNamespaceHandler extends NamespaceHandlerSupport {

	@Override
	public void init() {		
		//解析 <mvc:annotation-driven>标签
		registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
		//解析 <mvc:default-servlet-handler>标签
		registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser());
		//解析 <mvc:interceptors>标签
		registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser());
		registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser());
		registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser());
	}

}

在这个方法中注册了很多的BeanDefinitionParser,而<mvc:annotation-driven/>这个标签是通过AnnotationDrivenBeanDefinitionParser这个类来解析的。这个类中我们要分析的重点就是parse这个方法

二、AnnotationDrivenBeanDefinitionParser

通过阅读类注释文档,我们发现这个类主要是用来向工厂中注册了

HandlerMapping接口的实现类,用来处理请求映射的。

  • RequestMappingHandlerMapping    处理@RequestMapping注解

  • BeanNameUrlHandlerMapping     将controller类的名字映射为请求url。

 用来处理请求的。具体点说就是确定调用哪个controller的哪个方法来处理当前请求。

  • RequestMappingHandlerAdapter  处理@Controller注解的处理器,支持自定义方法参数和返回值

  • HttpRequestHandlerAdapter     处理继承HttpRequestHandler的处理器

  • SimpleControllerHandlerAdapter处理继承自Controller接口的处理器。

用来处理异常的解析器。

  • ExceptionHandlerExceptionResolver 

  • ResponseStatusExceptionResolver 

  • DefaultHandlerExceptionResolver 

1、HandlerMapping

Spring mvc 使用HandlerMapping来找到并保存url请求和处理函数间的mapping关系。 

例如:DefaultAnnotationHandlerMapping

DefaultAnnotationHandlerMapping将扫描当前所有已经注册的spring beans中的@requestmapping标注以找出url 和 handler method处理函数的关系并予以关联。

2、Handleradapter

Spring MVC通过HandlerAdapter来实际调用处理函数。 

例如:AnnotationMethodHandlerAdapter
DispatcherServlet中根据handlermapping找到对应的handler method后,首先检查当前工程中注册的所有可用的handlerAdapter,根据handlerAdapter中的supports方法找到可以使用的handlerAdapter。通过调用handlerAdapter中的handle方法来处理及准备handler method中的参数及annotation(这就是spring mvc如何将reqeust中的参数变成handle method中的输入参数的地方),最终调用实际的handle method。 

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

        //第一个在这 RequestMappingHandlerMapping

        RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);

        handlerMappingDef.setSource(source);

        handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

        handlerMappingDef.getPropertyValues().add("order", 0);

        handlerMappingDef.getPropertyValues().add("removeSemicolonContent", false);

        handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);

        String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(handlerMappingDef);

        //第二个在这 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);

        if (element.hasAttribute("ignoreDefaultModelOnRedirect")) {

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

        //异常处理解析器

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

        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(exceptionHandlerExceptionResolver, methodExceptionResolverName));

        parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));

        parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));

        parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));

        //这里注册了BeanNameUrlHandlerMapping,SimpleControllerHandlerAdapter等

        // Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"

        MvcNamespaceUtils.registerDefaultComponents(parserContext, source);

 

        parserContext.popAndRegisterContainingComponent();

 

        return null;

    }

//在这啊。

public static void registerDefaultComponents(ParserContext parserContext, Object source) {

        registerBeanNameUrlHandlerMapping(parserContext, source);

        registerHttpRequestHandlerAdapter(parserContext, source);

        registerSimpleControllerHandlerAdapter(parserContext, source);

三、总结

我们知道了它们自动为我们注册了这么多的Bean,那这些Bean是做什么的呢?

主要说明里面的两个,RequestMappingHandlerMapping 和 RequestMappingHandlerAdapter。

  •     第一个是HandlerMapping的实现类,它会处理@RequestMapping 注解,并将其注册到请求映射表中。
  •     第二个是HandlerAdapter的实现类,它是处理请求的适配器,说白了,就是确定调用哪个类的哪个方法,并且构造方法参数,返回值。

 那么<mvc:annotation-scan/>跟<context:component-scan/>有什么区别呢?

  • <context:component-scan/>标签是告诉Spring 来扫描指定包下的类,并注册被@Component,@Controller,@Service,@Repository等注解标记的组件。
  • <mvc:annotation-scan/>是告知Spring,我们启用注解驱动。然后Spring会自动为我们注册上面说到的几个Bean到工厂中,来处理我们的请求。
  • <context:annotation-config/>是对包进行扫描,实现注释驱动Bean定义,同时将bean自动注入容器中使用。即解决了@Controller标识的类的bean的注入和使用。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
当使用<mvc:annotation-driven>标签时,可能会出现以下错误: 1. 找不到org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping类 这个错误通常是由于Spring版本不兼容导致的。如果您使用的是Spring 3.1或更高版本,则应该使用<mvc:annotation-driven>标签。如果您使用的是Spring 3.或更低版本,则应该使用<context:component-scan>标签。 2. 找不到org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter类 这个错误通常是由于Spring版本不兼容导致的。如果您使用的是Spring 3.1或更高版本,则应该使用<mvc:annotation-driven>标签。如果您使用的是Spring 3.或更低版本,则应该使用<context:component-scan>标签。 3. 找不到org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter类 这个错误通常是由于Spring版本不兼容导致的。如果您使用的是Spring 5.或更高版本,则应该使用@EnableWebMvc注释。如果您使用的是Spring 4.或更低版本,则应该使用WebMvcConfigurerAdapter类。 4. 找不到org.springframework.web.servlet.DispatcherServlet类 这个错误通常是由于您没有正确配置DispatcherServlet导致的。请确保您已经正确配置了DispatcherServlet,并且在web.xml文件中正确地映射了它。 5. 找不到org.springframework.web.servlet.view.InternalResourceViewResolver类 这个错误通常是由于您没有正确配置InternalResourceViewResolver导致的。请确保您已经正确配置了InternalResourceViewResolver,并且在Spring配置文件中正确地定义了它。 总之,当使用<mvc:annotation-driven>标签时,如果出现错误,请检查您的Spring版本和配置是否正确。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值