Spring MVC — @RequestMapping原理讲解-1

天下之事常成于困约,而败于奢靡。这篇文章主要讲述Spring MVC — @RequestMapping原理讲解-1相关的知识,希望能为你提供帮助。
转载地址 :http://blog.csdn.net/j080624/article/details/56278461 为了降低文章篇幅,使得文章更目标化,简洁化,我们就不例举各种@RequestMapping的用法等内容了. 具体请点击查看@RequestMapping的用法        文章主要说明以下问题:

    1. Spring怎样处理@RequestMapping(怎样将请求路径映射到控制器类或方法)
    2. Spring怎样将请求分派给正确的控制器类或方法
    3. Spring如何实现灵活的控制器方法的
        在Spring  MVC 3.1  之前的版本中,Spring默认使用 DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter来处理 @RequestMapping注解和请求方法调用,而从3.1开始提供了一组新的API完成这些工作。相比之下,新的API更加的合理完善,开放,易拓 展,面向对象。这篇文章便是基于3.1的新API进行剖析的。
一、概念解析
        在开始之前我们先了解下新的API中引入的新接口或者类,这会有助于后面的处理过程的理解。不得不说新的API提供了更多漂亮的抽象,你能感受到面向对象的魅力。
  1. RequestMappingInfo  这个类是对请求映射的一个抽象,它包含了请求路径,请求方法,请求头等信息。其实可以看做是@RequestMapping的一个对应类。
  2. HandlerMethod这个类封装了处理器实例(Controller  Bean)和  处理方法实例(Method)以及方法参数数组(MethodParameter[])
  3. MethodParameter    这个类从2.0就有了,它封装了方法某个参数的相关信息及行为,如该参数的索引,该参数所属方法实例或构造器实例,该参数的类型等。
  4. HandlerMapping  该接口的实现类用来定义请求和处理器之前的映射关系,其中只定义了一个方法getHandler。
  5. AbstractHandlerMethodMapping  这是HandlerMapping的一个基本实现类,该类定义了请求与HandlerMethod实例的映射关系。
  6. RequestMappingInfoHandlerMapping这个是AbstractHandlerMethodMapping的实现类,他维护了一个RequestMappingInfo和HandlerMethod的Map属性。
  7. RequestMappingHandlerMapping  这个是RequestMappingInfoHandlerMapping的子类,它将@RequestMapping注解转化为RequestMappingInfo实例,并为父类使用。也就是我们处理@RequestMapping的终点。
  8. InitializingBean  这个接口定义了其实现Bean在容器完成属性设置后可以执行自定义初始化操作,我们的AbstractHandlerMethodMapping便实现了这个接口,并且定义了一组自定义操作,就是用来检测处理我们的@RequestMapping注解。
        概念讲的太多总不是什么好事。但明白了上述概念基本上就成功一半了,其中的实现相对@Autowired那篇简单多了。
二、InitialiZingBean.afterPropertySet()
【Spring MVC — @RequestMapping原理讲解-1】        我们从头开始,看看到底Spring是怎样检测并处理我们@RequestMapping注解的。不知大家还记不记的这段代码:
 
[java]  view plain  copy
  1. Object  exposedObject  =  bean;    
  2.                 try  {   
  3.                         populateBean(beanName,  mbd,  instanceWrapper);    
  4.                         if  (exposedObject  !=  null)  {   
  5.                                 exposedObject  =  initializeBean(beanName,  exposedObject,  mbd);    
  6.                         }   
  7.                 }   
      这是BeanFactory创建Bean过程中需要执行的一段代码,其中populateBean方法便是@Autowired注解的处理过程,执行的属性的自动注入等操作。因为initializeBean方法当时与主题无关没有讲,不过这时它便是我们关注的焦点了。
        上面概念中我们讲到InitiaizingBean接口,它的实现Bean会在容器完成属性注入后执行一个自定义操作,这不就满足initializeBean方法的执行唤醒嘛,我们来看它的实现:
