Spring MVC源码——初始化及请求处理

5 篇文章 0 订阅
2 篇文章 0 订阅

Spring MVC源码——初始化及请求处理

本文将从SpringMVC的初始化如何处理一个Get请求来介绍源码。

Spring MVC的初始化

基于对tomcat和javaweb的认识, web容器在初始化时将会调用Servlet提供的init方法。从这个角度入手,再加上我们对SpringMVC使用时配置web.xml的经验,可以得知我们阅读源码要熟悉的第一个类就是DispatcherServlet

 <servlet>
   <servlet-name>springMVC</servlet-name>
   <!--基于springmvc的web.xml配置所依赖的处理器-->
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
   <init-param>
     <param-name>contextConfigLocation</param-name>
     <param-value>classpath:springMVC.xml</param-value>
   </init-param>
   <load-on-startup>1</load-on-startup>
 </servlet>

DispatcherServlet

DispatcherServlet作为HttpServlet的子类,我们不难想到在容器初始化时其init方法将会被调用,而经过查找可以发现, 初始化时调用的init()方法位于其父类HttpServletBean中:

 @Override
 public final void init() throws ServletException {
 
    // 省略部分代码...
 
    initServletBean();
 }

这里省略部分代码,只看最后一行的调用,发现其调用了子类FrameworkServlet重写的initServletBean方法:

 @Override
 protected final void initServletBean() throws ServletException {
    // 省略...
 
    try {
       this.webApplicationContext = initWebApplicationContext();
       initFrameworkServlet();
    }
    // 省略...
 }

这里只需要关注**initWebApplicationContext()**方法即可:

 protected WebApplicationContext initWebApplicationContext() {
    // 省略...  
    if (wac == null) {
       wac = createWebApplicationContext(rootContext);
    }
    // 省略...
    return wac;
 }

这里关注一下WebApplicationContext的创建过程(createWebApplicationContext方法):

 protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {
    return createWebApplicationContext((ApplicationContext) parent);
 }
 
 protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
     // 省略...
     
     configureAndRefreshWebApplicationContext(wac);
 
     return wac;
 }

然后接下来是configureAndRefreshWebApplicationContext方法

 protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
     // 省略...
  
     // 这里添加了一个监听
     wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
     
     // 省略...
     
     postProcessWebApplicationContext(wac);
     applyInitializers(wac);
     wac.refresh();
 }

这里主要做两件事:

  1. 添加一个事件监听ContextRefreshListener
  2. 调用ApplicationContext的refresh方法

这里还是先看最后一行,一个比较熟悉的方法——refresh()。没错,这里要初始化Spring上下文了,下面是几个很熟悉的代码,这里就不详细介绍了,后面讨论Spring源码的时候会讲:

 public void refresh() throws BeansException, IllegalStateException {
     synchronized(this.startupShutdownMonitor) {
         this.prepareRefresh();
         ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
         this.prepareBeanFactory(beanFactory);
 
         try {
             this.postProcessBeanFactory(beanFactory);
             this.invokeBeanFactoryPostProcessors(beanFactory);
             this.registerBeanPostProcessors(beanFactory);
             this.initMessageSource();
             this.initApplicationEventMulticaster();
             this.onRefresh();
             this.registerListeners();
             this.finishBeanFactoryInitialization(beanFactory);
             this.finishRefresh();
         } catch (BeansException var9) {
             if (this.logger.isWarnEnabled()) {
                 this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
             }
 
             this.destroyBeans();
             this.cancelRefresh(var9);
             throw var9;
         } finally {
             this.resetCommonCaches();
         }
 
     }
 }

到这里可能会好奇,并没有看到SpringMVC相关类的初始化,这是为什么呢?

别急,前面还有一个事件监听没有关注。我们知道Spring在发布某些事件时,会由事件派发器去给相应的事件监听去派发(执行)事件,我们就从这里入手。

回到DispatcherServlet的父类FrameworkServlet中,其内部存在一个类ContextRefreshListener,作为一个事件监听存在,所以当ContextRefreshedEvent这个事件发布时, 其onApplicationEvent方法便会被触发。

ContextRefreshListener

 private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
 
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
       FrameworkServlet.this.onApplicationEvent(event);
    }
 }
 // org.springframework.web.servlet.FrameworkServlet#onApplicationEvent
 public void onApplicationEvent(ContextRefreshedEvent event) {
    this.refreshEventReceived = true;
    synchronized (this.onRefreshMonitor) {
       onRefresh(event.getApplicationContext());
    }
 }

深入FrameworkServlet的onApplicationEvent方法,最终会调用到由子类DispatcherServlet重写的onRefresh方法:

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

在initStrategies方法中,执行了一些列的初始化操作,内容如下:

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

各个组件的作用:

MultipartResolver:用于处理文件上传服务,如果有文件上传,那么就会将当前的HttpServletRequest包装成DefaultMultipartHttpServletRequest,并且将每个上传的内容封装成CommonsMultipartFile对象。

LocaleResolver:处理应用的国际化问题

ThemeResolver:主题解析

HandlerMapping:处理器映射器,负责将请求映射到具体的处理器。

HandlerAdapter:处理器适配器,调用具体的方法对用户发来的请求来进行处理。当HandleMapping获取到执行请求的Controller时,DispatcherServlet会根据controller对应的类型来调用相应的处理器适配器处理。

