上篇文章《SpringMVC详解-怎么接收请求》已经提到过了,当Servlet接收到请求后会最终调用doDispatch方法(这里省略部分代码保留主要功能):

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

        // 1.确定使用的handler
        mappedHandler = getHandler(processedRequest);
        if (mappedHandler == null) {
          noHandlerFound(processedRequest, response);
          return;
        }

        // 确定handler的适配器
        HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

        // 拦截器前置调用
        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
          return;
        }

        // 实际调用
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
        // 设置默认视图名
        applyDefaultViewName(processedRequest, mv);
        // 拦截器后置调用
        mappedHandler.applyPostHandle(processedRequest, response, mv);
      // 分发结果
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
  }

1 处理器 HandlerMapping

请求分发完毕后就需要去进行处理,这就是我们本文所要讲的重点:HandlerMapping。如果使用过springmvc进行开发过的同学都知道,handler通常指的就是业务上我们在Controller层中编写的带有@RequestMapping的方法,在SpringBoot中为了方便开发,框架还提供了@GetMapping,@PostMaping等注解来简化配置。而这些所有方法为构成一个HandlerMapping。

2 如何转化HandlerMapping?

我们目光可以聚焦到EnableWebMvcConfiguration#requestMappingHandlerMapping方法,它会去创建RequestMappingHandlerMapping对象(就是转化上面提到注解的类)。这个对象实现了InitializingBean接口,所以最终会调用其afterPropertiesSet方法:

@Override
  public void afterPropertiesSet() {
         // 初始化handler
    initHandlerMethods();
  }
  
protected void initHandlerMethods() {
    for (String beanName : getCandidateBeanNames()) {
      if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
        processCandidateBean(beanName);
      }
    }
    handlerMethodsInitialized(getHandlerMethods());
}

其中的processCandidateBean会去解析带有@Controller注解的类。我们简单的看一下代码:

if (beanType != null && isHandler(beanType)) {
      detectHandlerMethods(beanName);
    }
    
@Override
  protected boolean isHandler(Class<?> beanType) {
    return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
        AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}

isHandler方法会找到带有@Controller注解或@RequestMapping的类,detectHandlerMethods方法会对整个类进行处理,解析带有@RequestMapping注解的方法并注册到MappingRegistry中去。

3 如何注册HandlerMapping?

最后看一下框架是怎么将handlers注册到控制器DispatcherServlet中去的。

DispatcherServlet的父类中添加了一个ContextRefreshListener监听器,当容器完全启动后,会调用其onRefresh方法,而正是这个方法,去注册了handers。

@Override
  protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
  }

  protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context);
    initLocaleResolver(context);
    initThemeResolver(context);
    // 注册handler
    initHandlerMappings(context);
    initHandlerAdapters(context);
    initHandlerExceptionResolvers(context);
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
  }

可以看到控制器初始化了非常多的东西,这里我们只关注initHandlerMappings方法,后面的文章会讲到

initHandlerAdapters,initHandlerExceptionResolvers等。

具体代码如下(省略部分代码):

private void initHandlerMappings(ApplicationContext context) {
    this.handlerMappings = null;

    if (this.detectAllHandlerMappings) { // true
      Map<String, HandlerMapping> matchingBeans =
          BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);

      this.handlerMappings = new ArrayList<>(matchingBeans.values());
    }
    else {
        HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
        this.handlerMappings = Collections.singletonList(hm);
    }

由以上代码可以知道,它会去Spring IOC容器中获取HandlerMapping类型的所有Bean设置为控制器的Handler,而上文说的RequestMappingHandlerMapping就是其中一个Bean。与此同时我们发现,也可以通过自己手写一个HandlerMapping实现类从而注册到控制器当中去。

最后用流程图总结一下这个过程:
image.png


小燃儿
110 声望2 粉丝

java developer.