本文概述:
从简述 Tomcat 如何处理请求, 然后去探究 SpringMVC 如何去接收请求并通过 Dispatcher 进行派发
这里不会特别详细的去说整个流程, 而是简要介绍接收 Servlet 请求是如何处理的.
public class MyFirstServlrt extends HttpServlet {
}
当我们定义了这样的一个 Servlet ,然后 Tomcat 组件初始化的时候会保存该 Servlet 处理的路径 (本文不讲述这部分), 然后当存在的时候, 才会从 Engine 一直向内发送到 StandardWrapperValve, 这个类中保存的就是 Servlet 实例.
也就是说, 请求到达之后, 是交给了 HTTP Servlet 的子类, 也就是我们定义的类中进行处理
![[Pasted image 20230317223716.png|300]],
可以看见, 我们的定义的所有 HttpServlet 直接继承 HttpSerlvet, 请求经过 HttpServlt 的 service 方法分发到我们定义的 HelloServlet 中的.
protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {String method = req.getMethod();if (method.equals(METHOD_GET)) {long lastModified = getLastModified(req);if (lastModified == -1) {// servlet doesn't support if-modified-since, no reason// to go through further expensive logicdoGet(req, resp);} else {long ifModifiedSince;try {ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);} catch (IllegalArgumentException iae) {// Invalid date header - proceed as if none was setifModifiedSince = -1;}if (ifModifiedSince < (lastModified / 1000 * 1000)) {// If the servlet mod time is later, call doGet()// Round down to the nearest second for a proper compare// A ifModifiedSince of -1 will always be lessmaybeSetLastModified(resp, lastModified);doGet(req, resp);} else {resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);}}} else if (method.equals(METHOD_HEAD)) {long lastModified = getLastModified(req);maybeSetLastModified(resp, lastModified);doHead(req, resp);} else if (method.equals(METHOD_POST)) {doPost(req, resp);} else if (method.equals(METHOD_PUT)) {doPut(req, resp);} else if (method.equals(METHOD_DELETE)) {doDelete(req, resp);} else if (method.equals(METHOD_OPTIONS)) {doOptions(req,resp);} else if (method.equals(METHOD_TRACE)) {doTrace(req,resp);} else {String errMsg = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[1];errArgs[0] = method;errMsg = MessageFormat.format(errMsg, errArgs);resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);}}
经过 Service 方法会根据 HttpMethod 来执行具体的我们定义的 Serlvet 中的方法.
那么 SpringMVC 是如何从 HttpServlet 中接收这个请求的呢, 可能大家都知道 DispatcherServlet 来处理请求? 但是, 你有了解过, 请求如何会到 DispatcherServler 中呢?
先来看看继承图
![[Pasted image 20230124221729.png]]
原先, 请求会到达 HttpServler 的实现子类中, 而 SpringMVC 加入了一个 HTTPServlet, 这个 HttpServlet 就是 DispatcherServlet .这是因为多态嘛.
注意啊, 这里小白可能会不太理解. 请求是按照这个类图往下传递的吗, 然后 SpringMVC 才处理了请求.
先来联想 Tomcat 是如何处理的, 初始化的时候会加载所有的 Servelt, 然后包装起来, 当请求来的时候, 从 Egine 逐层发送到 WrapperServlet, 调用 service 方法, 这个 service 方法是谁的呢, 是 HTTPServlet 接口的的, 而 WrapperServlet 里面是啥的呢, 是一个继承 HTTP Servlet 的类, 也就是我们开发的时候的 Servlet.
所以说, 直接接收请求的其实就是 WrapperServlet 里面的 Servlet, 但是因为多态嘛, 子类拥有父类的方法, 先调用了父类的方法, 然后才调用了子类的方法.
举个例子 HelloServlet 在处理请求的时候, 会先调用其 service 方法, 然后 service 方法进行判断, 然后直接强转然后调用了子类的方法 (强转意味着什么, 其实从真实保存的数据来说, 这个 Servlet 就是一个我们创建的 Servlet)
@Overridepublic void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {HttpServletRequest request;HttpServletResponse response;try {request = (HttpServletRequest) req;response = (HttpServletResponse) res;} catch (ClassCastException e) {throw new ServletException(lStrings.getString("http.non_http"));}service(request, response);}
然后此时我们再来看看 SpringMVC 如何处理的可能就有体会了.
SpringMVC 创建的就是一个 HTTPServlet, 不过是实现的子类
![[Pasted image 20230124221729.png]]
DispatcherServelt 此时其实就是清晰了, Servevlt 其实就是 DispatcherServelt, 其等于 HttpServlet, 等于 HttpServletBean, 等于 FrameWork, 然后在接受请求的时候, 首先到了 HttpServlet 对吧
然后我们实现了一个 FrameWorkServlet, 然后发送到了 FreameworkdServlet,.
FrameworkServlet 就有意思了, 是 MVC 自定义的, 也就是说让其调用子类的方法就可以了 (因为本质上存储的就是 DispatcherServlet, 只是先调用了父类方法, 但是父类方法也可以调用子类方法, 具体可以了解模板方法, 父类定义流程, 子类实现流程中核心步骤)
也就是说 DispatcherServlet 也就是实现了 HttpServlet 接口的一个自定义的 Servlet, 和我们创建的一样, 但是他强了很多倍.
DispatcherServlet 在处理的时候, 还是和普通的 servlet 一样, 先是 service 方法, 但是但是, 因为 Dispatcher Servlet 的父类 FrameworkServlet 就实现了 service 方法, 这里要思考, 原本 HTTPservlet 调用 service 方法是调用的 HelloServlet 父类的 service 方法, 而 FrameWorkServlet 就有 service 方法, 现在是不是是不是,dispatcherServlet 要调用 FrameWorkServlet 的 service 方法
@Overrideprotected 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);}}
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {long startTime = System.currentTimeMillis();Throwable failureCause = null;LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();LocaleContext localeContext = buildLocaleContext(request);RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());initContextHolders(request, localeContext, requestAttributes);try {// DispatcherServlet正式进场了!!!!doService(request, response);}... 省略
OK, 到这里就结束了,SpringMVC 之所以能接收所有的 Servlet 请求, 本质上是因为 DispatcherServlet 也是一个普通的 Servlet, 每个我们创建的 Servlet 在执行我们的业务代码之前都需要执行 service 方法, 而 DispatcherServlet 的父类就有 service 方法, 所以就屏蔽了父类的 service 方法, 然后 FrameworkServlet 是 MVC 自定义的抽象类, 所以理所当然的把消息传给亲儿子 DisapatcherServlet 来处理
![[Pasted image 20230318161457.png]]
不知道大家还记不记得, 如果是 xml 配置 SpringMVC, 我们设置了一个属性, 就是配置的映射路径, 我们给 DispatcherServlet 设置的是 “/”
...省略标签dispatcherServlet /
也就是说, 就是这里配置的 Serlvet, Tomcat 初始化的时候会加载这里的信息 (不是创建), 然后当请求的时候, 会接手所有根路径下的请求, 也就是请求就到了 DispatcherServlet, 然后他就先调用 service 方法, 即 FrameWorkServlet 的 service 方法, 最后调用 doDispatcher 方法.