享读SpringMVC源码0--从Tomcat到SpringMVC

作为过渡篇,本文以Tomcat服务器为例,讲讲请求是如何从Tomcat来到SpringMVC的(Springboot环境下)

1.回顾

SpringMVC的核心是DispatcherServlet. 而DispatcherServlet的本质还是一个Servlet 。
说到Servlet 就得讲讲web服务器Tomcat。

文接Tomcat原理系列之三:请求链上的那些类Tomcat原理系列之七:详解socket如何封装成request(下)

2.请求从Tomcat到SpringMVC

请求通过Socket 开启应用之旅,在Tomcat原理系列之三:请求链上的那些类 中讲过,请求来到Tomcat的最后一个valve,StandardWrapperValve时。执行其invoke方法
在这里插入图片描述

StandardWrapperValve

请求来到StandardWrapperValve#invoke方法

final class StandardWrapperValve
    extends ValveBase {
    public final void invoke(Request request, Response response)
        throws IOException, ServletException {
        // 1.Allocate a servlet instance to process this request
        //分配一个servet 去处理请求
        servlet = wrapper.allocate();
        // 2.Create the filter chain for this request 
        //封装servlet, 为当前请求创建一个过滤器链
        ApplicationFilterChain filterChain =
                        ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
        //3.执行过滤器链的doFilter方法,(链式执行各种Filter ,末端执行servlet's service() method)
        filterChain.doFilter(request.getRequest(),
                                            response.getResponse());
        }
}
ApplicationFilterChain

之后来到 ApplicationFilterChain#doFilter方法

public final class ApplicationFilterChain implements FilterChain {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response)
            throws IOException, ServletException {
        internalDoFilter(request,response);//调用内部internalDoFilter方法
            }
     private void internalDoFilter(ServletRequest request,
                                      ServletResponse response)
            throws IOException, ServletException {
             // Call the next filter if there is one
             // 4.链式调用每个Filter的doFilter方法
             if (pos < n) {
                  filter.doFilter(request, response, this);
             }
             //5.调用servlet的service方法
             servlet.service(request, response);
            }
}

在Filter的末端,开始准备进入Servlet执行方法。

HttpServlet

我们来看看DispatcherServlet 的继承图
在这里插入图片描述
从图中我们可以看出,DispatcherServlet 通过几层父类间接实现Servlet。

ApplicationFilterChain中 调用servlet.service(request, response);传递的参数是(ServletRequest request, ServletResponse response)。所以执行的是HttpServlet 中的service方法.可以看出此方法的目的是将ServletRequest,ServletResponse 转换为我们熟悉的HttpServletRequest ,HttpServletResponse

public abstract class HttpServlet extends GenericServlet {
 //6.执行HttpServlet.service()方法,做req,res 的强制转换
 public void service(ServletRequest req, ServletResponse res){
    HttpServletRequest  request;
    HttpServletResponse response;
    try {
        //转为 HttpServletRequest,HttpServletResponse
         request = (HttpServletRequest) req;
         response = (HttpServletResponse) res;
    } catch (ClassCastException e) {
                throw new ServletException("non-HTTP request or response");
    }
    service(request, response);
    }
 //8.执行HttpServlet.service此时的参数已经是HttpServletRequest,HttpServletResponse
 //并根据请求方法类型。调用不同的处理方法。子类FrameworkServlet 类中重写了一些方法,所以转去执行子类的doGet方法
 protected void service(HttpServletRequest req, HttpServletResponse resp)
         throws ServletException, IOException {
      if (method.equals(METHOD_GET)) {
        doGet(req, resp);   
      }
    }
 }
FrameworkServlet
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
 //7.兼容PATCH方法,非PATCH方法最终还是交给HttpServlet.service()
 protected void service(HttpServletRequest request, HttpServletResponse response)
 			throws ServletException, IOException {
 
 		HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
 		if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
 			processRequest(request, response);
 		}
 		else {
 			super.service(request, response);
 		}
 }
//9.get代理方法,转到processRequset去处理
 protected final void doGet(HttpServletRequest request, HttpServletResponse response)
 			throws ServletException, IOException {
 
 		processRequest(request, response);
 	}
