Spring MVC 解读——<mvc:annotation-driven/>(剖析的比较的详细)

本文深入解读了SpringMVC中@annotation-driven标签的作用,包括其内部实现机制以及如何自动注册关键Bean以处理请求。重点介绍了RequestMappingHandlerMapping和RequestMappingHandlerAdapter的作用,与<context:component-scan>标签的区别,并详细展示了代码实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

摘要  <mvc:annotation-driven/>是做什么的?它做了什么?它与<context:component-scan/>有什么区别?

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

一、AnnotationDrivenBeanDefinitionParser

    通常如果我们希望通过注解的方式来进行Spring MVC开发,我们都会在***-servlet.xml中加入<mvc:annotation-driven/>标签来告诉Spring我们的目的。但是我们为什么这么做呢?这个标签是什么意思呢?它做了什么呢?

    同样为了弄清楚这些问题, 像<context:component-scan/>标签一样,我们先找到它的解析类。第一篇文章中说过了,所有的自定义命名空间(像mvc,context等)下的标签解析都是由BeanDefinitionParser接口的子类来完成的。参看第一篇文章中的图片

我们看到有多个AnnotationDrivenBeanDefinitionParser,他们是用来处理不同命名空间下的<annotation-driven/>标签的,我们今天研究的是<mvc:annotation-driven/>标签,所以我们找到对应的实现类是org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser。
    通过阅读类注释文档,我们发现这个类主要是用来向工厂中注册了

    上面几个Bean实例。这几个类都是用来做什么的呢?

    前两个是HandlerMapping接口的实现类,用来处理请求映射的。其中第一个是处理@RequestMapping注解的。第二个会将controller类的名字映射为请求url。

    中间三个是用来处理请求的。具体点说就是确定调用哪个controller的哪个方法来处理当前请求。第一个处理@Controller注解的处理器,支持自定义方法参数和返回值(很酷)。第二个是处理继承HttpRequestHandler的处理器。第三个处理继承自Controller接口的处理器。

    后面三个是用来处理异常的解析器。

二、实现

    光说无凭据,我们直接看代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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的实现类,它是处理请求的适配器,说白了,就是确定调用哪个类的哪个方法,并且构造方法参数,返回值。(后面文章也会陆续详细介绍的)

    那么它跟<context:component-scan/>有什么区别呢?其实想上篇文章中介绍的,<context:component-scan/>标签是告诉Spring 来扫描指定包下的类,并注册被@Component,@Controller,@Service,@Repository等注解标记的组件。

    而<mvc:annotation-scan/>是告知Spring,我们启用注解驱动。然后Spring会自动为我们注册上面说到的几个Bean到工厂中,来处理我们的请求。

转自:http://my.oschina.net/HeliosFly/blog/205343

<?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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!-- 明确指定只扫描Controller --> <context:component-scan base-package="com.itheihei" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean> <mvc:resources mapping="/js/**" location="/js/"/> <mvc:resources mapping="/images/**" location="/images/"/> <mvc:resources mapping="/css/**" location="/css/"/> <mvc:annotation-driven/> </beans><?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- 排除Controller,只扫描Service/Repository等 --> <context:component-scan base-package="com.itheihei"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> </beans>@Controller public class AccountController { @Autowired AccountService accountService; @RequestMapping("findAllAccounts") public String findAllAccounts() { System.out.println("方法成功执行"); return "list"; } } <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>首页</title> </head> <body> <a href="${pageContext.request.contextPath}/findAllAccounts">查看所有用户信息</a> </body> </html><?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> <!-- 将所有请求映射到 dispatcherServlet --> </servlet-mapping> </web-app>为什么会404
最新发布
05-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值