[java]  view plain  copy
  1. protected  Object  initializeBean(final  String  beanName,  final  Object  bean,  RootBeanDefinition  mbd)  {   
  2.                 if  (System.getSecurityManager()  !=  null)  {   
  3.                         AccessController.doPrivileged(new  PrivilegedAction< Object> ()  {   
  4.                                 public  Object  run()  {   
  5.                                         invokeAwareMethods(beanName,  bean);    
  6.                                         return  null;    
  7.                                 }   
  8.                         },  getAccessControlContext());    
  9.                 }   
  10.                 else  {//这里检测当前Bean是否实现一些列Aware接口,并调用相关方法,我们不关心。   
  11.                         invokeAwareMethods(beanName,  bean);    
  12.                 }   
  13.                    
  14.                 Object  wrappedBean  =  bean;    
  15.                 if  (mbd  ==  null  ||  !mbd.isSynthetic())  {//BeanPostProcessor  的回调,不关心   
  16.                         wrappedBean  =  applyBeanPostProcessorsBeforeInitialization(wrappedBean,  beanName);    
  17.                 }   
  18.    
  19.                 try  {   
  20.                         invokeInitMethods(beanName,  wrappedBean,  mbd); //这是我们需要关心的,下面看下它的实现   
  21.                 }   
  22.    
  23.                 if  (mbd  ==  null  ||  !mbd.isSynthetic())  {//BeanPostProcessor  的回调,不关心   
  24.                         wrappedBean  =  applyBeanPostProcessorsAfterInitialization(wrappedBean,  beanName);    
  25.                 }   
  26.                 return  wrappedBean;    
  27.         }   
      我们接着来看下invokeInitMethods方法的实现:
 
[java]  view plain  copy
  1. protected  void  invokeInitMethods(String  beanName,  final  Object  bean,  RootBeanDefinition  mbd)   
  2.                         throws  Throwable  {   
  3.                 //是否是InitializingBean的实例   
  4.                 boolean  isInitializingBean  =  (bean  instanceof  InitializingBean);    
  5.                 if  (isInitializingBean  & &      
  6.                                 (mbd  ==  null  ||  !mbd.isExternallyManagedInitMethod("afterPropertiesSet")))  {   
  7.                         if  (System.getSecurityManager()  !=  null)  {   
  8.                                 try  {   
  9.                                         AccessController.doPrivileged(new  PrivilegedExceptionAction< Object> ()  {   
  10.                                                 public  Object  run()  throws  Exception  {//利用系统安全管理器调用   
  11.                                                         ((InitializingBean)  bean).afterPropertiesSet();    
  12.                                                         return  null;    
  13.                                                 }   
  14.                                         },  getAccessControlContext());    
  15.                                 }   
  16.                         }                                   
  17.                         else  {//调用InitializingBean的afterPropertiesSet方法。   
  18.                                 ((InitializingBean)  bean).afterPropertiesSet();    
  19.                         }   
  20.                 }   
  21.                 //调用自定义初始化方法。。。省略,不关心   
  22.         }   
      上一篇关于< mvc:annotation-driven/> 的文章,我们说过了,当在配置文件中加上该标记后,Spring(3.1后)会默认为我们注册RequestMappingHandlerMapping等Bean定义。而RequestMappingHandlerMapping实现了InitializingBean接口,因此,在初始化并装配该Bean实例时,执行到上述代码是,便会执行他的afterPropertySet方法。我们接下来看看他的afterPropertySet方法:
 
