这样深度剖析SpringMVC的执行流程,看完帮你立即提升一个台阶!

前言:

目录

1.DispatcherServlet 前端控制器

3.HandlerAdapter 处理器适配器

4.ViewResolver 视图解析器

5.View 视图对象

1.前端控制器拦截用户的请求

3.获取处理器适配器HandlerAdpater

4.处理器适配器对Handler进行处理

5.处理派发结果

​​​​​​​

 SpringMVC执行需要的几个核心组件

在深入分析SpringMVC执行流程之前,首先我们需要认识SpringMVC执行的过程中需要用到的几个核心组件。因为SpringMVC整个执行流程都是围绕这几个核心组件去实现的。现在文哥就带领大家认识一下,SpringMVC执行需要用到的核心组件有哪些。

1.DispatcherServlet 前端控制器

前端控制器是整个SpringMVC执行流程的核心。我们通过前端控制器来接收前端用户发送的请求,并通过前端控制器将请求交给各个组件来处理。我们在搭建SpringMVC入门环境的时候,是配置了前端控制器的。小伙伴们还记不记得在哪里配置的?就是在web.xml里面配置的。

2.HandlerMapping 处理器映射器

当前端控制器接收了用户的请求之后,前端控制器自己是不会对用户的请求进行处理的。而是将用户的请求交给处理器映射器进行处理。处理器映射器会对用户的请求进行处理,处理成Handler。之后处理器映射器会将处理好的Handler的返回给前端控制器。在SpringMVC中,官方将处理器映射器设计成了一个接口。给大家贴出官方的部分源码: 

public interface HandlerMapping {
    String PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE = HandlerMapping.class.getName() + ".pathWithinHandlerMapping";
    String BEST_MATCHING_PATTERN_ATTRIBUTE = HandlerMapping.class.getName() + ".bestMatchingPattern";
    String INTROSPECT_TYPE_LEVEL_MAPPING = HandlerMapping.class.getName() + ".introspectTypeLevelMapping";
    String URI_TEMPLATE_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".uriTemplateVariables";
    String MATRIX_VARIABLES_ATTRIBUTE = HandlerMapping.class.getName() + ".matrixVariables";
    String PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE = HandlerMapping.class.getName() + ".producibleMediaTypes";

    @Nullable
    HandlerExecutionChain getHandler(HttpServletRequest var1) throws Exception;
}

为什么设计成一个接口呢?大家思考一下,其实这样设计的原因就是我们可以实现多种不同类型的处理器映射器,在不同的场合,选择最适合的处理器映射器进行处理。

3.HandlerAdapter 处理器适配器

当处理器映射器将请求处理之后,将处理好的Handler返回给前端控制器。前端控制器自己不会进一步的处理,而是将Handler交给处理器适配器HandlerAdapter来进行进一步的处理。处理器适配器会将Handler处理成ModelAndView逻辑视图。在SpringMVC源码中,SpringMVC官方也是将处理器适配器设计成了接口,我们贴出部分源码给大家展示:

public interface HandlerAdapter {
    boolean supports(Object var1);

    @Nullable
    ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;

    long getLastModified(HttpServletRequest var1, Object var2);
}

为什么设计成接口,和处理器映射器一样,我们可以实现很多种不用类型的处理器适配器。这样在不同的场合下面,我们可以使用最匹配的处理器适配器对Handler进行处理。

4.ViewResolver 视图解析器

当处理器适配器将Handler处理成ModelView逻辑视图以后,处理器适配器会将ModelAndView返回给前端控制器,此时前端控制器会将ModelAndView交给视图解析器进行进一步解析,最终解析成视图进行渲染。我们在搭建SpringMVC环境的时候,会在SpringMVC的配置文件springmvc.xml里面对视图解析器进行配置,配置如下:

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/pages/"></property>
    <property name="suffix" value=".jsp"></property>
</bean>

在官方源码中,SpringMVC官方也是将视图解析器设计成了一个接口,文哥给大家贴出官方给出的部分源码:

public interface ViewResolver {
    @Nullable
    View resolveViewName(String var1, Locale var2) throws Exception;
}

5.View 视图对象

