spring5/springboot2源码学习 -- HandlerMapping

概念

根据请求找到对应的处理器Handler。另外,Handler总是会包装成一个HandlerExecutionChain实例,该实例可能会有一些HandlerInterceptor实例.DispatcherServlet总是会调用每个HandlerInterceptor的preHandle方法,如果所有的preHandle方法都返回true才会调用handler本身

实现

接口定义

public interface HandlerMapping {

	String BEST_MATCHING_HANDLER_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingHandler";

	String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath";

	String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";

	String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";

	String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";

	String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";

	String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";

	String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";
	//根据请求找到对应的Handler
	HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}

具体实现

在DispatcherServlet中,并不是只有一个HandlerMapping,而是有个HandlerMapping的列表,每当有请求过来,DispatcherServlet会依次遍历HandlerMapping,直到找到对应的Handler为止。

从实现来说,HandlerMapping的实现是一种典型的模板方法模式。

在公共的抽象类中定义了getHandler()的实现

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 = obtainApplicationContext().getBean(handlerName);
		}

		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

		if (logger.isTraceEnabled()) {
			logger.trace("Mapped to " + handler);
		}
		else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
			logger.debug("Mapped to " + executionChain.getHandler());
		}

		if (hasCorsConfigurationSource(handler)) {
			CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			config = (config != null ? config.combine(handlerConfig) : handlerConfig);
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}

		return executionChain;
	}

而每个具体的实现类重写getDefaultHandler()方法。

子类

类图如下
在这里插入图片描述
从使用的角度来看,我们要将一个请求映射到一个处理器,有这几个办法:

  • 通过@RequestMapping注解。这种方式,对应的HandlerMapping就是RequestMappingHandlerMapping
  • 通过定义一个名字以/开头的bean。这个比较少见,即如果bean的name是/开头,比如/foo,那请求的path如果是/foo那就会交由这个bean来处理。对应的HandlerMapping是BeanNameUrlHandlerMapping
  • SimpleUrlHandlerMapping跟BeanNameUrlHandlerMapping差不多,可以自己手动将一个path映射到一个bean上
  • WelcomPageHandlerMapping就是映射到index.html

@RequestMapping的实现

我们知道,@Component是将一个普通的Java类注册成一个Spring的bean。@Controller是一个特殊的@Component。特殊性就在于HandlerMapping对于@Controller/@RestController/@RequestMapping有特殊的处理。

AbstractHandlerMethodMapping实现了InitializingBean,所以有这么个方法:

@Override
public void afterPropertiesSet() {
  initHandlerMethods();//@1
}
//@1
protected void initHandlerMethods() {
  //getCandidateBeanNames()基本上就是获取所有ApplicationContext中的bean的name
  for (String beanName : getCandidateBeanNames()) {
    //只有name不是以scopedTarget开头就执行@2
    if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
      processCandidateBean(beanName);//@2
    }
  }
  handlerMethodsInitialized(getHandlerMethods());
}
//@2
protected void processCandidateBean(String beanName) {
  Class<?> beanType = null;
  try {
    beanType = obtainApplicationContext().getType(beanName);
  }
  catch (Throwable ex) {
    // An unresolvable bean type, probably from a lazy bean - let's ignore it.
    if (logger.isTraceEnabled()) {
      logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
    }
  }
  //判断是不是一个handler
  if (beanType != null && isHandler(beanType)) {//@3
    detectHandlerMethods(beanName);
  }
}
//@3
protected boolean isHandler(Class<?> beanType) {
  //这个bean是不是有@Controller注解或者@RequestMapping注解
  return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
          AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

上述的逻辑很简单粗暴,就是获取所有bean,看看bean的类上是不是有@Controller或者@RequestMapping注解。如果有,再去解析内部的方法。

解析内部方法的过程:

protected void detectHandlerMethods(Object handler) {
  Class<?> handlerType = (handler instanceof String ?
                          obtainApplicationContext().getType((String) handler) : handler.getClass());

  if (handlerType != null) {
    Class<?> userType = ClassUtils.getUserClass(handlerType);
   	//获取的是Method->RequestMappingInfo的Map
    Map<Method, T> methods = MethodIntrospector.selectMethods(userType,(MethodIntrospector.MetadataLookup<T>) method -> {
      try {
        return getMappingForMethod(method, userType);//@4
      }
      catch (Throwable ex) {
        throw new IllegalStateException("Invalid mapping on handler class [" +
                                        userType.getName() + "]: " + method, ex);
      }
    });
    if (logger.isTraceEnabled()) {
      logger.trace(formatMappings(userType, methods));
    }
    methods.forEach((method, mapping) -> {
      Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
      //将这个方法注册到url->handler的Map中去
      registerHandlerMethod(handler, invocableMethod, mapping);//@6
    });
  }
}

先看看@4的实现

protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
  RequestMappingInfo info = createRequestMappingInfo(method);//@5
  if (info != null) {
    //这里还有个createRequestMappingInfo是因为@RequestMapping即可以注解在类上,也可以注解在方法上,所以会有个先方法的,然后再用类的其他值补全
    RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
    if (typeInfo != null) {
      info = typeInfo.combine(info);
    }
    String prefix = getPathPrefix(handlerType);
    if (prefix != null) {
      info = RequestMappingInfo.paths(prefix).build().combine(info);
    }
  }
  return info;
}
//@5
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
  //获取方法上的@RequestMapping注解
  RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
  RequestCondition<?> condition = (element instanceof Class ?
                                   getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
  //根据注解的各个值创建一个RequestMappingInfo,这个RequestMappingInfo其实就是组合了@RequestMapping注解的各个字段的值,method啊,header啊,consumer啊之类的
  return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}

