• spring boot项目06:Web请求探秘-整个过程


    JDK 8

    Spring Boot 2.4.5

    Eclipse Version: 2021-03 (4.19.0)

    ---

    今天安排了一个任务:调查Spring Web(Servlet、Tomcat、HTTP 1.1)应用的 HTTP请求是怎么处理的,于是,熬夜(start at 00:41)到现在才终于了解了整个流程。

    目标是 了解 GET、POST 两种请求的处理过程,结果,忙活了一天,就把 GET 探究了一遍,POST的,大概差不多吧,不同请求方法小地方有差别。

    准备:

    1、会使用Eclipse进行调试(会其它IDE调试也行,或许效果更好,Step Into/Over/Return)

    2、打开Spring Boot项目的调试日志(命令行添加 --debug,够了,,如果使用 --trace,会处理不过来)

    3、知识储备:Java NIO相关,比如,selectKey之类的——调试到Poller、Connector时需要。

    DispatcherServlet,久闻其名,未见其真容,今天算是“见面”了。除了它,还有FrameworkServlet、HttpServlet等。

    public abstract class GenericServlet implements Servlet, ServletConfig,
            java.io.Serializable {
    }
    
    public abstract class HttpServlet extends GenericServlet {
    }
    
    public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
    }
    
    public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
    }
    
    public class DispatcherServlet extends FrameworkServlet {
    }
    

    关系大概如下:依次继承extends

    注:

    上面的DispatcherServlet、FrameworkServlet 属于 package org.springframework.web.servlet,而HttpServlet 属于package javax.servlet.http。

    思路:

    DispatcherServlet中有两个函数比较出名:doDispatch、doService,于是,端点就从这两个函数开始设置;

    然后,沿着这两个请求 一直找 函数调用方;

    直到,涉及到 org.apache.tomcat.util.net.NioEndpoint#Poller、org.apache.catalina.connector.Connector 的使用时停止——到这里就很难调试下去了。

    调试步骤:

    0、设置各种端点——从DispatchServlet类 开始,整个过程中,自己设置了几十个断点(有些不会用到);

    1、Eclipse中 以 调试模式 启动项目(调试时 可以使用 快捷键);

    2、使用 Postman 访问准备的接口;

    	@GetMapping("hello")
    	public String hello(@RequestParam(value="name", defaultValue = "World") String name) {
    		return String.format("Hello, %s", name);
    	}

    注:写文章前最后一次调试 耗时近 2小时!(太费精时了!)

    3、重复上面的步骤,直到 找到 一个请求被 Spring Web 处理的完整路径。

    打开Postman,发送GET请求,此时 Postman、Eclipse展示如下:

    - 请求在调试结束前,一直处于 发送状态

    - 发出请求,停止了第一个断点位置——AbstractProtocol 类 的 process 函数,左边可以看到 对应线程的调用栈(stack),在右边(下图未展示),还可以看到当前的变量(鼠标放到变量上 也可以看到)。

    当前方法变量表——可以看变量的值:

    接下来,开始 各种执行各种调试命令,出发!

    ……若干小时后……

    调查结果
    public abstract class AbstractProtocol<S> implements ProtocolHandler, MBeanRegistration {
        protected static class ConnectionHandler<S> implements AbstractEndpoint.Handler<S> {
            public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
    			Processor processor = (Processor) wrapper.getCurrentProcessor();
    			processor = recycledProcessors.pop();
    			processor = getProtocol().createProcessor(); // Http11NioProtocol, Http11Processor
    			wrapper.setCurrentProcessor(processor); // NioEndpoint#NioSocketWrapper
    			state = processor.process(wrapper, status);
    				AbstractProcessorLight#process
    					status == SocketEvent.OPEN_READ
    					|
    					state = service(socketWrapper); // Http11Processor#service
    						setSocketWrapper(socketWrapper);
    							super.setSocketWrapper(socketWrapper);
    						prepareRequestProtocol(); // Http11Processor
    						getAdapter() // CoyoteAdapter
    							.service(request, response); // CoyoteAdapter#service
    								connector.getService() // StandardService
    									.getContainer() // StandardEngine
    										.getPipeline() // StandardPipeline
    											.getFirst() // StandardEngineValve
    												.invoke(request, response); // StandardEngineValve#invoke
    													host.getPipeline().getFirst().invoke(request, response); // ErrorReportValve#invoke ?
    														getNext().invoke(request, response); // StandardHostValve#invoke
    															Context context = request.getContext(); // TomcatEmbeddedContext
    															context.getPipeline().getFirst().invoke(request, response); // AuthenticatorBase#invoke
    																getNext().invoke(request, response); // StandardContextValve#invoke
    																	Wrapper wrapper = request.getWrapper(); // StandardWrapper
    																	response.sendAcknowledgement(ContinueResponseTiming.IMMEDIATELY); // Response#action#ACK
    																		hook.action(actionCode, param); // hook=Http11Processor
    																			ack((ContinueResponseTiming) param);
    																	wrapper.getPipeline().getFirst().invoke(request, response); // 最后一句,StandardWrapperValve#invoke
    																		StandardWrapper wrapper = (StandardWrapper) getContainer(); // StandardWrapper
    																		Context context = (Context) wrapper.getParent(); // TomcatEmbeddedContext
    																		servlet = wrapper.allocate();
    																		ApplicationFilterChain filterChain = 
    																			ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); // 5个 ApplicationFilterConfig
    																		Container container = this.container; // StandardWrapper
    																		filterChain.doFilter(request.getRequest(), response.getResponse()); // ApplicationFilterChain#doFilter
    																			internalDoFilter(request,response);
    																				ApplicationFilterConfig filterConfig = filters[pos++];
    																				Filter filter = filterConfig.getFilter();
    																				filter.doFilter(request, response, this); // OrderedCharacterEncodingFilter#doFilter // 第一次
    																					doFilterInternal(httpRequest, httpResponse, filterChain); # CharacterEncodingFilter#doFilterInternal
    																						String encoding = getEncoding(); // UTF-8
    																						filterChain.doFilter(request, response); // 开始第二轮,Return
    																		filterChain.doFilter(request.getRequest(), response.getResponse()); // ApplicationFilterChain#doFilter
    																			TomcatEmbeddedContext
    																			WebMvcMetricsFilter
    																			filter.doFilter(request, response, this); // OncePerRequestFilter#doFilter
    																				doFilterInternal(httpRequest, httpResponse, filterChain); // WebMvcMetricsFilter#doFilterInternal
    																					filterChain.doFilter(request, response); // // ApplicationFilterChain#doFilter
    																		filterChain.doFilter(request.getRequest(), response.getResponse()); // ApplicationFilterChain#doFilter
    																			internalDoFilter(request,response);
    																				FormContentFilter
    																				filter.doFilter(request, response, this); // OncePerRequestFilter#doFilter
    																					doFilterInternal(httpRequest, httpResponse, filterChain); // FormContentFilter
    																						filterChain.doFilter(request, response);
    																		filterChain.doFilter(request.getRequest(), response.getResponse()); // ApplicationFilterChain#doFilter
    																			internalDoFilter(request,response);
    																				RequestContextFilter
    																				filter.doFilter(request, response, this);
    																					doFilterInternal(httpRequest, httpResponse, filterChain); // RequestContextFilter
    																						filterChain.doFilter(request, response);
    																		filterChain.doFilter(request.getRequest(), response.getResponse()); // ApplicationFilterChain#doFilter
    																			internalDoFilter(request,response);
    																				WsFilter
    																				filter.doFilter(request, response, this); // 不同!WsFilter#doFilter
    																					chain.doFilter(request, response);
    																		filterChain.doFilter(request.getRequest(), response.getResponse()); // ApplicationFilterChain#doFilter
    																			internalDoFilter(request,response);
    																				// 5个filter用完,继续向下执行
    																				servlet.service(request, response); // servlet=DispatchServlet,实际执行HttpServlet#service
    																					service(request, response); // 执行FrameworkServlet#service
    																						super.service(request, response); // 执行 HttpServlet#service-长
    																							doGet(req, resp); // GET请求,执行FrameworkServlet#doGet
    																								processRequest(request, response);
    																									initContextHolders(request, localeContext, requestAttributes);
    																									doService(request, response); // 执行 DispatchServlet#doService
    																										logRequest(request); // 打印第一条调试日志!
    																										doDispatch(request, response); // 执行 DispatchServlet#doDispatch
    																											mappedHandler = getHandler(processedRequest); // 第二条DEBUG日志, HandlerExecutionChain
    																											HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // RequestMappingHandlerAdapter, Spring容器中存在此Bean
    																											mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // 执行 AbstractHandlerMethodAdapter#handle
    																												return handleInternal(request, response, (HandlerMethod) handler); // 执行 RequestMappingHandlerAdapter#handleInternal
    																													mav = invokeHandlerMethod(request, response, handlerMethod);
    																														ServletWebRequest webRequest = new ServletWebRequest(request, response);
    																														WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod); // ServletRequestDataBinderFactory
    																														ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);// ModelFactory
    																														ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); // com.benzl.prj1.Prj1Application#hello(String)
    																														AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); // StandardServletAsyncWebRequest
    																														WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); // WebAsyncManager
    																														invocableMethod.invokeAndHandle(webRequest, mavContainer); // ServletInvocableHandlerMethod#invokeAndHandle
    																															Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); // InvocableHandlerMethod#invokeForRequest
    																																Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
    																																	args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); // HandlerMethodArgumentResolverComposite#resolveArgument
    																																		HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
    																																		return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory); // AbstractNamedValueMethodArgumentResolver#resolveArgument
    																																			NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
    																																			handleResolvedValue(arg, namedValueInfo.name, parameter, mavContainer, webRequest);
    																																return doInvoke(args);
    																																	Method method = getBridgedMethod();  // 执行 HandlerMethod#getBridgedMethod
    																																	return method.invoke(getBean(), args); // getBean()=com.benzl.prj1.Prj1Application$$EnhancerBySpringCGLIB$$3f2d7a4c@fb0bff5 // 反射Method#invoke
    																															this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest); // HandlerMethodReturnValueHandlerComposite#handleReturnValue
    																																handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); // RequestResponseBodyMethodProcessor 
    																																	writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); // AbstractMessageConverterMethodProcessor#writeWithMessageConverters
    																																	// 输出第三条DEBUG日志
    																																	// 输出第四条DEBUG日志
    																																	((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage); // AbstractHttpMessageConverter#write
    																																	// 这里断片了---------------↓
    																																	CoyoteOutputStream#flush
    																																		OutputBuffer#flush
    																																			OutputBuffer#doFlush
    																																				coyoteResponse.sendHeaders();
    																																					action(ActionCode.COMMIT, this); // Response#action ? 怎么来这里了?actionCode=COMMIT
    																																						hook.action(actionCode, param); // hook=Http11Processor, 执行 AbstractProcessor#action
    																																							case COMMIT:
    																																							prepareResponse();
    																																					setCommitted(true);
    																																				coyoteResponse.action(ActionCode.CLIENT_FLUSH, null); // Respone#action, actionCode=CLIENT_FLUSH
    																																					hook.action(actionCode, this);
    																																						case CLIENT_FLUSH:
    																																						action(ActionCode.COMMIT, null); // AbstractProcessor#action
    																																						flush(); // 客户端(浏览器、postman收到 响应)!!!
    																																	// 这里断片了---------------↑
    																																	回
    																																回
    																															回
    																														return getModelAndView(mavContainer, modelFactory, webRequest);
    																														finally: webRequest.requestCompleted();
    																													return mav;	
    																												回				
    																											回 DispatchServlet#doDispatch,继续执行...
    																											processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
    																										回 DispatchServlet#doService
    																										...不继续了
    		}

    上面的调查结果请拷贝到 Notepad++ 里面打开,然后,视图->取消(不选)自动换行,再细看。

    针对调查结果,下面再做一个简要介绍:

    本文从 AbstractProtocol 开始:其实,上面停止了 AbstractProtocol的子类ConnectionHandler 的process函数中(起点)。

    用到的 协议对象 Http11NioProtocol,对于的处理器 Http11Processor;

    NioEndpoint 是 配置 HTTP1.1 使用的,其下包含 Poller内部类,NioSocketWrapper静态内部类,以及其它 内部类;

    Http11Processor 源码:继承了 AbstractProcessor,所以,调用时 就会用到 其 父类中的方法(Java基础,同 上面的 *Servlet类)

    public interface Processor {
    }
    
    public abstract class AbstractProcessorLight implements Processor {
    }
    
    public abstract class AbstractProcessor extends AbstractProcessorLight implements ActionHook {
    }
    
    public class Http11Processor extends AbstractProcessor {
    }

    ConnectionHandler中的 process方法 调用 processor.process(wrapper, status),实际调用的是 AbstractProcessorLight#process;

    接着,调用 Http11Processor#service:其中重要的 调用 prepareRequestProtocol()、getAdapter().service(request, response);

    getAdapter().service(request, response) 中,获取的 org.apache.catalina.connector.CoyoteAdapter 对象,再执行其 service方法;

    CoyoteAdapter#service 中最重要的调用:

    // Calling the container
    connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

    最终的 invoke 实际调用的是 org.apache.catalina.core.StandardEngineValve#invoke。

    final class StandardEngineValve extends ValveBase {
    }

    注意,上面 ValveBase 是 抽象类,其下的很多子类 会在 本地分析中被用到:

    StandardEngineValve#invoke 中的重要调用:host.getPipeline().getFirst().invoke(request, response),实际执行的是 ErrorReportValve#invoke;

    ErrorReportValve#invoke 中 调用 getNext().invoke(request, response),实际执行 StandardHostValve#invoke;

    然后又是 TomcatEmbeddedContext对象、AuthenticatorBase#invoke、StandardContextValve#invoke,

    最后,调用了 StandardWrapperValve#invoke。

    StandardWrapperValve#invoke 调用中,完成了 请求处理、请求响应 的全过程

    1、servlet = wrapper.allocate(); 分配 servlet;

    2、ApplicationFilterFactory.createFilterChain(request, wrapper, servlet)

    创建过滤器链,包含五个过滤器(调试工具中 可以看到):

    [
    	FilterMap[filterName=characterEncodingFilter, urlPattern=/*], 
    	FilterMap[filterName=webMvcMetricsFilter, urlPattern=/*], 
    	FilterMap[filterName=formContentFilter, urlPattern=/*], 
    	FilterMap[filterName=requestContextFilter, urlPattern=/*], 
    	FilterMap[filterName=Tomcat WebSocket (JSR356) Filter, urlPattern=/*]
    ]

    3、filterChain.doFilter(request.getRequest(), response.getResponse())

    过滤器链建好后,多次“触发”上面的调用——千万注意,是一个扣一个的链式,而不是 循环调用!

    疑问:做什么用呢?

    4、ApplicationFilterChain#internalDoFilter 中 调用 servlet.service(request, response)

    这里的 servlet 实际是 DispatchServlet对象, 实际执行的是 HttpServlet#service,进一步调用 FrameworkServlet#service。

    注意,HttpServlet 中有两个 service函数,都是两个参数,但一个 签名长,一个短(下面的签名没有抛出异常的信息,不是完整的哦!):

    // 短 会 调用 长的
    public void service(ServletRequest req, ServletResponse res)
    // 长
    protected void service(HttpServletRequest req, HttpServletResponse resp)

    注意了!注意了!注意了!

    关键点来了

    HttpServlet 中 签名长 的 service函数中包含 各种 处理请求的方法调用——doGet、doHead、doPost、doPut、doDelete、doOptions、doTrace。

    本文近分析了 doGet 分支,其它的 应该类似的。

    其后,有陆续调用了 DispatchServlet#doService、DispatchServlet#doDispatch等,不再具体分析,可以看前面的 调查结果 以及 自行调试。

    最终,调用 HandlerMethodReturnValueHandlerComposite#handleReturnValue 想要客户端,得到响应。

    注:调查过程中,发现很多 对象是在 Spring容器中存在的,比如,webEndpointServletHandlerMapping、managementServletContext。

    ---本文完---

    疑问:

    1、请求数据 怎么 定制?Servlet中的 过滤器、拦截器、AOP 等怎么使用?

    2、本文的HTTP 是 1.1,其它还有 HTTP 2、WebSocket、ReactiveWeb,可以进行类似的分析;

    3、响应数据 怎么 定制?

    4、Web应用的初始化是怎么做的呢?还需 调试 入口类的 run方法——又是几多精时啊。

    还要继续 探究 才是。

  • 相关阅读:
    面试之求找两个数和为某个数、几个连续数等于某个数
    oracle 创建字段自增长——两种实现方式汇总
    UnityTestTools測试工具
    现代控制理论课件资料与说明
    教你轻松自己定义ViewPagerIndicator
    【Android】自己定义ListView的Adapter报空指针异常解决方法
    SICP-Exercise 1.5
    网络编程----------SOCKET编程实现简单的TCP协议
    字符串翻转
    机房收费系统重构(六)—泛型集合
  • 原文地址:https://www.cnblogs.com/luo630/p/15139725.html
Copyright © 2020-2023  润新知