Spring MVC执行流程解析

一、Spring MVC的执行流程

上面介绍了Spring MVC的使用,配置了一个DispatcherServlet作为所有请求的入口,DispatcherServlet继承自抽象类FrameworkServlet,而FrameworkServlet又继承自HttpServlet,所有当有请求进来时,会先进入到FrameworkServlet的service()方法中,而在该方法中又会去调用父类HttpServlet的service()方法,该类的方法在《从Servlet到Spring MVC》中已经介绍过了,它会根据不同的请求去调用doGet()、doPost()等方法

protected void service(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

   HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
   if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
      processRequest(request, response);
   }
   else {
      super.service(request, response);
   }
}

而FrameworkServlet中全部实现了这些doXxx()方法,而在这些doXxx()方法中,又都会去调用processRequest()方法

protected final void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

   processRequest(request, response);
}

/**
 * Delegate POST requests to {@link #processRequest}.
 * @see #doService
 */
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {

   processRequest(request, response);
}

在processRequest()方法中,除了加载一些上下文信息、绑定参数之后,最核心的就是去调用DispatcherServlet的doService()方法

protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContext localeContext = buildLocaleContext(request);

    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);

    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());

    initContextHolders(request, localeContext, requestAttributes);

    doService(request, response);
}

在doService()方法中,会设置一些请求的参数,但最核心的是去调用doDispatch()方法,而doDispatch()才是SpringMVC处理请求最核心的方法,下面介绍一下doDispatch()方法的执行流程,以@RequestMapping注解为例,这个也是我们开发中用的最多的

1.1 获取处理器执行链

在doDispatch()方法中,首先会去解析请求,得到一个处理器执行链,包含了拦截器集合和处理方法,而在映射的时候,就会用得到HandlerMapping,在《从Servlet到Spring MVC》中,介绍了如何配置HandlerMapping,如果没有配置,SpringMVC提供了四个默认的配置

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;

    // 进行映射
    mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null) {
        noHandlerFound(processedRequest, response);
        return;
    }

}

调用getHandler()方法就是去遍历所有的映射器处理器,看哪个能根据请求的url匹配到处理器方法

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        /** 拿到所有handlerMappings (容器启动阶段初始化:拿到所有实现了HandlerMapping的Bean)
       * @see DispatcherServlet#initHandlerMappings
       * 测试发现: 不同的HandlerMapping可以有相同path, 谁先解析到就用哪个
       * */
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}
1.1.1 获取处理器

调用getHandlerInternal()方法来获取处理器

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    Object handler = getHandlerInternal(request);
    if (handler == null) {
        handler = getDefaultHandler();
    }
    if (handler == null) {
        return null;
    }
	……
}

首先获取request请求中的url,然后调用lookupHandlerMethod()去匹配

protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
    // 通过UrlPathHelper对象,用于来解析从们的request中解析出请求映射路径
    String lookupPath = initLookupPath(request);
    this.mappingRegistry.acquireReadLock();
    try {
        // 通过lookupPath解析最终的handler——HandlerMethod对象
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
        return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
    }
    finally {
        this.mappingRegistry.releaseReadLock();
    }
}

在lookupHandlerMethod()中,根据url来匹配mappingRegistry.pathLookup,pathLookup是一个MultiValueMap,它最大的特点是value可以重复,所以同一个url可能会匹配到多个RequestMappingInfo

pathLookup的数据,是在SpringMVC容器启动的时候,就回去加载解析的,以@RequestMapping注解为例,在Bean实例化的过程中,就回去解析类中的方法是否有@RequestMapping注解,然后拼接url作为pathLookup的key,将类以及方法封装成RequestMappingInfo

如果通过pathLookup找到了url相匹配的处理器,这个时候还是不够,还需要去解析@RequestMapping注解中的mthod、header等属性是否匹配,RequestMappingInfo中包含了@RequestMapping注解所有配置的条件匹配器,比如ParamsRequestCondition、HeadersRequestCondition等

protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
    List<Match> matches = new ArrayList<>();
    // pathLookup<path,RequestMappingInfo>会在初始化阶段解析好
    List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
    if (directPathMatches != null) {
        // 如果根据path能直接匹配的RequestMappingInfo 则用该mapping进行匹配其他条件(method、header等)
        addMatchingMappings(directPathMatches, matches, request);
    }
    if (matches.isEmpty()) {
        // 如果无path匹配,用所有的RequestMappingInfo  通过AntPathMatcher匹配
        addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
    }
    ……
}

