,继续剖析HandlerMapping,DefaultAnnotationHandlerMapping是SpringMVC 中最重要的HandlerMapping组件。虽然它在spring3.1版本后被废弃了。

包括2部分内容

  1. DefaultAnnotationHandlerMapping剖析

  2. HandlerMapping的拦截器

1.DefaultAnnotationHandlerMapping剖析

鉴于它的重要地位,贴下结构图

SpringMVC源码分析(6)剖析DefaultAnnotationHandlerMapping_第1张图片

public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandlerMapping {
    //是否使用后缀注册url(比如如果注册了/users,同时也会注册 /users.* 和/users/
   private boolean useDefaultSuffixPattern = true;
    //缓存handler和requestMapping条件关系,验证时使用
   private final Map cachedMappings = new HashMap();
   
   ...
   }

1.1. 重写determineUrlsForHandler方法

AbstractDetectingUrlHandlerMapping的子类,重写determineUrlsForHandler方法;

initApplicationContext时被调用。下图为determineUrlsForHandler调用上下文。在springmvc容器初始化过程中调用。

SpringMVC源码分析(6)剖析DefaultAnnotationHandlerMapping_第2张图片

在上篇文章中总结过,HandlerMapping的主要职责

  1. 注册Handler.可以是注解,可以是XML声明。

  2. 生成url,有很多策略。beanName,前缀,包名称都可以作为参考

  3. 维护mapping关系

  4. url的匹配能力

DefaultAnnotationHandlerMapping也是如此,determineUrlsForHandler方法,根据方法名就可以猜到,“为Handler匹配URL”。和ControllerClassNameHandlerMapping,ControllerBeanNameHandlerMapping之流是办的事一样的。区别就是他们是基于XML定义的,灵活性不足。DefaultAnnotationHandlerMapping灵活性和功能上更强大而已,名称从RequestMapping注解中获取。具体可以参考determineUrlsForHandlerMethods方法。

//按方法逐个生成URL
protected String[] determineUrlsForHandlerMethods(Class handlerType, final boolean hasTypeLevelMapping) {
   String[] subcla***esult = determineUrlsForHandlerMethods(handlerType);
   if (subcla***esult != null) {
      return subcla***esult;
   }

   final Set urls = new LinkedHashSet();
   Set> handlerTypes = new LinkedHashSet>();
   handlerTypes.add(handlerType);
   handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
   for (Class currentHandlerType : handlerTypes) {
      ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
         public void doWith(Method method) {
            RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
            if (mapping != null) {
               String[] mappedPatterns = mapping.value();
               if (mappedPatterns.length > 0) {
                  for (String mappedPattern : mappedPatterns) {
                     if (!hasTypeLevelMapping && !mappedPattern.startsWith("/")) {
                        mappedPattern = "/" + mappedPattern;
                     }
                     addUrlsForPath(urls, mappedPattern);
                  }
               }
               else if (hasTypeLevelMapping) {
                  // empty method-level RequestMapping
                  urls.add(null);
               }
            }
         }
      }, ReflectionUtils.USER_DECLARED_METHODS);
   }
   return StringUtils.toStringArray(urls);
}

2.HandlerMapping的拦截器

2.1 Interceptor位置

从下面的结构图中可以看出,拦截器分布在2个位置,分为2类。

MappedInterceptor是与url绑定的,对符合条件的URL进行拦截;

Interceptor是属于全局范围的,对所有请求进行进行拦截。

SpringMVC源码分析(6)剖析DefaultAnnotationHandlerMapping_第3张图片


2.2 Interceptor的类结构

SpringMVC源码分析(6)剖析DefaultAnnotationHandlerMapping_第4张图片


UserRoleAuthorizationInterceptor 检查当前用户的授权
PathExposingHandlerInterceptor 暴露bestMatchingPattern
ConversionServiceExposingInterceptor
暴露 ConversionService
ThemeChangeInterceptor 支持主题切换
LocaleChangeInterceptor 支持切换语言
UriTemplateVariablesHandlerInterceptor 暴露请求变量
 WebContentInterceptor 检查,准备请求和响应

值得关注的是WebRequestInterceptor。需要专门开辟一章研究。

2.3 默认intercepter配置


   
   
   
      
         
   

这是一段最普通的声明

SpringMVC源码分析(6)剖析DefaultAnnotationHandlerMapping_第5张图片

我有了个误解:的子标签声明的拦截器会设置在interceptor中,结果不是如此。

ConversionServiceExposingInterceptor是在解析标签时,默认注册的。文章中提到过。

2.4 没事找事型的配置

了解了spring mvc的标签解析过程,很容易配置一个自定义程度比较高的处理器类。确点就是很繁琐

如下

这一段代码虽然可以运行,但缺少类型转换拦截器,需要配置。


   
      
         
      
   
   
      
         
            
               
                  /account
               
            
            
               
            
         
      
   

如此声明,一定要把    注释掉,否则系统会存在两个DefaultAnnotationHandlerMapping。


个人感觉这一段和上面的效果是一样的。

区别

仅是LocaleChangeInterceptor这样公共的拦截器设置在了interceptors属性上,

而不是mappedInterceptors。

源码解析,一定不要停留在设置表面,要洞察底层细节。

当然,是默认配置好,还是原生态的配置好,一个是简介透明,一个自定义程度高,各取所需。