[java]  view plain  copy
  1. public  void  afterPropertiesSet()  {   
  2.                 initHandlerMethods();    
  3.         }   
  4.    
  5.         //Scan  beans  in  the  ApplicationContext,  detect  and  register  handler  methods.   
  6.         protected  void  initHandlerMethods()  {   
  7.                 //扫描所有注册的Bean   
  8.                 String[]  beanNames  =  (this.detectHandlerMethodsInAncestorContexts  ?   
  9.                       BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(),     
  10.                                 Object.class)  :  getApplicationContext().getBeanNamesForType(Object.class));    
  11.                 //遍历这些Bean,依次判断是否是处理器,并检测其HandlerMethod   
  12.                 for  (String  beanName  :  beanNames)  {   
  13.                         if  (isHandler(getApplicationContext().getType(beanName))){   
  14.                                 detectHandlerMethods(beanName);    
  15.                         }   
  16.                 }   
  17.                 //这个方法是个空实现,不管他   
  18.                 handlerMethodsInitialized(getHandlerMethods());    
  19.         }   
      它直接调用了initHandlerMethods()方法,并且该方法被描述为:扫描ApplicationContext中的beans,检测并注册处理器方法。we are close。
三、检测@RequestMapping    
        我们再看它是怎样判断是否是处理器的,以及怎么detect Handler Methods 的:
 
[java]  view plain  copy
  1. @Override   
  2.         protected  boolean  isHandler(Class< ?>   beanType)  {   
  3.                 return  ((AnnotationUtils.findAnnotation(beanType,  Controller.class)  !=  null)  ||   
  4.                                 (AnnotationUtils.findAnnotation(beanType,  RequestMapping.class)  !=  null));    
  5.         }   
      啊哈,很简单,就是看看有没有被@Controller或者@RequestMapping注解标记
 
[java]  view plain  copy
  1. protected  void  detectHandlerMethods(final  Object  handler)  {   
  2.                 Class< ?>   handlerType  =  (handler  instanceof  String)  ?   
  3.                                 getApplicationContext().getType((String)  handler)  :  handler.getClass();    
  4.                 final  Class< ?>   userType  =  ClassUtils.getUserClass(handlerType);    
  5.                 Set< Method>   methods  =  HandlerMethodSelector.selectMethods(userType,  new  MethodFilter(){   
  6.                         public  boolean  matches(Method  method)  {//只选择被@RequestMapping标记的方法   
  7.                                 return  getMappingForMethod(method,  userType)  !=  null;    
  8.                         }   
  9.                 });    
  10.    
  11.                 for  (Method  method  :  methods)  {   
  12.                         //根据方法上的@RequestMapping来创建RequestMappingInfo实例。   
  13.                         T  mapping  =  getMappingForMethod(method,  userType);    
  14.                         //注册请求映射   
  15.                         registerHandlerMethod(handler,  method,  mapping);    
  16.                 }   
  17.         }   
      整个的检测过程大致清楚了:1)遍历Handler中的所有方法,找出其中被@RequestMapping注解标记的方法。2)然后遍历这些方法,生成RequestMappingInfo实例。3)将RequestMappingInfo实例以及处理器方法注册到缓存中。
      下面我们看看细节:
 
[java]  view plain  copy
  1. @Override   
  2.         protected  RequestMappingInfo  getMappingForMethod(Method  method,  Class< ?>   handlerType)  {   
  3.                 RequestMappingInfo  info  =  null;    
  4.                 //获取方法method上的@RequestMapping实例。   
  5.                 RequestMapping  methodAnnotation  =     
  6.                                                                 AnnotationUtils.findAnnotation(method,  RequestMapping.class);    
  7.                 if  (methodAnnotation  !=  null)  {//方法被注解了   
  8.                         RequestCondition< ?>   methodCondition  =  getCustomMethodCondition(method); //始终返回null   
  9.                         info  =  createRequestMappingInfo(methodAnnotation,  methodCondition); //创建MappingInfo   
  10.                         //检查方法所属的类有没有@RequestMapping注解   
  11.                         RequestMapping  typeAnnotation  =  AnnotationUtils.findAnnotation(handlerType,     
  12.                                                                                                                                                 RequestMapping.class);    
  13.                         if  (typeAnnotation  !=  null)  {//有类层次的@RequestMapping注解   
  14.                                 RequestCondition< ?>   typeCondition  =  getCustomTypeCondition(handlerType); //null   
  15.                                 //将类层次的RequestMapping和方法级别的RequestMapping结合   
  16.                                 info  =  createRequestMappingInfo(typeAnnotation,  typeCondition).combine(info);    
  17.                         }   
  18.                 }   
  19.                 return  info;    
  20.         }   
      很清晰吧,先获取方法上的@RequestMapping信息,然后获取类级别上的@RequestMapping 信息,然后将两者结合,这里我们有必要再了解下怎样创建RequestMappingInfo对象的(包括他的内部结构),以及怎样将类级别的request mapping信息和方法级别的进行结合的?
 