如果直接通过url没有在pathLookup找到,则会去调用getMatchingMapping()方法,通过pathMatcher来匹配,pathMatcher是一个AntPathMatcher类的实例,提供了按照通配符? * {匹配的逻辑

如果匹配到多个按照? > * > {} >**进行排序,然后取最匹配的那一个

protected String getMatchingMapping(String pattern, HttpServletRequest request) {
   String lookupPath = this.pathHelper.getLookupPathForRequest(request);
   String match = (this.pathMatcher.match(pattern, lookupPath) ? pattern : null);
   if (match != null) {
      this.matches.add(match);
   }
   return match;
}
1.1.2 封装处理器链

得到处理器之后,还需要取获取配置的拦截器,然后封装成一个执行器链

public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    Object handler = getHandlerInternal(request);
    if (handler == null) {
        handler = getDefaultHandler();
    }
    if (handler == null) {
        return null;
    }
	……
    HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
}

调用getHandlerExecutionChain(),首先将处理器封装成一个HandlerExecutionChain,然后遍历配置的所有拦截器,只有与当前处理器匹配的,就加入到HandlerExecutionChain的interceptorList中,在执行执行器的方法前后,会调用拦截器的方法

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

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

1.2 获取处理器适配器

获取完处理器之后,就要为处理器匹配最合适的适配器,那么适配器是干嘛的,简单的来说就是解析参数的。

以@RequestMapping为例,它的方法参数,可以通过@RequestBody、@RequestParam等注解来获取参数,那么肯定就要有能够解析这些注解的适配器

如果没有通过Spring MVC的配置文件进行配置,默认有HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter、RequestMappingHandlerAdapter和HandlerFunctionAdapter四种

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HttpServletRequest processedRequest = request;
    HandlerExecutionChain mappedHandler = null;

    // 进行映射
    mappedHandler = getHandler(processedRequest);
    if (mappedHandler == null) {
        noHandlerFound(processedRequest, response);
        return;
    }
    // 找到最合适的HandlerAdapter
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

}

寻找最合适的处理器适配器也很简单,遍历所有的是适配器,然后调用它们的support()方法进行匹配

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
   if (this.handlerAdapters != null) {
      for (HandlerAdapter adapter : this.handlerAdapters) {
         if (adapter.supports(handler)) {
            return adapter;
         }
      }
   }
}

supports()的方法也很简单,以HttpRequestHandlerAdapter为例:

只需要判断当前处理器是不是HttpRequestHandler的实现类即可,而RequestMappingHandlerAdapter的supports()方法永远返回true

public boolean supports(Object handler) {
   return (handler instanceof HttpRequestHandler);
}

1.3 执行处理器方法

在执行处理器的方法前,首先会去执行拦截器的前置方法

// 前置拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
    // 返回false就不进行后续处理了
    return;
}

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    for (int i = 0; i < this.interceptorList.size(); i++) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        if (!interceptor.preHandle(request, response, this.handler)) {
            triggerAfterCompletion(request, response, null);
            return false;
        }
        this.interceptorIndex = i;
    }
    return true;
}

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
    for (int i = this.interceptorIndex; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        interceptor.afterCompletion(request, response, this.handler, ex);
    }
}

调用处理器的handle()方法,然后调用invokeHandlerMethod()方法设置一些常用的配置,比如:请求参数解析器、返回参数解析器、数据绑定器等,然后调用ServletInvocableHandlerMethod的invokeAndHandle()方法,执行处理器方法

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    ModelAndView mv = null;
    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    ……
}

protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                           HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
    // 把我们的请求req resp包装成 ServletWebRequest
    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    // 获取容器中全局配置的InitBinder和当前HandlerMethod所对应的Controller中
    // 配置的InitBinder,用于进行参数的绑定
    WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);

    // 获取容器中全局配置的ModelAttribute和当前HandlerMethod所对应的Controller 中配置的ModelAttribute,
    // 这些配置的方法将会在目标方法调用之前进行调用
    ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);

    // 封装handlerMethod,会在调用前解析参数、调用后对返回值进行处理
    ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
    if (this.argumentResolvers != null) {
        // 让invocableMethod拥有参数解析能力
        invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
    }
    if (this.returnValueHandlers != null) {
        // 让invocableMethod拥有返回值处理能力
        invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
    }
    // 让invocableMethod拥有InitBinder解析能力
    invocableMethod.setDataBinderFactory(binderFactory);
    // 设置ParameterNameDiscoverer,该对象将按照一定的规则获取当前参数的名称
    invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
    // ModelAndView处理容器
    ModelAndViewContainer mavContainer = new ModelAndViewContainer();
    // 将request的Attribute复制一份到ModelMap
    mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
    // *调用我们标注了@ModelAttribute的方法,主要是为我们的目标方法预加载
    modelFactory.initModel(webRequest, mavContainer, invocableMethod);
    // 重定向的时候,忽略model中的数据 默认false
    mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

    // *对请求参数进行处理,调用目标HandlerMethod,并且将返回值封装为一个ModelAndView对象
    invocableMethod.invokeAndHandle(webRequest, mavContainer);

    // 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
    // 还会判断是否需要将FlashAttributes封装到新的请求中
    return getModelAndView(mavContainer, modelFactory, webRequest);
}
1.1.1 请求参数解析

