首页 > 其他分享 >springboot开发日记(12)——请求映射原理

springboot开发日记(12)——请求映射原理

时间:2023-02-13 17:15:06浏览次数:40  
标签:12 springboot 映射 request handler 方法 response 请求

请求映射原理

由于springboot底层使用的是springMVC,所以研究请求映射原理我们需要从DispatcherServlet入手,搜索DispatcherServlet这个包,我们可以发现他是一个继承类,打开继承树我们可以发现上面还有HttpServlet、HttpServletBean、FrameworkServlet这几个父类,我们不妨从最顶层看起。

HttpServletBean继承于HttpServlet,本质也是servlet,所以我们需要研究他的doGet()、doPost()等方法。但是经过搜索发现并没有这些方法,于是我们去子类中寻找。

在FrameworkServlet搜索doGet(),我们可以发现成功找到了这一方法:

protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.processRequest(request, response);
}

发现他和其他do方法都调用或间接调用了processRequest这一方法,我们再去找这个方法:

protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    long startTime = System.currentTimeMillis();
    Throwable failureCause = null;
    LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
    LocaleContext localeContext = this.buildLocaleContext(request);
    RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes requestAttributes = this.buildRequestAttributes(request, response, previousAttributes);
    WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
    asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
    this.initContextHolders(request, localeContext, requestAttributes);

    try {
        this.doService(request, response);
    } catch (IOException | ServletException var16) {
        failureCause = var16;
        throw var16;
    } catch (Throwable var17) {
        failureCause = var17;
        throw new NestedServletException("Request processing failed", var17);
    } finally {
        this.resetContextHolders(request, previousLocaleContext, previousAttributes);
        if (requestAttributes != null) {
            requestAttributes.requestCompleted();
        }

        this.logResult(request, response, (Throwable)failureCause, asyncManager);
        this.publishRequestHandledEvent(request, response, startTime, (Throwable)failureCause);
    }

}

前面的初始化过程我们跳过,找到在try下的doService:

protected abstract void doService(HttpServletRequest request, HttpServletResponse response) throws Exception;

这是一个抽象方法,说明我们需要去子类中寻找他具体怎么实现的,于是继续查看他的子类:DispatcherServlet

在DispatcherServlet中找到了doService的实现方法(完整代码过长暂且不表)

我们仍然找在try下的doDispatch

try {
    this.doDispatch(request, response);

找到这个方法,发现这里是最底层的处理方法,每个请求都会调用这个方法。 最后整理,结构如下图:

 

为了更好的研究这个方法是怎么工作的,我们打一个断点进行debug测试。在网站成功启动后,我们点击post测试按钮,此时后台变量中request显示是由/user发送过来的请求,我们接着步过进行测试,找到mappedHandler = this.getHandler(processedRequest);这个函数,我们继续步过这个方法,发现此时mappedHandler这个变量中的handler的值变为了helloController中的post方法调用的函数。所以这个函数的作用是决定哪一个controller方法可以处理当前请求。

mappedHandler = this.getHandler(processedRequest);这个函数打上断点并步入,我们发现

他调用了一个handlerMappings的数组,负责处理处理器映射,此时里面有5项

 我们打开第二个WelcomePageHandlerMapping

发现pathMatcher里面是根据字符来匹配,在欢迎页这一HandlerMapping中"/" 代表直接访问路径是"/"时,会调用这个处理器,此时的view是index.html,也就是欢迎页。从而实现首页的访问。

RequestMappingHandlerMapping:保存所有@RequestMapping和handler的映射规则。

springMVC启动时对所有Controller和解析的注解都保存到handlerMapping中。然后通过循环将所有的映射进行遍历寻找谁能够处理该请求。

while(var2.hasNext()) {
    HandlerMapping mapping = (HandlerMapping)var2.next();
    HandlerExecutionChain handler = mapping.getHandler(request);
    if (handler != null) {
        return handler;
    }
}

接着步过进入第一次循环,也就是遍历RequestMappingHandlerMapping,此时再看mapping这个变量

 

可以发现,mappingRegistry这个属性里面已经有了所有的类以及能处理的请求。

继续步入步过,直到执行到这个方法

lookuppath就是当前要找的路径,request则是原生的请求。这个方法里有以下语句:

在mappingRegistry这个属性中通过要找的路径进行寻找。从上文我们知道mappingRegistry中有所有的类,根据我们需要的路径进行第一次筛选,继续步过。

我们可以看到,此时directPathMatches里面有4个对象,路径都是/user,只是请求方式不一样。接着运行

if (directPathMatches != null) {
    this.addMatchingMappings(directPathMatches, matches, request);
}

其中,addMatchingMappings的作用:

首先对拿到的映射数组遍历,根据顺序一个个匹配,看那个合适,就用那个。

直到找到了可以处理请求的mapping后,就执行add方法,把它加到matcher数组里面

所以我们会得到一个拥有所有匹配结果的集合matches,经过判空等操作后得到的matches如果有多个元素,则会自动选择第一个元素作为最佳选项。元素中如果有多个方法,即对同一个请求有多个方法进行处理时,会报错。因此springboot要求对一个请求只能有一个处理方法。

所有的请求映射都在HandlerMapping中。

SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问到index.html;

SpringBoot自动配置了默认 的 RequestMappingHandlerMapping

请求进来,尝试所有的HandlerMapping看是否有请求信息。如果有就找到这个请求对应的handler,如果没有就是下一个 HandlerMapping。

 

 

标签:12,springboot,映射,request,handler,方法,response,请求
From: https://www.cnblogs.com/tarorat/p/17115608.html

相关文章