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的注入和使用。