当视图解析器将ModelAndView进行解析,解析成View之后。视图需要进行渲染才会真正的被用户看到。所以在SpringMVC源码里面,SpringMVC官方也把View设计成了一个接口,这样可以实现各种不同类型的视图,可以根据用户的需求,对视图进行不同格式的渲染。文哥给大家贴出官方给出的部分源码:

public interface View {
    String RESPONSE_STATUS_ATTRIBUTE = View.class.getName() + ".responseStatus";
    String PATH_VARIABLES = View.class.getName() + ".pathVariables";
    String SELECTED_CONTENT_TYPE = View.class.getName() + ".selectedContentType";

    @Nullable
    String getContentType();

    void render(@Nullable Map<String, ?> var1, HttpServletRequest var2, HttpServletResponse var3) throws Exception;
}
到目前为止,文哥就把SpringMVC执行所需要的几个关键的组件给大家介绍清楚了。接下来文哥带领大家从源码级别分析SpringMVC的执行流程。

SpringMVC执行流程源码分析


我们以上篇文章给大家编写的案例切入点,来分析其整个执行流程。上篇文章中我们编写的开控制器代码如下:

@Controller
public class HelloController {

    @RequestMapping(value = "hello") //映射url请求
    public String hello(){
        return "success";
    }
}

我们通过发送 localhost:8080/hello来进行解析。全程文哥会给大家打断点演示整个流程,让各位小伙伴们非常直观的了解SpringMVC执行的各个步骤和细节。


1.前端控制器拦截用户的请求

我们首先查看前端控制器的继承体系,我们发现前端控制器就是一个Servlet。我们打开前端控制的继承图:

我们发现前端控制器DispatcherServlet间接的继承了Servlet接口,所以我们认为前端控制器就是一个Servlet!在javaweb中我们学习了Servlet,有一个名为service的方法。这个方法就是提供服务的方法,也就是接收并处理用户的操作都是在service方法中实现的。在SpringMVC中,也需要前端控制器这个Servlet来接收并处理用户的请求,这个方法是什么呢,是一个叫doService的方法进行处理的。

在doService方法中有一个核心方法对请求进行进一步的处理:

通过源码解析,我们发现用户的请求进一步的交给前端控制器中的一个叫doDispatcher的方法进行处理的。我们通过debugg模式启动项目,浏览器发送请求。通过打断点的方式,判断用户的请求有没有交给doDispatcher方法进行处理:

经过实测,我们发现用户的请求确实交给前端控制器的doDispatcher方法进行拦截了。

2.处理器映射器执行用户的请求 

通过上节内容发现,用户的请求被前端控制器的doDispatcher方法进行拦截了。我们使用F8放行断点,将断点放行到如下代码:

这句代码有什么作用呢?就是使用处理映射器对用户的请求进行进一步处理。如何处理的呢,我们按F7快捷键进入到当前方法的内部:

我们发现处理器映射器处理用户请求的整个流程就是,首先获取所有的处理器映射器,然后循环遍历所有的处理器映射器,来对用户的请求进行处理,获取对应的Handler。有小伙伴可能就要问了,SpringMVC初始化了几个处理器映射器呢?

 

通过源码发现,最终是RequestMappingHandlerMapping处理器映射器帮助我们处理了请求。处理完的结果封装到了HandlerExecutionChain里面:

我们发现处理器映射器给我们处理的Handler封装到了一个叫HandlerExecutionChain里面。而在HandlerExecutionChain对象里面有一个handler对象,是HandlerMethod类型的,这就是处理器映射器最终将我们的请求处理成的Handler对象!

3.获取处理器适配器HandlerAdpater

通过上节内容,我们印证了用户的请求被处理器映射器RequestMappingHandlerMapping处理成了Handler对象。接下来我们继续使用F8快捷键放行我们的代码,将代码放行至如下:

这句代码的作用是什么呢?通过方法名称我们都可以猜出,就是获取处理器适配器,为Handler的处理做准备。到底如何获取HandlerApater的?我们使用F7快捷键进入getHandlerAdapter方法的内部:

