Spring MVC — @RequestMapping原理讲解-1

转载地址 :http://blog.csdn.net/j080624/article/details/56278461

为了下降文章篇幅,使得文章更目标化,简洁化,咱们就不例举各类@RequestMapping的用法等内容了.

    文章主要说明如下问题:html

    1. Spring怎样处理@RequestMapping(怎样将请求路径映射到控制器类或方法)java

    2. Spring怎样将请求分派给正确的控制器类或方法web

    3. Spring如何实现灵活的控制器方法的spring

    在Spring MVC 3.1 以前的版本中,Spring默认使用 DefaultAnnotationHandlerMapping,AnnotationMethodHandlerAdapter来处理 @RequestMapping注解和请求方法调用,而从3.1开始提供了一组新的API完成这些工做。相比之下,新的API更加的合理完善,开放,易拓 展,面向对象。这篇文章即是基于3.1的新API进行剖析的。api

1、概念解析

    在开始以前咱们先了解下新的API中引入的新接口或者类,这会有助于后面的处理过程的理解。不得不说新的API提供了更多漂亮的抽象,你能感觉到面向对象的魅力。数组

  1. RequestMappingInfo 这个类是对请求映射的一个抽象,它包含了请求路径,请求方法,请求头等信息。其实能够看作是@RequestMapping的一个对应类。缓存

  2. HandlerMethod这个类封装了处理器实例(Controller Bean)和 处理方法实例(Method)以及方法参数数组(MethodParameter[])安全

  3. MethodParameter  这个类从2.0就有了,它封装了方法某个参数的相关信息及行为,如该参数的索引,该参数所属方法实例或构造器实例,该参数的类型等。mvc

  4. HandlerMapping 该接口的实现类用来定义请求和处理器以前的映射关系,其中只定义了一个方法getHandler。app

  5. AbstractHandlerMethodMapping 这是HandlerMapping的一个基本实现类,该类定义了请求与HandlerMethod实例的映射关系。

  6. RequestMappingInfoHandlerMapping这个是AbstractHandlerMethodMapping的实现类,他维护了一个RequestMappingInfo和HandlerMethod的Map属性。

  7. RequestMappingHandlerMapping 这个是RequestMappingInfoHandlerMapping的子类,它将@RequestMapping注解转化为RequestMappingInfo实例,并为父类使用。也就是咱们处理@RequestMapping的终点。

  8. InitializingBean 这个接口定义了其实现Bean在容器完成属性设置后能够执行自定义初始化操做,咱们的AbstractHandlerMethodMapping便实现了这个接口,而且定义了一组自定义操做,就是用来检测处理咱们的@RequestMapping注解。

    概念讲的太多总不是什么好事。但明白了上述概念基本上就成功一半了,其中的实现相对@Autowired那篇简单多了。

2、InitialiZingBean.afterPropertySet()

    咱们从头开始,看看到底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。

3、检测@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的组合,重点是考虑进各类状况,咱们来看下方法的注释吧:

    清晰,全面吧,有兴趣的能够看一下代码,这里不讲了。

4、注册请求映射

    上面咱们已经讲了@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实例。

5、承上启下

    篇幅有些长了,超出字数限制了,只能分红两篇了..........................

    这章只分析了咱们前面三个问题中的第一个,可是已经至关接近了。下一篇咱们来说,Spring怎样处理客户发来的请求,以及方法调用的。

相关文章
相关标签/搜索