当前位置: 首页 > article >正文

【SpringMVC原理分析】

Spring MVC大家应该都很熟悉吧。作为一个全面的Web框架,提供了构建Web应用程序所需的所有基础设施。下面我们结合源码从两个方面来分析, SpringMVC到底是怎么工作的。

  • Spring MVC 基础设施准备过程
  • Spring MVC 的工作流程

1、Spring MVC 基础设施准备过程

tomcat启动的时候会执行Servlet的init方法, 所以准备流程肯定是在这个方法中。

public interface Servlet {
	//启动入口
    void init(ServletConfig var1) throws ServletException;
}

//实现了Servlet接口
public abstract class HttpServletBean...{
    //执行init方法
    public final void init() throws ServletException {
       ...
       // 执行初始化
       initServletBean();
    }
}

追踪源码进入到initServletBean()方法, 这个方法会初始化spring容器, 并完成容器的刷新动作。
关键在刷新容器之前注册了一个ContextRefreshListener用来监听容器的刷新动作。
那势必会在监听方法中做一些事情。

public interface Servlet {
	//启动入口
    void init(ServletConfig var1) throws ServletException;
}

//实现了Servlet接口
public abstract class HttpServletBean...{
    //执行init方法
    public final void init() throws ServletException {
       ...
       // 执行初始化
       initServletBean();
    }
}

public abstract class FrameworkServlet ...{
    @Override
    protected final void initServletBean() throws ServletException {
        try {
        	//初始化spring容器
           this.webApplicationContext = initWebApplicationContext();
           initFrameworkServlet();
        }
    }
    
    protected WebApplicationContext initWebApplicationContext() {
       WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
       WebApplicationContext wac = null;
       ...
       if (wac == null) {
           //创建spring容器
          wac = createWebApplicationContext(rootContext);
       }
       ...
    }
    
    protected WebApplicationContext createWebApplicationContext(...) {
       ...
       //刷新容器
       configureAndRefreshWebApplicationContext(wac);
    
       return wac;
    }
    
    protected void configureAndRefreshWebApplicationContext(...) {
       ...
       //注册监听器
       wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
       ...
       applyInitializers(wac);
       //在这里刷新
       wac.refresh();
    }
}

我们找到这个监听器, 发现监听的是ContextRefreshedEvent事件。
也就是说这时候spring中的bean都已经初始化好了。
在这个事件的处理中初始化了HandlerMappings和HandlerAdapters。

private class ContextRefreshListener implements ApplicationListener<ContextRefreshedEvent> {
	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		FrameworkServlet.this.onApplicationEvent(event);
	}
}

//最后追踪到DispatcherServlet类的initStrategies方法
public class DispatcherServlet extends FrameworkServlet {
	@Override
	protected void onRefresh(ApplicationContext context) {
		initStrategies(context);
	}

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

追踪到这里基本思路已经很清晰了, 剩下就是代码实现细节。
下面简单介绍下initHandlerMappings和initHandlerAdapters在这两个方法主要干了啥事。

  • initHandlerMappings
    主要注册了三个默认的Mapping Bean, 分别对应处理三种方式实现的servlet :
    1、BeanNameUrlHandlerMapping处理实现了controller或者HttpRequestHandler接口的Servlet。
    2、RequestMappingHandlerMapping处理被@Controller或者@RequestMapping注解标注的Servlet。
    3、RouterFunctionMapping处理HandlerFunction类型的Bean。
  • initHandlerAdapters
    主要是注册四个默认的适配器, 以便请求过来的时候去执行不同的handlerMethod, 当然这个过程还包含参数处理,返回值处理等都会初始化相应的处理对象封装到适配器中:
    1、如果是@Controller -> RequestMappingHandlerAdapter。
    2、如果是HttpRequestHandler接口 -> HttpRequestHandlerAdapter。
    3、如果是Controller接口 -> SimpleControllerHandlerAdapter。
    4、如果是HandlerFunction对象 -> HandlerFunctionAdapter。

至此, springmvc的基础设施基本就准备好了。

2、Spring MVC 工作流程

那Spring MVC 是如何工作的呢?
如何将一个Request请求路由到我们的Controller, 又是如何调用方法的, 参数和返回值又是怎么处理的呢?
下面看源码分析下:

public interface Servlet {
	//入口自然是service方法
    void service(ServletRequest var1, ServletResponse var2);
}

public class DispatcherServlet extends FrameworkServlet {

