tomcat + spring mvc原理(九):spring mvc如何将请求投送到Controller中的方法1

tomcat + spring mvc原理(九):spring mvc如何将请求投送到Controller中的方法 1

前言

    在原理七中列举了spring mvc的九大组件,分别是:

  • HandlerMapping:处理器映射,寻找对应Interceptor和Controller
  • HandlerAdapter:处理器适配,使用处理器Handler干活
  • HandlerExceptionResolver:处理器错误解析,请求处理过程报错,设置ModelAndView
  • ViewResolver:视图解析器,寻找对应视图
  • RequestToViewNameTranslator:从请求获取view名,view名供视图解析器寻找对应视图
  • LocaleResolver:从请求获取Locale,Locale供视图解析器使用
  • ThemeResolver:主题解析器
  • MultipartResolver:处理上传请求
  • FlashMapManager:管理重定向相关的FlashMap

这篇文章主要是介绍HandlerMapping组件的原理。HandlerMapping组件的调用是在DispatcherServlet的doDispatch方法中,其中有:

mappedHandler = getHandler(processedRequest);

mappedHandler = getHandler(processedRequest),返回的mappedHandler是HandlerExecutionChain类型,内含Interceptor和Handler。和Controller一样,Interceptor也被归类为一种Handler。(各组件的配合使用完成请求的过程在原理八中介绍)

其中 getHandler方法的代码如下:

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  if (this.handlerMappings != null) {
    for (HandlerMapping mapping : this.handlerMappings) {
      HandlerExecutionChain handler = mapping.getHandler(request);
      if (handler != null) {
        return handler;
      }
    }
  }
  return null;
}

DispatcherServlet类中维护了一个HandlerMapping的列表:

private List<HandlerMapping> handlerMappings;

通过传入的请求体,遍历这个HandlerMapping来找到处理这个请求的Handler(Controller或者Interceptor),这篇文章的主要内容就是分析HandlerMapping的原理,探索它是怎样实现“路由”到我们定义的Interceptor或者Controller的。

HandlerMapping组件的整体架构

tomcat + spring mvc原理(九):HandlerMapping原理-handlerMapping.png

    HandlerMapping是一个接口,定义了HandlerMapping的标准,子类只需要实现接口定义的方法。这样做的好处是,HandlerMapping作为一个比较灵活的组件,支持用户实现该标准,自定义自己的处理器映射。一般使用接口做隔离的目的,就是能够提供比较好的扩展性,类定义遵循接口标准,可以方便地拔插不同的类实现。
    实现HandlerMapping接口的类是两个系列:一个系列是AbstractUrlHandlerMapping,这个通过名字就可以看出是通过url匹配Handler;另一个系列是AbstractHandlerMethodMapping,它匹配的方式比较多,而且直接匹配到方法,我们目前常用的@RequestMapping注解、@GetMapping注解和@PostMapping注解就是这种类型。由于我没有了解过AbstractUrlHandlerMapping系列的使用场景,目前看上去使用比较多的是AbstractHandlerMethodMapping,所以我只会着重介绍后者的实现原理。

接口标准和AbstractHandlerMapping

    事实上,HandlerMapping接口只有一个方法:

HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception

就是在上文中DispatcherServlet类的getHandler方法中调用的mapping.getHandler(request)。
    上面的类继承图谱只显示了AbstractHandlerMapping继承树的一部分,下面的图展示了完整的部分。可以看到,AbstractHandlerMapping还实现了ApplicationContextAware、ServletContextAware、BeanNameAware接口。
tomcat + spring mvc原理(九):HandlerMapping原理-AbstractHandlerMapping.png
在spring mvc中Aware系列的类代表对可以获取Aware前面修饰的环境,比如ApplicationContextAware可以获取ApplicationContext,ServletContextAware可以获取ServletContext,BeanNameAware可以获取BeanName。只需要implements这个接口,重载这个接口唯一的方法:

public interface ApplicationContextAware extends Aware {
    void setApplicationContext(ApplicationContext var1) throws BeansException;
}

整个spring mvc服务在初始化是会自动调用实现类重载的这个setApplicationContext方法,将ApplicationContext变量传递给我们使用。这个技巧在业务代码编写中也可以使用。
    不过,AbstractHandlerMapping系列子类的初始化并不是调用setApplicationContext方法,ApplicationObjectSupport和WebApplicationObjectSupport对这个方法进行了重新封装。ApplicationObjectSupport自身持有ApplicationContext对象,在重载的setApplicationContext中调用了自行定义的initApplicationContext(ApplicationContext context)方法,然后内部调用无参的initApplicationContext()方法。无参的方法在ApplicationObjectSupport中是空实现,最终的是由AbstractHandlerMapping重载了initApplicationContext()方法。

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

    其实initApplicationContext()的有效代码做的事就是配置和初始化Interceptor。extendInterceptors方法在AbstractHandlerMapping是模板方法,为子类提供添加、修改Interceptor列表的入口(虽然子类并没有实现)。
