JavaWeb学习之——Spring篇之HandlerMapping

1.HandlerMapping

HandlerMapping接口只有一个方法

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;

就是根据Reuqest返回HandlerExecutionChain,看名字就知道是一个处理链,包含多个Interceptor和一个Handler。

203621_vbwF_3039671.png

2.AbstractHandlerMapping

AbstractHandlerMapping是HanlderMapping的默认实现类。是一个模板方法(Spring 底层大代码大量使用模板方法)。

2.1 初始化interceptor

@Override
protected void initApplicationContext() throws BeansException {
   extendInterceptors(this.interceptors);
   detectMappedInterceptors(this.adaptedInterceptors);
   initInterceptors();
}

deleteMappedIntercptors方法的左右就是把ApplicationContext中所有类型为MappedInterceptor的bean加入到adaptedIntercptors这个成员变量中

protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
   mappedInterceptors.addAll(
         BeanFactoryUtils.beansOfTypeIncludingAncestors(
               getApplicationContext(), MappedInterceptor.class, true, false).values());
}

initInterceptors()函数的作用就是把成员变量interceptors中的interceptor加入到adaptedInterceptors成员变量中去,如果是WebRequestInterceptor则转换为对应的adapter类,代码如下

protected void initInterceptors() {
   if (!this.interceptors.isEmpty()) {
      for (int i = 0; i < this.interceptors.size(); i++) {
         Object interceptor = this.interceptors.get(i);
         if (interceptor == null) {
            throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
         }
         this.adaptedInterceptors.add(adaptInterceptor(interceptor));
      }
   }
}
protected HandlerInterceptor adaptInterceptor(Object interceptor) {
   if (interceptor instanceof HandlerInterceptor) {
      return (HandlerInterceptor) interceptor;
   }
   else if (interceptor instanceof WebRequestInterceptor) {
      return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
   }
   else {
      throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
   }
}

2.2 实现Handler接口

@Override
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
   Object handler = getHandlerInternal(request);
   if (handler == null) {
      handler = getDefaultHandler();
   }
   if (handler == null) {
      return null;
   }
   // Bean name or resolved handler?
   if (handler instanceof String) {
      String handlerName = (String) handler;
      handler = getApplicationContext().getBean(handlerName);
   }

   HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
   if (CorsUtils.isCorsRequest(request)) {
      CorsConfiguration globalConfig = this.corsConfigSource.getCorsConfiguration(request);
      CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
      CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
      executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
   }
   return executionChain;
}

主要做了2件事:

  • 1.获取request对应的Handler,获取handler是通过getHandlerInternal方法,这个由子类去实现 
  • 2.找出对应的Interceptors返回HandlerExecutionChain,从根据request找出合适的Interceptors加入其中(例如MappedInterceptor是根据url地址来判断是否加入)。

AbstractHandleMapping是一个模板方法,子类需要实现getHandlerInternal。下面介绍2个主要实现类AbstractUrlHandleMapping和AbstractHandleMethodMapping。

3. AbstractUrlHandleMapping

3.1 getHandlerInternal

从名字可以猜到,AbstractUrlHandleMapping是通过url来匹配对应的Handler。AbstractUrlHandleMapping依然是一个抽象类。getHandlerInternal代码:

/**
 * 根据request的url查询对应的handler
 */
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
   //查找url地址,有可能是servlet的url,也有可能是Spring Mvc定义的url
   String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
   //通过url查找对应的Handler
   Object handler = lookupHandler(lookupPath, request);
   //如果找不到对应的handler
   if (handler == null) {
      // 特殊情况,如果查询地址为"/",则判断是否用rootHandler,如果没有就设置为defaultHandler
      Object rawHandler = null;
      if ("/".equals(lookupPath)) {
         rawHandler = getRootHandler();
      }
      if (rawHandler == null) {
         rawHandler = getDefaultHandler();
      }
      if (rawHandler != null) {
         // 如果是字符串,则从ApplicationConetxt中获取对应的bean
         if (rawHandler instanceof String) {
            String handlerName = (String) rawHandler;
            rawHandler = getApplicationContext().getBean(handlerName);
         }
         //模板方法,验证Handler
         validateHandler(rawHandler, request);
         //返回HandlerExecutionChain类型,会注册2个interceptor
         //PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor
         handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
      }
   }
   if (handler != null && logger.isDebugEnabled()) {
      logger.debug("Mapping [" + lookupPath + "] to " + handler);
   }
   else if (handler == null && logger.isTraceEnabled()) {
      logger.trace("No handler mapping found for [" + lookupPath + "]");
   }
   return handler;
}

