SpringMVC源码分析(6)剖析DefaultAnnotationHandlerMapping

本文深入探讨了SpringMVC中的HandlerMapping组件——DefaultAnnotationHandlerMapping的工作原理,包括其如何为控制器方法生成URL映射及拦截器配置方式。

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

包括2部分内容

  1. DefaultAnnotationHandlerMapping剖析

  2. HandlerMapping的拦截器

1.DefaultAnnotationHandlerMapping剖析

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

wKioL1hCKnWx4gYCAACX0CQYtFA954.png

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

1.1. 重写determineUrlsForHandler方法

AbstractDetectingUrlHandlerMapping的子类,重写determineUrlsForHandler方法;

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

wKioL1hCK1yhXCRbAABFzPcjfXs824.png

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

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

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

  3. 维护mapping关系

  4. url的匹配能力

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

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
//按方法逐个生成URL
protected  String[] determineUrlsForHandlerMethods(Class<?> handlerType,  final  boolean  hasTypeLevelMapping) {
    String[] subclassResult = determineUrlsForHandlerMethods(handlerType);
    if  (subclassResult !=  null ) {
       return  subclassResult;
    }
 
    final  Set<String> urls =  new  LinkedHashSet<String>();
    Set<Class<?>> handlerTypes =  new  LinkedHashSet<Class<?>>();
    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是属于全局范围的,对所有请求进行进行拦截。

wKiom1hCMDzyA3KAAAA1iMyumJU462.png


2.2 Interceptor的类结构

wKioL1hCMruCfElNAACC0tMAcZI252.png


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

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

2.3 默认intercepter配置

1
2
3
4
5
6
7
8
< mvc:interceptors >
    < bean  class = "org.springframework.web.servlet.i18n.LocaleChangeInterceptor"  />
    < bean  class = "org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor" />
    < mvc:interceptor >
       < mvc:mapping  path = "/account" />
          < bean  class = "org.springframework.web.servlet.theme.ThemeChangeInterceptor" ></ bean >
    </ mvc:interceptor >
</ mvc:interceptors >

这是一段最普通的声明

wKioL1hCT1-ByAozAAByb2_2KaM218.png

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

ConversionServiceExposingInterceptor是在解析标签时,默认注册的。<SpringMVC源码分析(1)标签解析>文章中提到过。

2.4 没事找事型的配置

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

如下

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
< bean   class = "org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" >
    < property  name = "interceptors" >
       < array >
          < bean  class = "org.springframework.web.servlet.i18n.LocaleChangeInterceptor"  />
       </ array >
    </ property >
    < property  name = "mappedInterceptors" >
       < list >
          < bean  class = "org.springframework.web.servlet.handler.MappedInterceptor" >
             < constructor-arg  index = "0" >
                < list >
                   < value >/account</ value >
                </ list >
             </ constructor-arg >
             < constructor-arg  index = "1" >
                < bean  class = "org.springframework.web.servlet.theme.ThemeChangeInterceptor" ></ bean >
             </ constructor-arg >
          </ bean >
       </ list >
    </ property >
</ bean >

如此声明,一定要把    <mvc:annotation-driven />注释掉,否则系统会存在两个DefaultAnnotationHandlerMapping。


个人感觉这一段和上面的<mvc:interceptors>效果是一样的。

区别

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

而不是mappedInterceptors。

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

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



本文转自 randy_shandong 51CTO博客,原文链接:http://blog.51cto.com/dba10g/1879103,如需转载请自行联系原作者

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值