SpringMVC源码分析(6)剖析DefaultAnnotationHandlerMapping

接<SpringMVC源码分析(5)剖析重要组件HandlerMapping>,继续剖析HandlerMapping,DefaultAnnotationHandlerMapping是SpringMVC 中最重要的HandlerMapping组件。虽然它在spring3.1版本后被废弃了。

包括2部分内容

  1. DefaultAnnotationHandlerMapping剖析
  2. HandlerMapping的拦截器

1.DefaultAnnotationHandlerMapping剖析

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

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容器初始化过程中调用。

在上篇文章中总结过,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[] 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是属于全局范围的,对所有请求进行进行拦截。

2.2 Interceptor的类结构

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

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

2.3 默认intercepter配置

<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>

这是一段最普通的声明

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

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

2.4 没事找事型的配置

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

如下

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

<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。

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

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

时间: 2024-10-17 22:44:30

SpringMVC源码分析(6)剖析DefaultAnnotationHandlerMapping的相关文章

SpringMVC源码分析(5)剖析重要组件HandlerMapping

<SpringMVC源码分析(4)剖析DispatcherServlet重要组件>   简要介绍了各个组件,从本章开始,针对各个组件,深入介绍.首先是HandlerMapping. HanlerMapping是沟通请求和后端controller映射,是所有请求的入口. 1.类结构介绍 该图只对属性和类层级进行了描述,屏蔽了方法,主要是为了根据内部属性,重点理解Spring HandlerMapping提供功能. 1.1 AbstractHandlerMapping HandlerMapping

SpringMVC源码分析(4)剖析DispatcherServlet重要组件

<SpringMVC源码分析(3)DispatcherServlet的请求处理流程 >简单介绍了一个请求的处理过程, 简略描述了调用过程,并没有涉及过多细节,如url匹配,报文解析转换等. <SpringMVC源码分析(2)DispatcherServlet的初始化>:介绍了servlet的初始化过程,尤其initStrategies方法. 本文主要总结DispatcherServlet几个重要组件的关系. 1.类图 该类图并没有全面的描述SpringMVC相关类,重点说明组件的关

SpringMVC源码分析(3)DispatcherServlet的请求处理流程

<SpringMVC源码分析(1)标签解析>:介绍了解析过程中,初始化若干组件. <SpringMVC源码分析(2)DispatcherServlet的初始化>:初始化DispatcherServlet的多个组件. 本文继续分析DispatcherServlet解析请求的过程. 概览 ①:DispatcherServlet是springmvc中的前端控制器(front controller),负责接收request并将request转发给对应的处理组件. ②:HanlerMappi

SpringMVC源码分析(2)DispatcherServlet的初始化

DispatcherServlet的初始化,是在org.springframework.web.context.ContextLoaderListener完成加载后,才开始的.这时候WebApplicationContext(包含DAO,Service等)已经初始完毕. DispatcherServlet的初始过程主要完成 1.WebApplicationContext父子容器维护 2.初始化Servlet策略 本文主要内容 DispatcherServlet的集成体系 DispatcherSe

springmvc源码分析系列-请求处理流程

接上一篇-springmvc源码分析开头片 上一节主要说了一下springmvc与struts2的作为MVC中的C(controller)控制层的一些区别及两者在作为控制层方面的一些优缺点.今天就结合下面的一张图和上一篇中关于springmvc各个模块之间及各个模块中的类的继承关系的一张图对springmvc的请求处理流程进行一个分析.当然有了springmvc的请求处理流程我们就知道了springmvc是如何在启动的时候去加载或者去解析对应的具体控制器,以及modleAndView使干什么用的

【转】springmvc源码分析链接

SpringMVC源码 SpringMVC源码分析系列 说到java的mvc框架,struts2和springmvc想必大家都知道,struts2的设计基本上完全脱离了Servlet容器,而 springmvc是依托着Servlet容器元素来设计的,同时springmvc基于Spring框架,Spring框架想必搞java的同学都很熟 悉. 一进Spring的官网就发现了这样一排醒目的文字, spring可以让我们构造简单的.便携的.又快又易于扩展的基于jvm的系统和应用程序. 没错,基于Spr

SpringMVC源码分析系列

说到java的mvc框架,struts2和springmvc想必大家都知道,struts2的设计基本上完全脱离了Servlet容器,而springmvc是依托着Servlet容器元素来设计的,同时springmvc基于Spring框架,Spring框架想必搞java的同学都很熟悉. 一进Spring的官网就发现了这样一排醒目的文字, spring可以让我们构造简单的.便携的.又快又易于扩展的基于jvm的系统和应用程序. 没错,基于Spring的MVC框架SpringMVC同样也可以构造具有这些特

8、SpringMVC源码分析(3):分析ModelAndView的形成过程

首先,我们还是从DispatcherServlet.doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception方法开始,看看这个牛逼的ModelAndView是怎么开始的,又是怎么结束的: 1 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Except

springMVC源码分析--HttpMessageConverter数据转化(一)

之前的博客我们已经介绍了很多springMVC相关的模块,接下来我们介绍一下springMVC在获取参数和返回结果值方面的处理.虽然在之前的博客老田已经分别介绍了参数处理器和返回值处理器: (1)springMVC参数值处理器 springMVC源码分析--HandlerMethodArgumentResolver参数解析器(一) (2)springMVC返回值处理器 springMVC源码分析--HandlerMethodReturnValueHandler返回值解析器(一) 但对参数和返回值