主要做了一下几件事:

  1. 根据request生成查询handler的URL
  2. 根据url获取对应的handler
  3. 如果handler没有在map中查到,则使用默认的handler。

3.2 lookupHandler

方法目的就是根据url 查找对应的Handler,但是在url匹配的时候往往会有"/test/*"或者Path变量“/test/{id}”这样的情况,这样就无法通过url地址直接从map中获取对应的handler。

protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
   // 能否直接找到
   Object handler = this.handlerMap.get(urlPath);
   if (handler != null) {
      // 如果是字符串从ApplicationContext中直接获取
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = getApplicationContext().getBean(handlerName);
      }
      // 模板方法,验证handler
      validateHandler(handler, request);
      //返回HandlerExecutionChain类型,会注册2个interceptor
      //PathExposingHandlerInterceptor和UriTemplateVariablesHandlerInterceptor
      return buildPathExposingHandler(handler, urlPath, urlPath, null);
   }
   // 模板匹配
   List<String> matchingPatterns = new ArrayList<String>();
   for (String registeredPattern : this.handlerMap.keySet()) {
      if (getPathMatcher().match(registeredPattern, urlPath)) {
         matchingPatterns.add(registeredPattern);
      }
      else if (useTrailingSlashMatch()) {
         if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
            matchingPatterns.add(registeredPattern +"/");
         }
      }
   }
   //根据order找出优先级坐高的
   String bestPatternMatch = null;
   Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
   if (!matchingPatterns.isEmpty()) {
      Collections.sort(matchingPatterns, patternComparator);
      if (logger.isDebugEnabled()) {
         logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
      }
      bestPatternMatch = matchingPatterns.get(0);
   }
   if (bestPatternMatch != null) {
      handler = this.handlerMap.get(bestPatternMatch);
      if (handler == null) {
         Assert.isTrue(bestPatternMatch.endsWith("/"));
         handler = this.handlerMap.get(bestPatternMatch.substring(0, bestPatternMatch.length() - 1));
      }
      
      if (handler instanceof String) {
         String handlerName = (String) handler;
         handler = getApplicationContext().getBean(handlerName);
      }
      validateHandler(handler, request);
      String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);

      // 这里是处理当有多个url和最优的path是同一优先级的,那么从里面获取地址变量,加入到uriTemplateVariables中
      Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
      for (String matchingPattern : matchingPatterns) {
         if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
            Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
            Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
            uriTemplateVariables.putAll(decodedVars);
         }
      }
      return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
   }
   //找不到对应的handler
   return null;
}

这个函数主要完成了几下几件事

  1. url能直接从map中获取的则直接获取
  2. 否则通过路径pattern去匹配url
  3. 从所有匹配到handler查找出优先级最高的
  4. 如果有多个同最优先级的,则查看里面是否有pathVariable
  5. 找到任意的Handler则加入2个特殊的interceptor

3.3 buildPathExposingHandler 

上面所有的能成功找到对应的handler后,都会调用这个方法。这儿方法就是在handler的基础上,加入2个interceptor,返回一个HandlerExecutionChain

protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern,
      String pathWithinMapping, Map<String, String> uriTemplateVariables) {

   HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler);
   chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping));
   if (!CollectionUtils.isEmpty(uriTemplateVariables)) {
      chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables));
   }
   return chain;
}

这2个interceptor的作用就是当前匹配的url、匹配条件和url模板参数设置到request的attribute里面

3.4 Map初始化

上面一直说的通过url从map中获取对应的Handler,这个map是成员变量

private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();

这个map的初始化是在registerHandler方法,这个方法比较功能比较简单,参数分别是url和handler,然后把url和handler放入map中,再做一些重复检查和特殊handler(roothandler)的设置。url和handler从哪里来,则又是子类提供处理具体的逻辑来调用这个方法。

到这里,关于怎么通过url来获取handler已经解决了,下面就是url和handler是怎么注册到handlermap中的,其中大部分子类都被@Deprecated标记,

140403_yCyj_3039671.png

3.5 SimpleUrlHandlerMapping

这个类非常简单,就是给定参数map,注册到handlermap中

protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
   if (urlMap.isEmpty()) {
      logger.warn("Neither 'urlMap' nor 'mappings' set on SimpleUrlHandlerMapping");
   }
   else {
      for (Map.Entry<String, Object> entry : urlMap.entrySet()) {
         String url = entry.getKey();
         Object handler = entry.getValue();
         // Prepend with slash if not already present.
         if (!url.startsWith("/")) {
            url = "/" + url;
         }
         // Remove whitespace from handler bean name.
         if (handler instanceof String) {
            handler = ((String) handler).trim();
         }
         registerHandler(url, handler);
      }
   }
}

4. AbstractHandlerMethodMapping

现在来看看AbstractHandlerMapping的另外一个继承 AbstractHandlerMethodMapping

140738_FOFj_3039671.png

AbstractHandlerMethodMapping从名字就可以看出,是把一个方法作为一个handler。并且继承体系也十分清晰。分析的顺序和AbstractUrlHandleMapping类似,首先还是先分析一下getHandlerInternal这个方法

@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
   String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
   if (logger.isDebugEnabled()) {
      logger.debug("Looking up handler method for path " + lookupPath);
   }
   this.mappingRegistry.acquireReadLock();
   try {
      HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
      if (logger.isDebugEnabled()) {
         if (handlerMethod != null) {
            logger.debug("Returning handler method [" + handlerMethod + "]");
         }
         else {
            logger.debug("Did not find handler method for [" + lookupPath + "]");
         }
      }
      return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
   }
   finally {
      this.mappingRegistry.releaseReadLock();
   }
}

看起来和AbstractUrlHandleMapping类似,只不过AbstractUrlHandleMapping中的handler是一个bean,而AbstractHandlerMethodMapping的handler是一个Method

初始化方法initHandlerMethods:

protected void initHandlerMethods() {
   //获取所有beanName
   String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
         BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
         getApplicationContext().getBeanNamesForType(Object.class));

   for (String beanName : beanNames) {
      //beanName是以scopedTarget.开头的,则不用户Handler Method
      if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
         Class<?> beanType = null;
         try {
            beanType = getApplicationContext().getType(beanName);
         }
         catch (Throwable ex) {
            ...
         }
         // isHandler模板方法,由子类去判断改bean是否注册
         if (beanType != null && isHandler(beanType)) {
            //注册到HandlerMethod
            detectHandlerMethods(beanName);
         }
      }
   }
   //初始化HandlerMethod
   handlerMethodsInitialized(getHandlerMethods());
}

detectHandlerMethods,就是注册handlerMethod。具体说明看注解

protected void detectHandlerMethods(final Object handler) {
   Class<?> handlerType = (handler instanceof String ?
         getApplicationContext().getType((String) handler) : handler.getClass());
   final Class<?> userType = ClassUtils.getUserClass(handlerType);
  
   //查找该handler中的method及其对应的条件(参照@RequestMapping中的参数)
   Map<Method, T> methods = MethodIntrospector.selectMethods(userType,
         new MethodIntrospector.MetadataLookup<T>() {
            @Override
            public T inspect(Method method) {
               return getMappingForMethod(method, userType);
            }
         });
   for (Map.Entry<Method, T> entry : methods.entrySet()) {
      Method invocableMethod = AopUtils.selectInvocableMethod(entry.getKey(), userType);
      T mapping = entry.getValue();
      registerHandlerMethod(handler, invocableMethod, mapping);
   }
}
  1.  isHandler 是一个模板方法,由子类去判断改bean是否是一个handler。
  2. getMappingForMethod也是一个模板方法,判断一个类中Method方法的相关条件信息
  3. detectHandlerMethods是在传入的beanName对应的bean中查找handlerMethod,然后注册
  4. handlerMethodsInitialized 是一个预留方法,里面没有做任何事,如果我们自定义一个HandlerMapping有需要的话可以重写

4.1 RequestMappingHandlerMapping

RequestMappingHandlerMapping继承AbstractHandlerMethodMapping,首先看两个模板方法

isHandler,就是判断是否有Controller或者RequestMapping注解。

@Override
protected boolean isHandler(Class<?> beanType) {
   return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
         AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

getMappingForMethod这个方法就是根据传入的函数,返回一个RequestMappingInfo (也就是@RequestMapping中的参数信息)

@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
   RequestMappingInfo info = createRequestMappingInfo(method);
   if (info != null) {
      RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
      if (typeInfo != null) {
         info = typeInfo.combine(info);
      }
   }
   return info;
}

这样,AbstractHandlerMethodMapping的就能通过一个url可以找出对应MethodHandler,

转载于:https://my.oschina.net/u/3039671/blog/825068

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值