再继续看看注册这个映射@6的实现的实现

//mapping是RequestMappingInfo,handler是@Controller注解的bean name,method是@RequestMapping注解的方法
public void register(T mapping, Object handler, Method method) {
  //将这个method和一些关联的信息组合成handler
  HandlerMethod handlerMethod = createHandlerMethod(handler, method);//@7
  //验证这个RequestMappingInfo是不是已经有对应的handler了,有的话就报异常
  validateMethodMapping(handlerMethod, mapping);
  this.mappingLookup.put(mapping, handlerMethod);
	//获取到关联的url,可以有多个
  List<String> directUrls = getDirectUrls(mapping);
  for (String url : directUrls) {
    this.urlLookup.add(url, mapping);
  }

  String name = null;
  //生成一个名字,比如这个类是DemoController,方法是index,那生成的名字就是DC#index
  if (getNamingStrategy() != null) {
    name = getNamingStrategy().getName(handlerMethod, mapping);
    addMappingName(name, handlerMethod);
  }
	//cors的配置,详情见https://blog.csdn.net/yuxiuzhiai/article/details/103501443
  CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping);
  if (corsConfig != null) {
    this.corsLookup.put(handlerMethod, corsConfig);
  }

  this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name));
}

//@7
protected HandlerMethod createHandlerMethod(Object handler, Method method) {
  if (handler instanceof String) {
    //这个HandlerMethod就是把这个方法,包含这个方法的beanName,已经整个BeanFactory,方法的参数这些组装到一起,形成一个hanler
    return new HandlerMethod((String) handler,
                             obtainApplicationContext().getAutowireCapableBeanFactory(), method);
  }
  return new HandlerMethod(handler, method);
}

梳理一遍

  • AbstractHandlerMethodMapping实现了InitializingBean,所以会触发initHandlerMethods()方法
  • initHandlerMethods()就是粗暴的遍历所有bean,看他是不是满足成为一个handler的条件,条件就是@Controller和@RequestMapping主机
  • 如果有类和方法满足,则根绝@RequestMapping注解,生成一个RequestMappingInfo,然后根据这个RequestMappingInfo和这个类、这个方法、方法参数这些构造出一个HandlerMethod
  • 保存这个url到这个HandlerMethod的映射
  • 处理下cors
  • 结束

next

到这还没结束,上述的所有只是说了spring是怎么样将@Controller和@RequestMapping注解的方法和类整成一个可以处理请求的handler的。而spring中并不是用这个裸handler处理请求的。接着看最开始的方法(又回到最初的梦想)

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		//我们知道,如果是RequestMappingHandlerMapping处理的,那这里返回的是一个HandlerMethod
  	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 = obtainApplicationContext().getBean(handlerName);
		}
		//关于handler的东西请见https://blog.csdn.net/yuxiuzhiai/article/details/103501568
		HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

		if (logger.isTraceEnabled()) {
			logger.trace("Mapped to " + handler);
		}
		else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
			logger.debug("Mapped to " + executionChain.getHandler());
		}
		//cors处理
		if (hasCorsConfigurationSource(handler)) {
			CorsConfiguration config = (this.corsConfigurationSource != null ? this.corsConfigurationSource.getCorsConfiguration(request) : null);
			CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
			config = (config != null ? config.combine(handlerConfig) : handlerConfig);
			executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
		}
		//返回包裹了拦截器链的handler
		return executionChain;
	}

关于handler和mvc 方法拦截器的解析请见https://blog.csdn.net/yuxiuzhiai/article/details/103501568

关于spring mvc的cors的处理请见https://blog.csdn.net/yuxiuzhiai/article/details/103501443

结语

整个HandlerMapping的作用就是根据请求返回一个包裹这拦截器链的handler。

总共涉及25个类(HandlerMapping12,RequestCondition11,HandlerMethodMappingNamingStrategy2)

(水平有限,最近在看spring源码,分享学习过程,希望对各位有点微小的帮助。
如有错误,请指正~)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值