	protected void doService(HttpServletRequest request, HttpServletResponse response){
		...
		doDispatch(request, response);
		...
	}
	
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response){
	   ...
       // 根据path找到对应的HandlerExecutionChain(包含HandlerMethod与HandlerInterceptor)
       mappedHandler = getHandler(processedRequest);
       if (mappedHandler == null) {
           //404
          noHandlerFound(processedRequest, response);
          return;
       }

       // 根据HandlerMethod找到匹配的Adapter
       // 如果是@Controller -> RequestMappingHandlerAdapter
       // 如果是HttpRequestHandler接口 -> HttpRequestHandlerAdapter
       // 如果是Controller接口 -> SimpleControllerHandlerAdapter
       // 如果是HandlerFunction对象 -> HandlerFunctionAdapter
       HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
		...

         // 调用HandlerInteceptor的applyPreHandler方法 如果返回false 结束调用流程
        if (!mappedHandler.applyPreHandle(processedRequest, response)) {
           return;
        }

        // 调用handler方法
        mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

        if (asyncManager.isConcurrentHandlingStarted()) {
           return;
        }

        // 设置默认视图
        applyDefaultViewName(processedRequest, mv);

        // 调用HandlerInteceptor的applyPostandler方法
        mappedHandler.applyPostHandle(processedRequest, response, mv);
        
     	//渲染视图
     	processDispatchResult(...);
  		...
	}
}

从上面源码, 我们从Servlet的service方法一直追踪到DispatcherServlet的doDispatcher方法, 看到了对request的处理流程:

1、先通过HandlerMapping拿到request对应的HandlerExcutionChain, 并且根据HandlerExcutionChain中的handler找到对应的Adapter。
2、执行HandlerExcutionChain中的interceptor的preHandle方法(责任链模式), 这个过程中只要有一个拦截器返回false就不再往后执行, 直接执行afterCompletion方法然后return。
3、然后就是执行handler方法, 其中包括用HandlerMethodArgumentResolver解析参数, 并且在执行完方法后用HandlerMethodReturnValHandler对返回值进行解析处理(适配器模式&组合模式&策略模式)
4、方法执行完后, 执行HandlerExcutionChain中的interceptor的postHandle方法进行后置处理
5、处理完后得到执行结果ModelAndView, 还需要进行render, 把ModelAndView的view渲染到response中
6、另外如果发生异常, 还会进行异常处理, 这时候就要用到HandlerExceptionResolver对异常进行包装处理和返回


http://www.kler.cn/a/403457.html

相关文章:

  • Linux驱动开发快速入门——字符设备驱动(直接操作寄存器设备树版)
  • Git 提交的相对引用
  • 006-自定义枚举注解
  • PH热榜 | 2024-11-22
  • 杰理-gpadc
  • 在 Ubuntu 系统上安装 npm 环境以及 nvm(Node Version Manager)
  • k8s-NetworkPolicy
  • [游戏开发][Unity]Unity3D中的基本概念及关键组件解析
  • 【从零开始的LeetCode-算法】3354. 使数组元素等于零
  • 大数据实验4-HBase
  • 基于redis完成延迟队列
  • 蓝桥杯某C语言算法题解决方案(质因数分解)
  • 使用Cursor和Claude AI打造你的第一个App
  • c++调用 c# dll 通过 clr (详细避坑)
  • 数据加密使用方法
  • 使用Python编写一个简单的网页爬虫,从网站上抓取标题和正文内容。
  • 是时候谈谈Go的测试了
  • ArcGIS计算水库库容量
  • 曼昆《经济学原理》第八版课后答案及英文版PDF
  • 7.高可用集群架构Keepalived双主热备原理
  • 头歌-本关任务:使用GmSSL命令行,生成SM2私钥并对文件进行签名验证(第二关)。
  • android viewpager2 嵌套 recyclerview 手势冲突
  • FFmpeg源码:mid_pred函数分析
  • Chromium Mojo(IPC)进程通信演示 c++(2)
  • 实验室管理技术革新:Spring Boot系统
  • 什么是事务,事务有什么特性?