//10.模板方法,请求的处理交给子类的doService方法去处理
 protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
 			throws ServletException, IOException {
        doService(request, response);
 	}
}
DispatcherServlet

最终请求来到了DispatcherServlet的doService方法,开始DispatcherServlet的工作内容

//最终来到了DispatcherServlet
public class DispatcherServlet extends FrameworkServlet {
    //11.做请求分发前的准备工作,主要是设置一些请求的相关属性
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        doDispatch(request, response);
    }
    //12 开启请求的分发。
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //开启DispatcherServlet的分发功能
    }
}

最终请求到了DispatcherServlet的doDispatch开启DispatcherServlet 请求分发处理功能。

3.DispatcherServlet的工作原理

请求到达doDispatch 方法后,DispatcherServlet对其做了什么呢?这就引出了DispatcherServlet的工作原理这个话题。

处理流程

借用网上张图:在这里插入图片描述

  1. 请求来到DispatcherServlet
  2. DispatcherServlet根据请求信息,从HandlerMapping 找能处理当前请求的Handler并封装成一个HandlerExecutionChain
  3. 根据解析到的Handler ,获取一个适配器。
  4. HandlerAdapter会根据Handler来调用真正的处理器开处理请求,列如我们写的controller类中的业务方法
  5. 处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是个逻辑上的View
  6. ViewResolver会根据逻辑View查找实际的View。
  7. 视图渲染完成后,DispatcherServlet返回。

这些流程反映在代码中,就是doDispatch 方法内的相关调用了.

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 {
				//检查是否是上传文件请求
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// 获取能处理当前请求的Handler 链
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// 获取能处理当前请求的的handler的适配器
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
					}
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
				//拦截器前置处理
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				//适配器handle方法处理请求。
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
				
				applyDefaultViewName(processedRequest, mv);
				//拦截器后置处理
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

4.总结

我们可以看出,DispatcherServlet采用了“中心化”的思想。请求统一过DispatcherServlet,这样的好处就是统一管理。DispatcherServlet就有点路由的意思了。

本文衔接Tomcat原理系列之七:详解socket如何封装成request(下) 终于粗略的过了一遍,请求如何从socket经Tomcat到达DispatcherServlet,变成我们常用的HttpServletRequest 。对于web 的工作原理有了更深的理解

明白道理不一定能让我们技术增进多少,但至少心里不疑惑。


如果本文任何错误,请批评指教,不胜感激 !

微信公众号:源码行动
享学源码,行动起来,来源码行动

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Tomcat是一个开源的Java Web服务器,用于运行Java应用程序。当Tomcat加载Spring和Spring MVC时,它遵循以下过程: 1. Tomcat从web.xml文件中加载DispatcherServlet,这是Spring MVC框架的核心组件。 2. DispatcherServlet的初始化方法被调用,它负责初始化Spring MVC的配置并与Spring框架进行集成。 3. 在初始化过程中,DispatcherServlet会读取Spring的配置文件,通常是applicationContext.xml。这个配置文件包含了定义Spring Bean的信息,以及其他的配置。 4. Spring框架会根据配置文件中的信息,创建和管理Bean对象,这些Bean对象是应用程序中的各个组件,比如控制器、服务层、数据访问层等。 5. 当有HTTP请求到达Tomcat服务器时,DispatcherServlet会拦截请求并将其传递给适当的控制器。 6. 控制器会执行业务逻辑,并根据请求的内容生成相应的模型数据。 7. DispatcherServlet将模型数据传递给视图解析器,并选择适当的视图来展示数据。 8. 最后,DispatcherServlet将视图渲染为HTML响应,返回给客户端。 总结来说,Tomcat加载Spring和Spring MVC是通过初始化DispatcherServlet,并根据配置文件创建和管理Spring Bean来实现的。这个过程可以使开发者更方便地使用Spring和Spring MVC框架来开发Java Web应用程序。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [从设计模式+源码讲到Tomcat加载Spring,你还不懂?](https://blog.csdn.net/weixin_42864905/article/details/110921634)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [springmvc+maven+tomcat服务器+demo](https://download.csdn.net/download/qypt2015/11832828)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值