在讲detectMappedInterceptors方法和initInterceptors方法的内部代码之前,先需要了解三种类型的Interceptor:

private final List<Object> interceptors = new ArrayList<>();  //1

private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();  //2

AbstractHandlerMapping类中持有了其中两类。

  1. interceptors是代表所有spring mvc中配置的拦截器,能够从环境上下文中获取所有Interceptor的配置。
  2. mappedInterceptors代表有url映射的Interceptor,在getHandler方法返回的HandlerExceptionChain中,只有匹配命中的MappedInterceptor才会被返回,可以从adaptedInterceptors获取。
  3. adaptedInterceptors包含了mappedInterceptor,也包含不需要匹配的Interceptor。
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
  mappedInterceptors.addAll(
      BeanFactoryUtils.beansOfTypeIncludingAncestors(
          obtainApplicationContext(), MappedInterceptor.class, true, false).values());
}

这里方法传参的起名虽然是mappedInterceptors,但是在上文initApplicationContext方法中可以看到,传入的实际是this.adaptedInterceptors。detectMappedInterceptors方法的作用就是将程序上下文中所有的MappedInterceptor放入到adaptedInterceptors中。adaptedInterceptors中还包括了其他HandlerInterceptor和WebRequestInterceptor类型的拦截器。

总而言之,AbstractHandlerMapping初始化时主要做的工作就是初始化Interceptor。
    初始化之后,对于HandlerMapping系列类来说,更重要的是如何实现getHandler方法。AbstractHandlerMapping重载了getHandler方法,并为后续子类提供了模板方法getHandlerInternal。

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
  //获取映射的Handler
  Object handler = getHandlerInternal(request);
  if (handler == null) {
    handler = getDefaultHandler();
  }
  if (handler == null) {
    return null;
  }
  if (handler instanceof String) {
    String handlerName = (String) handler;
    handler = obtainApplicationContext().getBean(handlerName);
  }

  //添加筛选的Interceptor
  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 (CorsUtils.isCorsRequest(request)) {
    CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
    CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
    CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
    executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
  }

  return executionChain;
}

   这个方法主要可以拆分为四步。第一步是获取映射的Handler(Controller对应的方法),getHandlerInternal只是一个模板方法,后续子类需要关注这个方法的实现。如果没有获取到Handler,可以取默认的Handler来处理,甚至直接利用Handler的name从环境上下文中加载。第二步是添加筛选出的Interceptor,由getHandlerExecutionChain方法构造出HandlerExecutionChain。上文提到,adaptedInterceptors中包括了mappedInterceptors和不需要匹配url的Interceptor。所以getHandlerExecutionChain中会筛选中命中url的mappedInterceptor和其他不需要匹配url的Interceptor,都加入到HandlerExecutionChain中返回。

protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
  HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
      (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

  String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
  for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
    if (interceptor instanceof MappedInterceptor) {
      MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
      if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
        chain.addInterceptor(mappedInterceptor.getInterceptor());
      }
    }
    else {
      chain.addInterceptor(interceptor);
    }
  }
  return chain;
}

    第三步打印跟踪日志或者调试日志。第四步是和http的跨站请求相关的。如果该请求为跨站请求,会在http头中有Origin属性,存储源站点信息。这样url匹配逻辑应该需要考虑源站url,所以要额外处理。

    为了合理设置篇幅,使层次更加清晰,后续AbstractHandlerMethodMapping子类系列的内容放在二期中详细分析。

tomcat + spring mvc原理(一):tomcat原理综述和静态架构
tomcat + spring mvc原理(二):tomcat容器初始化加载和启动
tomcat + spring mvc原理(三):tomcat网络请求的监控与处理1
tomcat + spring mvc原理(四):tomcat网络请求的监控与处理2
tomcat + spring mvc原理(五):tomcat Filter组件实现原理
tomcat + spring mvc原理(六):tomcat WAR包的部署与加载
tomcat + spring mvc原理(七):spring mvc的Servlet和九大标准组件的静态结构与初始化
tomcat + spring mvc原理(八):spring mvc对请求的处理流程

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值