HandlerExceptionResolver:解析请求处理过程中所产生的异常。

RequestToViewNameTranslator:就是将请求的url解析成一个viewname的解析器。具体怎么个算法解析,可以自定义,也可以使用默认的实现类。当我们的controller的方法中,没有返回值时(只考虑返回值为jsp页面的名字,不考虑直接输出字符串到浏览器),那么便会调用Translator来获取一个默认的viewname,然后使用这个viewname的值,去jsp目录中找与之同名的jsp页面,如果还找不到,那就显示404了。

ViewResolver:是根据视图名和Locale解析出视图。在传统的web项目中,jsp,ftl等页面的渲染生成都是由ViewResolver来完成的。但是随着前后端分离项目的流行,该组件的使用已经越来越少。

FlashMapManager:FlashMapManager用于获取重定向之前的一些信息,FlashMap 借助 session 重定向前通过 FlashMapManager将信息放入FlashMap,重定向后 再借助 FlashMapManager 从 session中找到重定向后需要的 FalshMap。

以一个GET请求为例看请求的过程

这里重点关注请求处理的核心流程,所以只贴出部分代码。

FrameworkServlet.doGet

首先是FrameworkServlet的doGet方法:

 @Override
 protected final void doGet(HttpServletRequest request, HttpServletResponse response)
       throws ServletException, IOException {
 
    processRequest(request, response);
 }
 
 // org.springframework.web.servlet.FrameworkServlet#processRequest
 protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
             throws ServletException, IOException {
 
         // 省略...
     
         try {
             doService(request, response);
         }
         // 省略...
     }

这里的doService方法是一个抽象方法,具体的实现在DispatcherServlet中:

 @Override
     protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
     // 省略...
     try {
         doDispatch(request, response);
     }
     // 省略...
 }

这里只关注一个核心的处理逻辑,就是调用自己的doDispatch方法:

 //  Spring MVC的最核心代码
 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
     HttpServletRequest processedRequest = request;
     HandlerExecutionChain mappedHandler = null;
     boolean multipartRequestParsed = false;
 
     WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
 
     try {
         //创建视图对象
         ModelAndView mv = null;
         Exception dispatchException = null;
 
         try {
             // 检查是不是文件上传请求(根据contentType判断)
             processedRequest = checkMultipart(request);
             multipartRequestParsed = (processedRequest != request);
 
             // 把请求交给handlerMapping处理,获取处理器和拦截器
             mappedHandler = getHandler(processedRequest);
             if (mappedHandler == null) {
                 // 找不到的话就报异常
                 noHandlerFound(processedRequest, response);
                 return;
             }
             // 获取处理器适配器
             HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
             // 获取请求类型
             String method = request.getMethod();
             //get方法为true
             boolean isGet = "GET".equals(method);
 
             if (isGet || "HEAD".equals(method)) {
                 // 省略部分代码...
             }
             // 循环调拦截器对应的 preHandle 方法 如果有拦截器返回false这里就会返回
             if (!mappedHandler.applyPreHandle(processedRequest, response)) {   // ===> applyPreHandle
                 return;
             }
             // 这里通过处理器适配器去执行方法 通过反射调用
             mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 
 
             if (asyncManager.isConcurrentHandlingStarted()) {
                 return;
             }
             // 设置默认视图,当返回了mav,但是又没设置具体view的时候
             applyDefaultViewName(processedRequest, mv);  
             // 调用拦截器的后置方法 postHandle
             mappedHandler.applyPostHandle(processedRequest, response, mv); 
         } catch (Exception ex) {
             // 省略...
         }
         // 执行结果处理逻辑
         //1,请求视图解析器,解析成view 
         //2,执行页面渲染(jsp)
         //3,调用拦截器afterCompletion方法
         processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);  // ===> go!
     } catch (Exception ex) {
         // 省略...
     // ...
 }

这里大致流程如下:

  1. 判断是否文件上传请求,如果是的话就把请求封装成MultipartHttpServletRequest
  2. getHandler方法:通过HandlerMapping构建处理器:包含我们的Controller中的指定方法(HandlerMethod)和拦截器
  3. getHandlerAdapter方法:获取处理器适配器
  4. applyPreHandle方法:调用拦截器的preHandle方法,期间如果拦截器返回false就返回
  5. handle方法:调用处理器(也就是我们定义的Controller)的指定方法(反射)来处理请求
  6. applyPostHandle方法:调用拦截器的portHandle方法,期间如果拦截器返回false就返回
  7. processDispatchResult方法:渲染视图、调用拦截器afterCompletion方法

总结

最后汇总一下整个请求的处理流程

  1. tomcat监听的端口到http请求之后交给Serlvet处理
  2. DispatchServlet最终收到请求之后开始处理
  3. DispatcherServlet调用处理器映射器HandlerMapping根据路径参数来获取处理器(我们的Controller方法)和拦截器
  4. DispatcherServlet获取处理器适配器用于后续执行处理器方法
  5. DispatcherServlet调用拦截器的preHandle链路处理
  6. 处理器适配器HandlerAdapter执行我们的业务代码
  7. DispatcherServlet调用拦截器的postHandle链路处理
  8. 视图解析器处理结果
  9. 调用拦截器的afterCompletion链路处理
  10. 响应用户
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值