通过以上源码,我们发现,处理器适配器的获取原理就是循环遍历所有的处理器适配器,然后判断当前处理器适配器是否支持适配处理,如果支持,就返回给处理器适配器。这里用到了经典的设计模式,那就是适配器模式。SpringMVC为我们初始化了以下3个处理器适配器:

这些适配器全部都实现类HandlerApater接口:

public interface HandlerAdapter {
    boolean supports(Object var1);

    @Nullable
    ModelAndView handle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;

    long getLastModified(HttpServletRequest var1, Object var2);
}

这里面有两个非常重要的方法,文哥给大家解释一下:

  • supports方法 判断处理器是否支持对Handler进行适配的方法,返回值为布尔值。true代表支持,false代表不支持。

  • handle方法 处理器适配器对Handler对象进行处理的方法,返回值是ModelAndView逻辑视图。

到底是哪个处理器适配器支持对当前的Handler进行适配的呢?通过源码追踪我们发现这个适配器就是RequestMappingHandlerAdapter。

4.处理器适配器对Handler进行处理

 上节内容,我们发现SpringMVC首先帮助我们找到最适合的处理器适配器,为Handler的进一步处理做准备。我们继续使用F8快捷键放行断点到如下位置:

这句代码的意思就是获取当前Handler对象,使用处理器适配器RequestMappingHandlerAdapter进行处理,最终将Handler对象处理成ModelAndView。如何处理的?我们使用F7快捷键进入到handle方法内部:

我们发现handle方法内部调用了handleInternal方法对Handler进行进一步处理,我们使用F7快捷键继续进入当前方法内部:

我们放行断点,发现在handleInternal方法内部又调用了invokeHandlerMethod方法。这个方法对Handler进行处理,最终返回ModelAndView。invokeHandlerMethod是如何处理我们的Handler的呢,我们继续进入到这个方法的内部:


 这个方法就是处理Handler的方法,我们使用F7进入方法内部,查看具体的处理细节:

继续进入invokeForRequest方法内部:

通过以上源码执行流程我们发现,处理器适配器处理我们的Handler就是基于反射的机制进行处理的!最后将处理结果返回成ModelAndView。

5.处理派发结果

通过上节内容,我们发现,SpringMVC通过处理器适配器将Handler处理成ModelAndView了。接下来我们接续放行断点到如下位置:

 

其实就是将我们的逻辑视图ModelAndView进行进一步的处理,如何处理呢?我们进入到这个方法内部去查看细节:

我们发现,有一个叫render的方法对逻辑视图进行处理,我们进入到这个方法内部,看看具体处理的流程:

那么View视图对象是如何获取到的呢,肯定使用的是视图解析器帮助我们获取的。我们进入到resolveViewName方法的内部,查看具体实现细节:

我们通过源码发现,视图解析器的获取还是循环遍历初始化好的视图解析器进行解析处理,最终得到一个View视图对象。这个视图解析器就是我们在springmvc.xml配置文件里面配置好的视图解析器InternalResourceViewResolver。处理器适配器最终处理好的View对象是InternalResourceView类型的。

当我们使用视图解析器获取到View对象之后,到底如何处理View的呢,我们继续使用F8快捷键放行断点:

我们使用F7快捷键进入到这个方法内部:

public void render(@Nullable Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
    if (this.logger.isTraceEnabled()) {
        this.logger.trace("Rendering view with name '" + this.beanName + "' with model " + model + " and static attributes " + this.staticAttributes);
    }

    Map<String, Object> mergedModel = this.createMergedOutputModel(model, request, response);
    this.prepareResponse(request, response);
    this.renderMergedOutputModel(mergedModel, this.getRequestToExpose(request), response);
}

 其实就是通过一个Response对象将视图内容向浏览器进行响应,最后响应的结果就是用户看到的内容。到这里文哥就从源码的角度给大家演示了SpringMVC的执行流程。小伙伴们对SpringMVC的执行流程是不是有了更深刻的理解?

总结:

通过本篇文章,相信大家对SpringMVC的执行流程有了更深刻的理解。亲自去查阅官方源码,只有这样才能将SpringMVC的执行流程理解透彻。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ღ᭄陽先生꧔ꦿ᭄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值