Spring Boot超詳細講解請求處理流程機制
1. 背景
之前我們對Spring Boot做了研究講解,我們知道怎么去集成配置, 知道它如何啟動, 如何實現(xiàn)自動化配置,那么它如何接收并處理外部請求, 具體原理是怎樣, 又要流轉(zhuǎn)哪些關(guān)鍵環(huán)節(jié)? filter,interceptor, view是在哪調(diào)用, 處理順序是怎樣?Spring Boot 和Spring MVC以及內(nèi)置容器又是怎樣的作用關(guān)系? 這里我們作具體剖析研究。
2. Spring Boot 的請求處理流程設(shè)計
整理處理流程:
從流程圖可以看到, 從內(nèi)嵌的服務(wù)器接收請求到Spring Web包的處理, 再調(diào)用Spring MVC框架, 最后再到自定義的Controller。經(jīng)過層層處理, 我們接下來再研究具體的處理機制。
UML關(guān)系圖:
從UML圖中可以看到, 層級較為復(fù)雜, 主要關(guān)注兩個層面:
一是繼承GenericWebApplicationContext類,具備上下文BEAN的管理能力;
另外是實現(xiàn)ConfigurableWebServerApplicationContext接口, 具備上下文配置能力。
3. Servlet服務(wù)模式請求流程分析
3.1 ServletWebServerApplicationContext分析
Spring Boot啟動時,會判斷應(yīng)用服務(wù)類型, 有兩種, 一種是Servlet服務(wù), 另一種是Reactive響應(yīng)式服務(wù)。ServletWebServerApplicationContext就是Servlet服務(wù)核心實現(xiàn)類。
它實現(xiàn) ConfigurableWebServerApplicationContext 接口,繼承 GenericWebApplicationContext 類:
public interface ConfigurableWebServerApplicationContext extends ConfigurableApplicationContext, WebServerApplicationContext { /** * 設(shè)置服務(wù)的命名空間 */ void setServerNamespace(String serverNamespace); }
繼承ConfigurableApplicationContext, WebServerApplicationContext兩個接口, 并定義setServerNamespace接口, 設(shè)置服務(wù)的命名空間。
看下WebServerApplicationContext源碼:
public interface WebServerApplicationContext extends ApplicationContext { /** * 獲取WebServer管理對象 */ WebServer getWebServer(); /** * 獲取服務(wù)的命名空間 */ String getServerNamespace(); }
webServer是一個服務(wù)管理接口, 包含服務(wù)的啟動與停止管理功能。
ServletWebServerApplicationContext 的構(gòu)造方法:
/** * 默認構(gòu)造方法 */ public ServletWebServerApplicationContext() { } /** * 指定beanFactory的構(gòu)造方法 */ public ServletWebServerApplicationContext(DefaultListableBeanFactory beanFactory) { super(beanFactory); }
支持傳遞指定beanFactory進行對象初始化。
ServletWebServerApplicationContext 的refresh方法:
@Override public final void refresh() throws BeansException, IllegalStateException { try { // 由父類方法初始化Spring上下文 super.refresh(); } catch (RuntimeException ex) { // 如果異常, 停止WebServer啟動并釋放資源 stopAndReleaseWebServer(); throw ex; } }
refresh()方法, 可以參考【Spring Boot啟動流程】第一章的3.2.4章節(jié)第12點說明, 里面做了具體說明, 就不再贅述。
ServletWebServerApplicationContext 的createWebServer方法:
private void createWebServer() { WebServer webServer = this.webServer; // 獲取ServletContext上下文 ServletContext servletContext = getServletContext(); if (webServer == null && servletContext == null) { // 如果為空, 則進行初始化創(chuàng)建 ServletWebServerFactory factory = getWebServerFactory(); this.webServer = factory.getWebServer(getSelfInitializer()); } else if (servletContext != null) { try { // 如果不為空,則直接啟動 getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } // 初始化屬性資源配置信息 initPropertySources(); }
ServletWebServerApplicationContext 的selfInitialize方法:
private void selfInitialize(ServletContext servletContext) throws ServletException { // 將ServeltContext設(shè)置為WebApplicationContext相關(guān)屬性 prepareWebApplicationContext(servletContext); // 注冊ApplicationScope作用域 registerApplicationScope(servletContext); // 注冊環(huán)境變量中的bean信息, 在BeanFactory中也可以獲得servletContext上下文信息 WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext); for (ServletContextInitializer beans : getServletContextInitializerBeans()) { // 設(shè)置Bean在ServletContext加載完畢后進行初始化 beans.onStartup(servletContext); } }
ServletWebServerApplicationContext 的finishRefresh方法:
@Override protected void finishRefresh() // 完成刷新邏輯處理, 比如清除緩存, 發(fā)布刷新事件等 super.finishRefresh(); // 啟動WebServer WebServer webServer = startWebServer(); if (webServer != null) // 發(fā)布WebServer初始化完成事件 publishEvent(new ServletWebServerInitializedEvent(webServer, this)); } }
以上是整個Servlet模式服務(wù)的啟動流程, ServletWebServerApplicationContext作為核心處理類,介紹了主要方法的處理邏輯 。
3.2 Servlet服務(wù)模式之請求流程具體分析
Spring Boot 是基于MVC做的封裝,先看下Spring MVC的處理流程:
Spring Boot 默認是采用Tomcat作為容器, WebServer的實現(xiàn)類為TomcatWebServer, start啟動方法:
@Override public void start() throws WebServerException { // 增加同步鎖 synchronized (this.monitor) { if (this.started) { // 如果啟動, 則直接返回 return; } try { //處理tomcat的Connectors連接配置信息, 就是tomcat得xml配置得Connector信息 addPreviouslyRemovedConnectors(); Connector connector = this.tomcat.getConnector(); if (connector != null && this.autoStart) { // 如果存在Connector, 且為自動啟動, 設(shè)置Tomcat的內(nèi)置上下文延遲處理(服務(wù)成功啟動后執(zhí)行) performDeferredLoadOnStartup(); } // 檢查配置的Connectors是否已經(jīng)啟動, 避免沖突 checkThatConnectorsHaveStarted(); this.started = true; logger.info("Tomcat started on port(s): " + getPortsDescription(true) + " with context path '" + getContextPath() + "'"); } catch (ConnectorStartFailedException ex) { // 出現(xiàn)異常, 靜默停止 stopSilently(); throw ex; } catch (Exception ex) { if (findBindException(ex) != null) { throw new PortInUseException(this.tomcat.getConnector().getPort()); } throw new WebServerException("Unable to start embedded Tomcat server", ex); } finally { // 獲取TOMCAT內(nèi)置上下文 Context context = findContext(); // 解除與classloader類加載器的綁定關(guān)系 ContextBindings.unbindClassLoader(context, context.getNamingToken(), getClass().getClassLoader()); } } }
Web接口調(diào)用棧關(guān)系:
我們編寫一個測試的Web接口, 看下其調(diào)用棧結(jié)構(gòu):
調(diào)用棧關(guān)系可以看到, 從tomcat的httpServlet接收到請求, 交給Spring MVC的DispatchServlet處理, 再分到我們自定義的WEB接口, 我們經(jīng)常定義的過濾器Filter, 在進入httpServlet之前已經(jīng)被處理。
DispatcherServlet的doService方法
我們查看下核心的, 請求分發(fā)處理流程:
@Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception // LOG日志記錄請求信息 logRequest(request); // 記錄Request級別作用域的請求變量信息, 必須要開啟INCLUDE_REQUEST_URI_ATTRIBUTE屬性 Map<String, Object> attributesSnapshot = null; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } // 設(shè)置context上下文信息 request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); // 設(shè)置locale區(qū)域信息 request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); // 設(shè)置theme解析器 request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); // 設(shè)置theme源信息 request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); // 設(shè)置flashmap信息, FlashMap 是傳遞重定向參數(shù)的時候要用到的一個類 if (this.flashMapManager != null) { FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); } try { // 分發(fā)請求, 對請求做真正的邏輯處理 doDispatch(request, response); } finally { // 判斷是否異步處理請求 if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // 上面初始化設(shè)置attributesSnapshot, 這里如果有記錄, 做還原處理 if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }
DispatcherServlet的doDispatch方法
doDispatch方法負責請求分發(fā)處理, 內(nèi)部會找到我們定義的處理器, 負責處理具體的請求邏輯。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { // 定義初始變量 HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; // 獲取Web異步請求管理器 WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { // 檢查請求是否為form-multipart提交類型,我們常見的文件上傳就是采用此類型 processedRequest = checkMultipart(request); // 如果是multipart該類型, 通過MultipartResolver解析 multipartRequestParsed = (processedRequest != request); // 獲取當前請求的映射處理器, 也就是自定義的controller, 如果沒有找到, 則返回, 不做下面邏輯處理 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } // 根據(jù)映射處理器, 獲取處理適配器(實際為RequestMappingHandlerAdapter) HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 獲取請求類型,包含GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS等 String method = request.getMethod(); // 判斷是否為GET類型 boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { // 獲取上次請求修改標記, 如果沒有修改, 默認返回-1 long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } // 在進入處理器之前, 先要判斷有沒設(shè)置攔截器, 如果有, 進入攔截器的前置處理邏輯, 默認有ResourceUrlProviderExposingInterceptor等攔截器 if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 這里就是真正調(diào)用處理器, 也就是我們在controller中定義的方法 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); // 異步處理判斷 if (asyncManager.isConcurrentHandlingStarted()) { return; } // 判斷有沒采用ModelAndView返回, 并進行對應(yīng)設(shè)置 applyDefaultViewName(processedRequest, mv); // 這里是攔截器的后置處理邏輯, 如果有匹配設(shè)置, 則會進行調(diào)用處理 mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { // As of 4.3, we're processing Errors thrown from handler methods as well, // making them available for @ExceptionHandler methods and other scenarios. dispatchException = new NestedServletException("Handler dispatch failed", err); } // 設(shè)置分發(fā)處理結(jié)果, 如果處理器的執(zhí)行出現(xiàn)異常,會根據(jù)設(shè)置做對應(yīng)渲染; 如果有設(shè)置視圖, 則會進行渲染解析 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 { //如果采用multipart類型提交, 會做一些清除工作, 比如上傳文件緩存等 if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
通過以上分析, 我們可以了解到Servlet模式服務(wù)的請求流程, 重點是DispatcherServlet的doDispatch方法,要了解一個請求進入之前所做的事情, 請求處理完成之后所做的事情, 以及對于filter, interceptor執(zhí)行順序這些都要清楚, 可以幫助我們更好的運用, 以及排查請求過程中出現(xiàn)的問題。
4. Reactive服務(wù)模式請求流程分析
4.1 ReactiveWebServerApplicationContext分析
ReactiveWebServerApplicationContext為響應(yīng)式服務(wù)容器管理, 是提供Reactive Web環(huán)境的Spring 容器。 Spring WebFlux應(yīng)用就采用ReactiveWebServerApplicationContext實現(xiàn), ReactiveWebServerApplicationContext與ServletWebServerApplicationContext 的實現(xiàn)類似, 都是由SpringBoot統(tǒng)一封裝設(shè)計, 總體處理流程基本一致。
ReactiveWebServerApplicationContext 構(gòu)造方法
public class ReactiveWebServerApplicationContext extends GenericReactiveWebApplicationContext implements ConfigurableWebServerApplicationContext { // 服務(wù)管理類, 包含服務(wù)啟動與停止, 以及請求handler處理 private volatile ServerManager serverManager; // 服務(wù)命名空間 private String serverNamespace; /** * 默認構(gòu)造方法 */ public ReactiveWebServerApplicationContext() { } /** * 指定beanFactory的構(gòu)造方法 */ public ReactiveWebServerApplicationContext(DefaultListableBeanFactory beanFactory) { super(beanFactory); }
ReactiveWebServerApplicationContext#ServerManager內(nèi)部類:
/** * 服務(wù)管理類 */ static final class ServerManager implements HttpHandler { // WebServer服務(wù) private final WebServer server; // 是否懶加載 private final boolean lazyInit; // Http Handler請求處理器 private volatile HttpHandler handler; // 構(gòu)造方法, 注入serverFactory與延遲加載標記 private ServerManager(ReactiveWebServerFactory factory, boolean lazyInit) { this.handler = this::handleUninitialized; this.server = factory.getWebServer(this); this.lazyInit = lazyInit; } // 處理未初始化的請求, 暫未實現(xiàn) private Mono<Void> handleUninitialized(ServerHttpRequest request, ServerHttpResponse response) { throw new IllegalStateException("The HttpHandler has not yet been initialized"); } // 重載方法, 處理Web請求 @Override public Mono<Void> handle(ServerHttpRequest request, ServerHttpResponse response) { return this.handler.handle(request, response); } public HttpHandler getHandler() { return this.handler; } // 獲取ServerManager服務(wù)管理類 public static ServerManager get(ReactiveWebServerFactory factory, boolean lazyInit) { return new ServerManager(factory, lazyInit); } // 獲取WebServer服務(wù) public static WebServer getWebServer(ServerManager manager) { return (manager != null) ? manager.server : null; } // 通過serverManager啟動服務(wù) public static void start(ServerManager manager, Supplier<HttpHandler> handlerSupplier) { if (manager != null && manager.server != null) { manager.handler = manager.lazyInit ? new LazyHttpHandler(Mono.fromSupplier(handlerSupplier)) : handlerSupplier.get(); manager.server.start(); } } // 通過serverManager停止服務(wù) public static void stop(ServerManager manager) { if (manager != null && manager.server != null) { try { manager.server.stop(); } catch (Exception ex) { throw new IllegalStateException(ex); } } } }
ReactiveWebServerApplicationContext#startReactiveWebServer方法:
private WebServer startReactiveWebServer() { ServerManager serverManager = this.serverManager; // 啟動WebServer, 從BeanFactory中獲取HttpHandler ServerManager.start(serverManager, this::getHttpHandler); // 獲取返回WebServer return ServerManager.getWebServer(serverManager); }
查看getHttpHandler方法:
protected HttpHandler getHttpHandler() { // 獲取所有實現(xiàn)HttpHandler接口的實現(xiàn)類 String[] beanNames = getBeanFactory().getBeanNamesForType(HttpHandler.class); // HttpHandler實現(xiàn)類只能存在一個, 沒有配置或多個都會拋出異常 if (beanNames.length == 0) { // throw new ApplicationContextException( "Unable to start ReactiveWebApplicationContext due to missing HttpHandler bean."); } // 存在多個, 拋出異常 if (beanNames.length > 1) { throw new ApplicationContextException( "Unable to start ReactiveWebApplicationContext due to multiple HttpHandler beans : " + StringUtils.arrayToCommaDelimitedString(beanNames)); } // 返回HttpHandler return getBeanFactory().getBean(beanNames[0], HttpHandler.class); }
ReactiveWebServerApplicationContext#onClose方法:
@Override protected void onClose() { // 先調(diào)用父類方法, 關(guān)閉邏輯處理, 目前是空實現(xiàn) super.onClose(); // 通過ServerManager停止服務(wù) stopAndReleaseReactiveWebServer(); }
4.2 webflux服務(wù)模式之請求流程具體分析
上面講解了ReactiveWebServerApplicationContext的代碼實現(xiàn)流程, 我們看下webflux服務(wù)的請求處理流程。
定義HttpHandler
// 定義Reactive服務(wù)的HttpHandler, @Bean public HttpHandler httpHandler() { return WebHttpHandlerBuilder.applicationContext(this.applicationContext) .build(); }
webflux使用的httpHandler類型是HttpWebHandlerAdapter, 創(chuàng)建的webserver為NettyWebServer類型
@Override public WebServer getWebServer(HttpHandler httpHandler) { // 創(chuàng)建HTTP SERVER服務(wù) HttpServer httpServer = createHttpServer(); // 定義HTTP HANDLER處理適配器 ReactorHttpHandlerAdapter handlerAdapter = new ReactorHttpHandlerAdapter( httpHandler); // 采用Netty作為WebServer實現(xiàn) return new NettyWebServer(httpServer, handlerAdapter, this.lifecycleTimeout); }
創(chuàng)建ReactorHttpHandlerAdapter使用的httpHandler就是我們上面定義的WebHttpHandlerBuilder。
當一個請求進來的時候,就是通過ReactorHttpHandlerAdapter的apply()方法然后進入了了HttpWebHandlerAdapter類的handle方法, 再執(zhí)行DispatcherHandler的handle方法:
public Mono<Void> handle(ServerWebExchange exchange) { // 校驗handlerMappings是否存在 if (this.handlerMappings == null) { return createNotFoundError(); } // 響應(yīng)式操作, 調(diào)用Handler實現(xiàn)類處理邏輯, handleResult處理執(zhí)行結(jié)果 return Flux.fromIterable(this.handlerMappings) .concatMap(mapping -> mapping.getHandler(exchange)) .next() .switchIfEmpty(createNotFoundError()) .flatMap(handler -> invokeHandler(exchange, handler)) .flatMap(result -> handleResult(exchange, result)); }
通過RequestMappingHandlerMapping找到對應(yīng)的HandlerMethod(就是我們Controller中對應(yīng)的方法),然后執(zhí)行invokeHandler方法:
private Mono<HandlerResult> invokeHandler(ServerWebExchange exchange, Object handler) { // 校驗Handler適配器是否存在 if (this.handlerAdapters != null) { // 遍歷handlerAdapters for (HandlerAdapter handlerAdapter : this.handlerAdapters) { // 判斷是否支持的適配器類型 if (handlerAdapter.supports(handler)) { // 處理Handler適配器具體邏輯 return handlerAdapter.handle(exchange, handler); } } } return Mono.error(new IllegalStateException("No HandlerAdapter: " + handler)); }
HandlerAdapter是RequestMappingHandlerAdapter,在RequestMappingHandlerAdapter方法中執(zhí)行了InvocableHandlerMethod的invode方法,然后通過反射執(zhí)行了Controller中的方法, 最后把Controller方法執(zhí)行的結(jié)果,通過DispatcherHandler中的handlerResult方法,輸出返回給調(diào)用客戶端。
5. 總結(jié)
學習研究Spring Boot的請求流程, 理解其內(nèi)置容器, Spring MVC和自定義controller之間是如何流轉(zhuǎn)處理的, 各自所做的事情, 每個環(huán)節(jié)的作用, 相互之間的調(diào)用關(guān)系, 才算是理解和掌握Spring Boot的使用, 在實際工作當中, 可能更多的是停留在使用層面, 但是如果能夠?qū)崿F(xiàn)原理有進一步認知, 我們才知道更合理的去使用, 以及更高效的去排查使用過程當中出現(xiàn)的各種問題。
到此這篇關(guān)于Spring Boot超詳細講解請求處理流程機制的文章就介紹到這了,更多相關(guān)Spring Boot請求處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis學習教程(三)-MyBatis配置優(yōu)化
這篇文章主要介紹了MyBatis學習教程(三)-MyBatis配置優(yōu)化的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-05-05解決Springboot項目bootstrap.yml不生效問題
Spring Boot 2.4版本開始,配置文件加載方式進行了重構(gòu),只會識別application.* 配置文件,并不會自動識別bootstrap.yml,所以本文給大家介紹Springboot項目bootstrap.yml不生效問題的解決方案,需要的朋友可以參考下2023-09-09Java獲取網(wǎng)頁數(shù)據(jù)步驟方法詳解
這篇文章主要介紹了Java獲取網(wǎng)頁數(shù)據(jù)步驟方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-03-03