所有分类
  • 所有分类
  • 未分类

SpringMVC的请求流程

简介

本文介绍SpringMVC的请求流程,分为如下部分:流程概述、流程源码追踪、接口注册的原理。

流程概述

流程图

在使用@ResponseBody时,是不经过视图解析器和视图渲染的。 

  • DispatcherServlet:作为前端控制器,整个流程控制的中心,控制其它组件执行,统一调度,降低组件之间的耦合性,提高每个组件的扩展性。
  • HandlerMapping:通过扩展处理器映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
  • HandlerAdapter:通过扩展处理器适配器,支持更多类型的处理器。例如:HTTP、Websocket、beanName
  • ViewResolver:通过扩展视图解析器,支持更多类型的视图解析。例如:jsp、freemarker、pdf、excel等。

组件详解

1.前端控制器DispatcherServlet。(不需要工程师开发,由框架提供)

作用:接收请求,响应结果,相当于转发器,中央处理器。有了dispatcherServlet减少了其它组件之间的耦合度。

详解:用户请求到达前端控制器,它就相当于mvc模式中的c。dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性。

2.处理器映射器HandlerMapping。(不需要工程师开发,由框架提供)

作用:根据请求的url和方法(GET, POST等)查找Handler。

详解:HandlerMapping负责根据用户请求找到Handler即处理器,SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。

3.处理器适配器HandlerAdapter。(不需要工程师开发,由框架提供)

作用:按照特定规则(HandlerAdapter要求的规则)去执行Handler。

详解:通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。

4.处理器Handler。(需要工程师开发)

注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以去正确执行Handler。

Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。

由于Handler涉及到具体的用户业务请求,所以一般情况需要工程师根据业务需求开发Handler。

5.视图解析器View resolver。(不需要工程师开发,由框架提供)

作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)

View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。 springmvc框架提供了很多的View视图类型,包括:jstlView、freemarkerView、pdfView等。

一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由工程师根据业务需求开发具体的页面。

6.视图View。(需要工程师开发)

View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…)

流程追踪

创建SpringBoot的web工程

创建controller: 

package com.example.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/test1")
    public String test1() {
        return "test1 success";
    }
}

堆栈总信息

 到箭头位置看:

追踪mappedHandler

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

getHandler //DispatcherServlet

可以得出

  1. RequestMappingHandlerMapping里边保存了接口的信息
  2. 返回的HandlerExecutionChain中包含了我们的接口对应的方法:HelloController#test1()    

追踪mapping.getHandler(request)

mapping.getHandler(request) //DispatcherServlet
    getHandler(HttpServletRequest request) //AbstractHandlerMapping
        Object handler = getHandlerInternal(request); //AbstractHandlerMapping
            getHandlerInternal(HttpServletRequest request) //RequestMappingInfoHandlerMapping
                getHandlerInternal(HttpServletRequest request) //AbstractHandlerMethodMapping
                    // 以下在:AbstractHandlerMethodMapping
                    HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
                        this.mappingRegistry.getMappingsByUrl(lookupPath);
                            getMappingsByUrl(lookupPath) //AbstractHandlerMethodMapping$MappingRegistry
                                this.urlLookup.get(urlPath);
                    // 以下在:HandlerMethod
                    handlerMethod.createWithResolvedBean()
                        // beanFactory:DefaultListableBeanFactory
                        // beanName:”helloController”
                        Object handler = this.beanFactory.getBean(beanName);
                        // this:里边有个Method成员,为:HellloController#test1()
                        return new HandlerMethod(this, handler);
                        
        // AbstractHandlerMapping
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request); 
            HandlerExecutionChain chain = new HandlerExecutionChain(handler));
            对于this.adaptedInterceptors的每一项
                chain.addInterceptor(interceptor);

 上边,加粗项很重要,里边保存了url与controller的方法的对应关系。

AbstractHandlerMethodMapping$MappingRegistry

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    class MappingRegistry {
        // RequestMappingInfo -> MappingRegistration<RequestMappingInfo>
        private final Map<T, MappingRegistration<T>> registry = new HashMap<>();

        // RequestMappingInfo -> HandlerMethod
        private final Map<T, HandlerMethod> mappingLookup = new LinkedHashMap<>();

        // 不包含通配符的url -> List<RequestMappingInfo>
        // 为什么是一个List呢?因为一个url可能对应多个方法,即:@RequestMapping注解path属性一样,但其他属性不一样
        private final MultiValueMap<String, T> urlLookup = new LinkedMultiValueMap<>();

        // 映射名 -> HandlerMethod
        private final Map<String, List<HandlerMethod>> nameLookup = new ConcurrentHashMap<>();

        // HandlerMethod -> 跨域配置
        private final Map<HandlerMethod, CorsConfiguration> corsLookup = new ConcurrentHashMap<>();

        // 其他代码
    }
}

本处对应的真实数据为: 

 AbstractHandlerMethodMapping$MappingRegistration

public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
    private static class MappingRegistration<T> {

        private final T mapping;

        private final HandlerMethod handlerMethod;

        private final List<String> directUrls;

        @Nullable
        private final String mappingName;
        
        //...
    }
}

这里的T是:RequestMappingInfo 

原理

启动时注册

启动时将@RequestMapping注册到HandlerMapping

根据请求获得处理器

根据处理器获得适配器

HandlerAdapter使用的是适配器模式,@RequestMapping对应的是RequestMappingHandlerAdapter

2

评论1

请先

  1. 1. 用户发起请求,请求先被 Servlet 拦截转发给 Spring MVC 框架 2. Spring MVC 里面的 DispatcherSerlvet 核心控制器,会接收到请求并转发给HandlerMapping 3. HandlerMapping 负责解析请求,根据请求信息和配置信息找到匹配的 Controller类,不过这里如果有配置拦截器,就会按照顺序执行拦截器里面的 preHandle方法 4. 找到匹配的 Controller 以后,把请求参数传递给 Controller 里面的方法 5. Controller 中的方法执行完以后,会返回一个 ModeAndView,这里面会包括视图名称和需要传递给视图的模型数据 6. 视图解析器根据名称找到视图,然后把数据模型填充到视图里面再渲染成 Html 内容返回给客户端
    summerzZ 2024-06-24 3
显示验证码
没有账号?注册  忘记密码?

社交账号快速登录