[java]  view plain  copy
  1. private  RequestMappingInfo  createRequestMappingInfo(RequestMapping  annotation,     
  2.                                                                                                                 RequestCondition< ?>   customCondition)  {   
  3.         return  new  RequestMappingInfo(   
  4.                   new  PatternsRequestCondition(annotation.value(),  getUrlPathHelper(),  getPathMatcher(),   
  5.                                   this.useSuffixPatternMatch,  this.useTrailingSlashMatch,  this.fileExtensions),   
  6.                   new  RequestMethodsRequestCondition(annotation.method()),   
  7.                   new  ParamsRequestCondition(annotation.params()),   
  8.                   new  HeadersRequestCondition(annotation.headers()),   
  9.                   new  ConsumesRequestCondition(annotation.consumes(),  annotation.headers()),   
  10.                   new  ProducesRequestCondition(annotation.produces(),  annotation.headers(),     
  11.                                         getContentNegotiationManager()),     
  12.                 customCondition   
  13.         );    
  14. }   
      其中涉及到了几个类,我们大致了解下含义:
  • PatternRequestCondition  它其实就是URL模式的封装,它包含了一个URL模式的Set集合。其实就是@RequestMapping注解中的value值得封装。
  • RequestMethodRequestCondition  它是@RequestMapping 注解中method属性的封装
  • ParamsRequestCondition  它是@RequestMapping注解中params属性的封装
        等等,依次类推。因此RequestMappingInfo其实就是对@RquestMapping 的封装。
        下面我们再看看怎样进行Combine的:
 
[java]  view plain  copy
  1. public  RequestMappingInfo  combine(RequestMappingInfo  other)  {   
  2.         PatternsRequestCondition  patterns  =  this.patternsCondition.combine(other.patternsCondition);    
  3.         RequestMethodsRequestCondition  methods  =  this.methodsCondition.combine(other.methodsCondition);    
  4.         ParamsRequestCondition  params  =  this.paramsCondition.combine(other.paramsCondition);    
  5.         HeadersRequestCondition  headers  =  this.headersCondition.combine(other.headersCondition);    
  6.         ConsumesRequestCondition  consumes  =  this.consumesCondition.combine(other.consumesCondition);    
  7.         ProducesRequestCondition  produces  =  this.producesCondition.combine(other.producesCondition);    
  8.         RequestConditionHolder  custom  =  this.customConditionHolder.combine(other.customConditionHolder);    
  9.    
  10.         return  new  RequestMappingInfo(patterns,  methods,  params,  headers,  consumes,     
  11.                                         produces,  custom.getCondition());    
  12. }< span  style="white-space:pre; ">         < /span>    
      很清晰,对每一个元素都进行combine操作,我们这里只看PatternRequestCondition是怎么结合的,就是看看怎样合并url的。其他没太大必要。
 