在执行处理器方法之前,需要先解析参数,得到处理器方法参数列表的参数值

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                               Object... providedArgs) throws Exception {
    //*获取我们目标方法入参的值
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    ……
}

参数解析的时候,先获取方法的参数列表,然后遍历这些参数,在遍历这些参数的时候,需要添加ParameterNameDiscoverer对象,才能得到参数的名称

然后获取所有的参数解析器,判断是否可以解析当前参数,以RequestParamMethodArgumentResolver参数解析器为例,就是判断当前参数是否有@RequestParam注解,如果可以解析,就直接调用resolveArgument()进行解析和数据绑定

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                                           Object... providedArgs) throws Exception {
    // 获取目标方法参数的描述数组对象
    MethodParameter[] parameters = getMethodParameters();
    //用来初始化我们对应参数名称的参数值得数组
    Object[] args = new Object[parameters.length];
    //循环我们得参数名数组
    for (int i = 0; i < parameters.length; i++) {
        MethodParameter parameter = parameters[i];
        //为我们得MethodParameter设置参数名称探测器对象
        parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
        args[i] = findProvidedArgument(parameter, providedArgs); // providedArgs
        if (args[i] != null) {
            continue;
        }
        // * 获取所有的参数解析器,然后筛选出合适的解析器
        if (!this.resolvers.supportsParameter(parameter)) {
            throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
        }
        // 通过上面筛选的 参数解析器来解析我们的参数
        args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
    }
    return args;
}
1.1.2 执行处理器方法

解析完参数之后,利用反射,调用处理器的方法

public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
                               Object... providedArgs) throws Exception {
    //*获取我们目标方法入参的值
    Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    if (logger.isTraceEnabled()) {
        logger.trace("Arguments: " + Arrays.toString(args));
    }
    //真的的调用我们的目标方法
    return doInvoke(args);
}
1.1.3 返回参数解析

执行完处理器的方法之后,会得到返回值,返回值可能是Json,也可以能是ModelAndView,把返回值封装在ModelAndViewContainer中

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
                            Object... providedArgs) throws Exception {

    /*真正的调用我们的目标对象 很重要 很重要*/
    Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
    ……
    // 遍历当前容器中所有ReturnValueHandler,判断哪种handler支持当前返回值的处理,
    // 如果支持,则使用该handler处理该返回值
    this.returnValueHandlers.handleReturnValue(
        returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}

最后,调用getModelAndView()处理之前的ModelAndViewContainer,封装成一个ModelAndView对象

// 对封装的ModelAndView进行处理,主要是判断当前请求是否进行了重定向,如果进行了重定向,
// 还会判断是否需要将FlashAttributes封装到新的请求中
return getModelAndView(mavContainer, modelFactory, webRequest);

1.4 视图解析

执行完处理器方法之后,接着是去执行所有拦截器的postHandle()方法

mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

// 如果mv有  视图没有,给你设置默认视图
applyDefaultViewName(processedRequest, mv);
//后置拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
    throws Exception {

    for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
        HandlerInterceptor interceptor = this.interceptorList.get(i);
        interceptor.postHandle(request, response, this.handler, mv);
    }
}

最后才是去渲染视图,在render()方法中,调用resolveViewName()方法去解析视图,然后返回一个View对象,最后调用View的render()方法去渲染视图

// 渲染视图
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
                                   @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
                                   @Nullable Exception exception) throws Exception {
    if (mv != null && !mv.wasCleared()) {
        // 解析、渲染视图
        render(mv, request, response);
        if (errorView) {
            WebUtils.clearErrorRequestAttributes(request);
        }
    }
}

protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
    // Determine locale for request and apply it to the response.
    Locale locale =
        (this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
    response.setLocale(locale);
    View view;
    String viewName = mv.getViewName();
    if (viewName != null) {
        // 解析视图名
        view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
    }
    if (mv.getStatus() != null) {
        response.setStatus(mv.getStatus().value());
    }
    view.render(mv.getModelInternal(), request, response);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值