[java]  view plain  copy
  1. public  PatternsRequestCondition  combine(PatternsRequestCondition  other)  {   
  2.                 Set< String>   result  =  new  LinkedHashSet< String> ();    
  3.                 if  (!this.patterns.isEmpty()  & &   !other.patterns.isEmpty())  {   
  4.                         for  (String  pattern1  :  this.patterns)  {   
  5.                                 for  (String  pattern2  :  other.patterns)  {   
  6.                                         result.add(this.pathMatcher.combine(pattern1,  pattern2));    
  7.                                 }   
  8.                         }   
  9.                 }   
  10.                 else  if  (!this.patterns.isEmpty())  {   
  11.                         result.addAll(this.patterns);    
  12.                 }   
  13.                 else  if  (!other.patterns.isEmpty())  {   
  14.                         result.addAll(other.patterns);    
  15.                 }   
  16.                 else  {   
  17.                         result.add("");    
  18.                 }   
  19.                 return  new  PatternsRequestCondition(result,  this.urlPathHelper,  this.pathMatcher,     
  20.                                       this.useSuffixPatternMatch,this.useTrailingSlashMatch,  this.fileExtensions);    
  21.         }   
        1)两个pattern都存在是,调用PathMatcher的combine方法合并两个pattern。
        2)只有一个有时,使用这个。
        3)两个都没有时,为空“”。
        现在真正的url拼接是由PathMatcher来完成的了。我们就不看他的代码了就是一串if else的组合,重点是考虑进各种情况,我们来看下方法的注释吧:
Spring MVC — @RequestMapping原理讲解-1

文章图片

        清晰,全面吧,有兴趣的可以看一下代码,这里不讲了。
四、注册请求映射
        上面我们已经讲了@RequestMapping的检测和处理,并且根据@RequestMapping生成了RequestMappingInfo实例,那Spring必定需要将这些信息保存起来,以处理我们的请求。
        第三节中我们提到一个方法还没有分析,就是registerHandlerMethod 方法:
 
[java]  view plain  copy
  1. protected  void  registerHandlerMethod(Object  handler,  Method  method,  T  mapping)  {   
  2.                 HandlerMethod  handlerMethod;    
  3.                 if  (handler  instanceof  String)  {   
  4.                         String  beanName  =  (String)  handler;    
  5.                         handlerMethod  =  new  HandlerMethod(beanName,  getApplicationContext(),  method);    
  6.                 }   
  7.                 else  {   
  8.                         handlerMethod  =  new  HandlerMethod(handler,  method);    
  9.                 }   
  10.                 //上面几行是根据新的处理器实例,方法实例,RequestMappingInfo来生成新的HandlerMethod实例   
  11.                 //下面是从缓存中查看是否有存在的HandlerMethod实例,如果有并且不相等则抛出异常   
  12.                 HandlerMethod  oldHandlerMethod  =  handlerMethods.get(mapping);    
  13.                 if  (oldHandlerMethod  !=  null  & &   !oldHandlerMethod.equals(handlerMethod))  {   
  14.                         throw  new  IllegalStateException();    
  15.                 }   
  16.                 //handlerMethods  是一个Map键是RequestMappingInfo对象,值是HandlerMethod实例   
  17.                 //因此一个HandlerMethod实例可能处理多个mapping,而一个mapping实例只能由一个method处理   
  18.                 this.handlerMethods.put(mapping,  handlerMethod);    
  19.                 //这里获取mapping实例中的所有url。   
  20.                 Set< String>   patterns  =  getMappingPathPatterns(mapping);    
  21.                 for  (String  pattern  :  patterns)  {   
  22.                         if  (!getPathMatcher().isPattern(pattern))  {   
  23.                                 //urlMap也是Map,键是url  模式,值是RequestMappingInfo实例   
  24.                                 //因此一个mapping实例可能对应多个pattern,但是一个pattern只能对应一个mapping实例   
  25.                                 this.urlMap.add(pattern,  mapping);    
  26.                         }   
  27.                 }   
  28.         }   
 
      这里可能稍微有点绕,其实道理很简单,当请求到达时,去urlMap中需找匹配的url,以及获取对应mapping实例,然后去handlerMethods中获取匹配HandlerMethod实例。
五、承上启下
        篇幅有些长了,超出字数限制了,只能分成两篇了..........................
        这章只分析了我们前面三个问题中的第一个,但是已经相当接近了。下一篇我们来讲,Spring怎样处理客户发来的请求,以及方法调用的。

    推荐阅读