小架構step系列19:請求和響應

1 概述

作為Web程序,通用形式是發起HTTP請求并獲取返回的結果,在這個過程中,需要把請求映射到代碼的接口上,提供這種接口的類一般稱為Controller,也就是需要把請求映射到Controller的接口方法上,把請求的參數映射到接口的參數中,并從接口返回接口處理的結果。

在后端渲染頁面的場景中,返回的結果需要處理為視圖View。而現在更普遍的是前后端分離,返回的結果一般處理為JSON格式的數據,前端拿到這種數據再進行處理和頁面的渲染。

本文來了解一下這個請求和響應的過程。

2 原理

2.1 源碼解析

在使用SpringBoot的時候,內嵌了一個tomcat服務器,做到了不需要感覺到有web服務器就能夠開發web程序。內嵌tomcat服務器由tomcat-embed-core包提供,tomcat接收請求時,會調用到StandardWrapperValve的invoke()方法,下面的源碼則從這段代碼開始。

// tomcat接收請求時,會調用到StandardWrapperValve的invoke()方法
// 源碼位置:org.apache.catalina.core.StandardWrapperValve(在tomcat-embed-core包)
public void invoke(Request request, Response response) throws IOException, ServletException {// 省略其它代碼// 1. 組裝Filter鏈ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);Container container = this.container;try {if ((servlet != null) && (filterChain != null)) {// Swallow output if neededif (context.getSwallowOutput()) {try {SystemLogHandler.startCapture();if (request.isAsyncDispatching()) {request.getAsyncContextInternal().doInternalDispatch();} else {filterChain.doFilter(request.getRequest(), response.getResponse());}} finally {String log = SystemLogHandler.stopCapture();if (log != null && log.length() > 0) {context.getLogger().info(log);}}} else {if (request.isAsyncDispatching()) {request.getAsyncContextInternal().doInternalDispatch();} else {// 2. 遞歸調用Filter鏈,filterChain為ApplicationFilterChainfilterChain.doFilter(request.getRequest(), response.getResponse());}}}} // 省略其它代碼
}// 源碼位置:org.apache.catalina.core.ApplicationFilterChain
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {if (Globals.IS_SECURITY_ENABLED) {// 省略其它代碼} else {// 3. 調用內部方法internalDoFilter()internalDoFilter(request, response);}
}// 源碼位置:org.apache.catalina.core.ApplicationFilterChain
private void internalDoFilter(ServletRequest request, ServletResponse response)throws IOException, ServletException {// pos為當前filter在Filter鏈中的位置,n是filter的個數,也就是遞歸調Filter,到最后一個后就不滿足pos<n條件if (pos < n) {// 取當前位置的Filter,同時pos加1方便下次取下一個FilterApplicationFilterConfig filterConfig = filters[pos++];try {Filter filter = filterConfig.getFilter();if (request.isAsyncSupported() &&"false".equalsIgnoreCase(filterConfig.getFilterDef().getAsyncSupported())) {request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);}if (Globals.IS_SECURITY_ENABLED) {final ServletRequest req = request;final ServletResponse res = response;Principal principal = ((HttpServletRequest) req).getUserPrincipal();Object[] args = new Object[] { req, res, this };SecurityUtil.doAsPrivilege("doFilter", filter, classType, args, principal);} else {// 調Filter的doFilter方法,注意Filter里一般要有filterChain.doFilter()才能觸發下一個Filter執行// this就是filterChainfilter.doFilter(request, response, this);}} catch (IOException | ServletException | RuntimeException e) {throw e;} catch (Throwable e) {e = ExceptionUtils.unwrapInvocationTargetException(e);ExceptionUtils.handleThrowable(e);throw new ServletException(sm.getString("filterChain.filter"), e);}return;}// Filter正常結束后繼續執行業務,如果不正常跑完Filter則在前面返回或拋異常了try {if (ApplicationDispatcher.WRAP_SAME_OBJECT) {lastServicedRequest.set(request);lastServicedResponse.set(response);}if (request.isAsyncSupported() && !servletSupportsAsync) {request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);}// Use potentially wrapped request from this pointif ((request instanceof HttpServletRequest) && (response instanceof HttpServletResponse) &&Globals.IS_SECURITY_ENABLED) {final ServletRequest req = request;final ServletResponse res = response;Principal principal = ((HttpServletRequest) req).getUserPrincipal();Object[] args = new Object[] { req, res };SecurityUtil.doAsPrivilege("service", servlet, classTypeUsedInService, args, principal);} else {// 4. servlet為DispatcherServlet,調起servlet的service()方法servlet.service(request, response);}} catch (IOException | ServletException | RuntimeException e) {throw e;} catch (Throwable e) {e = ExceptionUtils.unwrapInvocationTargetException(e);ExceptionUtils.handleThrowable(e);throw new ServletException(sm.getString("filterChain.servlet"), e);} finally {if (ApplicationDispatcher.WRAP_SAME_OBJECT) {lastServicedRequest.set(null);lastServicedResponse.set(null);}}
}// 繼承關系:DispatcherServlet < FrameworkServlet < HttpServletBean < HttpServlet
// DispatcherServlet本身沒有重載service()方法,該方法由HttpServlet提供
// 源碼位置:javax.servlet.http.HttpServlet
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {// 強制轉了一下request和response的類型,也就是只有HttpServletRequest、HttpServletResponse類型才能往下處理HttpServletRequest request;HttpServletResponse response;try {request = (HttpServletRequest) req;response = (HttpServletResponse) res;} catch (ClassCastException e) {throw new ServletException(lStrings.getString("http.non_http"));}// 5. 調service接口,方法名稱雖然相同但參數類型不一樣service(request, response);
}// 源碼位置:javax.servlet.http.HttpServlet
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String method = req.getMethod();// 根據HTTP的Method(GET、POST等)來分別處理,這里只看GET方法,其它方法類似if (method.equals(METHOD_GET)) {long lastModified = getLastModified(req);if (lastModified == -1) {// 6. 處理GET請求doGet(req, resp);} else {// 省略部分代碼}} 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 {//// Note that this means NO servlet supports whatever// method was requested, anywhere on this server.//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);}
}// FrameworkServlet重載了父類的doGet方法
// 源碼位置:org.springframework.web.servlet.FrameworkServlet
protected final void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// 7. 處理請求processRequest(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 {// 8. 處理請求doService(request, response);}catch (ServletException | IOException ex) {failureCause = ex;throw ex;}catch (Throwable ex) {failureCause = ex;throw new NestedServletException("Request processing failed", ex);}// 省略部分代碼
}// DispatcherServlet重載了父類的doService()方法
// 源碼位置:org.springframework.web.servlet.DispatcherServlet
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {// 省略部分代碼        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));}}}// Make framework objects available to handlers and view objects.request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());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);}RequestPath previousRequestPath = null;if (this.parseRequestPath) {previousRequestPath = (RequestPath) request.getAttribute(ServletRequestPathUtils.PATH_ATTRIBUTE);ServletRequestPathUtils.parseAndCache(request);}try {// 9. 處理請求doDispatch(request, response);}// 省略部分代碼
}// 源碼位置:org.springframework.web.servlet.DispatcherServlet
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);// 10. 根據請求中的URL路徑匹配到Controller提供的響應請求的http接口方法mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());String method = request.getMethod();boolean isGet = HttpMethod.GET.matches(method);if (isGet || HttpMethod.HEAD.matches(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}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);}// 省略部分代碼
}// 源碼位置:org.springframework.web.servlet.DispatcherServlet
// DispatcherServlet在初始化的時候,通過Spring查找實現了org.springframework.web.servlet.HandlerMapping接口的所有類,
// 找到的類存放到handlerMappings變量中,大體上有下面6個是實現了HandlerMapping接口的類:
//     org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
//     org.springframework.boot.autoconfigure.web.servlet.WelcomePageHandlerMapping
//     org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
//     org.springframework.web.servlet.function.support.RouterFunctionMapping
//     org.springframework.boot.autoconfigure.web.servlet.WelcomePageNotAcceptableHandlerMapping
//     org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {if (this.handlerMappings != null) {for (HandlerMapping mapping : this.handlerMappings) {// 11. 這里重點看RequestMappingHandlerMapping對請求的處理//     RequestMappingHandlerMapping的getHandler()邏輯是根據請求里的接口路徑匹配到Controller提供的http接口方法//     如果找到能處理請求的方法則返回,否則嘗試下一個HandlerMapping//     返回的是封裝了RequestMappingHandlerMapping的HandlerExecutionChainHandlerExecutionChain handler = mapping.getHandler(request);if (handler != null) {return handler;}}}return null;
}// 回到DispatcherServlet的doDispatch()繼續處理
// 源碼位置:org.springframework.web.servlet.DispatcherServlet
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);// 10. 根據請求中的URL路徑匹配到Controller提供的響應請求的http接口方法mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 12. 獲取處理請求的HandlerAdapter// mappedHandler.getHandler()就是Controller里的方法HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());String method = request.getMethod();boolean isGet = HttpMethod.GET.matches(method);if (isGet || HttpMethod.HEAD.matches(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}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);}// 省略部分代碼
}// 源碼位置:org.springframework.web.servlet.DispatcherServlet
// DispatcherServlet在初始化的時候,通過Spring查找實現了org.springframework.web.servlet.HandlerAdapter接口的類,
// 找到的類存放到handlerAdapters變量中,大體上有下面4個是實現了HandlerAdapter接口的類:
//     org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
//     org.springframework.web.servlet.function.support.HandlerFunctionAdapter
//     org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter
//     org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {if (this.handlerAdapters != null) {for (HandlerAdapter adapter : this.handlerAdapters) {// 13. RequestMappingHandlerAdapter的supports()邏輯是判斷參數handler是否是org.springframework.web.method.HandlerMethod類型//     從Controller里匹配到的接口方法就是HandlerMethod類型if (adapter.supports(handler)) {return adapter;}}}throw new ServletException("No adapter for handler [" + handler +"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}// 回到DispatcherServlet的doDispatch()繼續處理
// 源碼位置:org.springframework.web.servlet.DispatcherServlet
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);// 10. 根據請求中的URL路徑匹配到Controller提供的響應請求的http接口方法mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}// 12. 獲取處理請求的HandlerAdapter,這里主要看RequestMappingHandlerAdapter// mappedHandler.getHandler()就是Controller里的方法HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());String method = request.getMethod();boolean isGet = HttpMethod.GET.matches(method);if (isGet || HttpMethod.HEAD.matches(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 14. 調用HandlerAdapter的handle()處理請求,以RequestMappingHandlerAdapter為例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) {// 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);}// 13. 處理請求的返回結果,參考13.1processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}// 省略部分代碼
}// 源碼位置:org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter
// 繼承關系:RequestMappingHandlerAdapter < AbstractHandlerMethodAdapter
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 15. 調內部方法處理請求return handleInternal(request, response, (HandlerMethod) handler);
}// 源碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
protected ModelAndView handleInternal(HttpServletRequest request, ttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;checkRequest(request);if (this.synchronizeOnSession) {HttpSession session = request.getSession(false);if (session != null) {Object mutex = WebUtils.getSessionMutex(session);synchronized (mutex) {mav = invokeHandlerMethod(request, response, handlerMethod);}}else {mav = invokeHandlerMethod(request, response, handlerMethod);}}else {// 16. 調HandlerMethod方法處理請求mav = invokeHandlerMethod(request, response, handlerMethod);}if (!response.containsHeader(HEADER_CACHE_CONTROL)) {if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);}else {prepareResponse(response);}}return mav;
}// 源碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
// 初始化RequestMappingHandlerAdapter時會通過RequestMappingHandlerAdapter#afterPropertiesSet()方法初始化27個argumentResolver:
//     org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
//     org.springframework.web.method.annotation.RequestParamMapMethodArgumentResolver
//     org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver
//     org.springframework.web.servlet.mvc.method.annotation.PathVariableMapMethodArgumentResolver
//     org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMethodArgumentResolver
//     org.springframework.web.servlet.mvc.method.annotation.MatrixVariableMapMethodArgumentResolver
//     org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
//     org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
//     org.springframework.web.servlet.mvc.method.annotation.RequestPartMethodArgumentResolver
//     org.springframework.web.method.annotation.RequestHeaderMethodArgumentResolver
//     org.springframework.web.method.annotation.RequestHeaderMapMethodArgumentResolver
//     org.springframework.web.servlet.mvc.method.annotation.ServletCookieValueMethodArgumentResolver
//     org.springframework.web.method.annotation.ExpressionValueMethodArgumentResolver
//     org.springframework.web.servlet.mvc.method.annotation.SessionAttributeMethodArgumentResolver
//     org.springframework.web.servlet.mvc.method.annotation.RequestAttributeMethodArgumentResolver
//     org.springframework.web.servlet.mvc.method.annotation.ServletRequestMethodArgumentResolver
//     org.springframework.web.servlet.mvc.method.annotation.ServletResponseMethodArgumentResolver
//     org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor
//     org.springframework.web.servlet.mvc.method.annotation.RedirectAttributesMethodArgumentResolver
//     org.springframework.web.method.annotation.ModelMethodProcessor
//     org.springframework.web.method.annotation.MapMethodProcessor
//     org.springframework.web.method.annotation.ErrorsMethodArgumentResolver
//     org.springframework.web.method.annotation.SessionStatusMethodArgumentResolver
//     org.springframework.web.servlet.mvc.method.annotation.UriComponentsBuilderMethodArgumentResolver
//     org.springframework.web.servlet.mvc.method.annotation.PrincipalMethodArgumentResolver
//     org.springframework.web.method.annotation.RequestParamMethodArgumentResolver
//     org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
// 初始化RequestMappingHandlerAdapter時會通過RequestMappingHandlerAdapter#afterPropertiesSet()方法初始化15個returnValueHandler:
//     org.springframework.web.servlet.mvc.method.annotation.ModelAndViewMethodReturnValueHandler
//     org.springframework.web.method.annotation.ModelMethodProcessor
//     org.springframework.web.servlet.mvc.method.annotation.ViewMethodReturnValueHandler
//     org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitterReturnValueHandler
//     org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler
//     org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor
//     org.springframework.web.servlet.mvc.method.annotation.HttpHeadersReturnValueHandler
//     org.springframework.web.servlet.mvc.method.annotation.CallableMethodReturnValueHandler
//     org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler
//     org.springframework.web.servlet.mvc.method.annotation.AsyncTaskMethodReturnValueHandler
//     org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
//     org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
//     org.springframework.web.servlet.mvc.method.annotation.ViewNameMethodReturnValueHandler
//     org.springframework.web.method.annotation.MapMethodProcessor
//     org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest = new ServletWebRequest(request, response);try {WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);// 17. 把HandlerMethod封裝到ServletInvocableHandlerMethod中,// 設置上方法參數處理器argumentResolvers、返回值處理器returnValueHandlers、參數名處理器parameterNameDiscovererServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}invocableMethod.setDataBinderFactory(binderFactory);invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);// 18. 創建ModelAndViewContainer,用于承載請求結果ModelAndViewContainer mavContainer = new ModelAndViewContainer();mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));modelFactory.initModel(webRequest, mavContainer, invocableMethod);mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);asyncWebRequest.setTimeout(this.asyncRequestTimeout);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.setTaskExecutor(this.taskExecutor);asyncManager.setAsyncWebRequest(asyncWebRequest);asyncManager.registerCallableInterceptors(this.callableInterceptors);asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);if (asyncManager.hasConcurrentResult()) {Object result = asyncManager.getConcurrentResult();mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];asyncManager.clearConcurrentResult();LogFormatUtils.traceDebug(logger, traceOn -> {String formatted = LogFormatUtils.formatValue(result, !traceOn);return "Resume with async result [" + formatted + "]";});invocableMethod = invocableMethod.wrapConcurrentResult(result);}// 19. 調用ServletInvocableHandlerMethod的invokeAndHandle方法處理請求invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}
}// 源碼位置:org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {// 20. 處理請求Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);setResponseStatus(webRequest);if (returnValue == null) {if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {disableContentCachingIfNecessary(webRequest);mavContainer.setRequestHandled(true);return;}}else if (StringUtils.hasText(getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers != null, "No return value handlers");try {this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);}throw ex;}
}// 源碼位置:org.springframework.web.method.support.InvocableHandlerMethod
// 繼承關系:ServletInvocableHandlerMethod < InvocableHandlerMethod
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {// 21. 遍歷Controller接口方法的參數,對參數進行處理Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);if (logger.isTraceEnabled()) {logger.trace("Arguments: " + Arrays.toString(args));}return doInvoke(args);
}// 源碼位置:org.springframework.web.method.support.InvocableHandlerMethod
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {// 22. 從Controller接口方法取到參數列表MethodParameter[] parameters = getMethodParameters();if (ObjectUtils.isEmpty(parameters)) {return EMPTY_ARGS;}// 23. 遍歷參數進行處理Object[] args = new Object[parameters.length];for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];// 24. 處理參數名稱,參數名稱可以有${}等復雜形式的parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);args[i] = findProvidedArgument(parameter, providedArgs);if (args[i] != null) {continue;}// 25. 匹配參數處理器,參數處理器的說明參考上面列表if (!this.resolvers.supportsParameter(parameter)) {throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));}try {// 26. 處理參數得到參數值args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);}catch (Exception ex) {// Leave stack trace for later, exception may actually be resolved and handled...if (logger.isDebugEnabled()) {String exMsg = ex.getMessage();if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {logger.debug(formatArgumentError(parameter, exMsg));}}throw ex;}}return args;
}// 回到InvocableHandlerMethod的invokeForRequest()繼續處理
// 源碼位置:org.springframework.web.method.support.InvocableHandlerMethod
// 繼承關系:ServletInvocableHandlerMethod < InvocableHandlerMethod
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {// 21. 遍歷Controller接口方法的參數,對參數進行處理Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);if (logger.isTraceEnabled()) {logger.trace("Arguments: " + Arrays.toString(args));}// 27. 調Controller接口方法return doInvoke(args);
}// 源碼位置:org.springframework.web.method.support.InvocableHandlerMethod
protected Object doInvoke(Object... args) throws Exception {Method method = getBridgedMethod();try {if (KotlinDetector.isSuspendingFunction(method)) {return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);}// 28. method已經是java.lang.reflect.Method,即通過jdk提供的反射機制執行實際接口并返回結果return method.invoke(getBean(), args);}// 省略部分代碼
}// 回到ServletInvocableHandlerMethod的invokeAndHandle()繼續處理
// 源碼位置:org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {// 20. 處理請求,得到調Controller接口的返回值Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);setResponseStatus(webRequest);if (returnValue == null) {if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {disableContentCachingIfNecessary(webRequest);mavContainer.setRequestHandled(true);return;}}else if (StringUtils.hasText(getResponseStatusReason())) {mavContainer.setRequestHandled(true);return;}mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers != null, "No return value handlers");try {// 29. 調用returnValueHandler處理Controller接口的返回值this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);}throw ex;}
}// 源碼位置:org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {// 30. 匹配ReturnValueHandler,參考12.4.2.1.1HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {boolean isAsyncValue = isAsyncReturnValue(value, returnType);for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {continue;}// 31. 匹配ReturnValueHandlerif (handler.supportsReturnType(returnType)) {return handler;}}return null;
}// 回到HandlerMethodReturnValueHandlerComposite的handleReturnValue()繼續處理
// 源碼位置:org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {// 30. 匹配ReturnValueHandler,參考12.4.2.1.1HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}// 32. 處理返回值,以RequestResponseBodyMethodProcessor為例handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}// 源碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {mavContainer.setRequestHandled(true); // 注意:匹配到RequestResponseBodyMethodProcessor的時候,處理返回結果時已經置位“請求已處理”ServletServerHttpRequest inputMessage = createInputMessage(webRequest);ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);// 33. 使用MessageConverter寫結果到ServletServerHttpResponsewriteWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}// 源碼位置:org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor
// 繼承關系:RequestResponseBodyMethodProcessor < AbstractMessageConverterMethodProcessor
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {Object body;Class<?> valueType;Type targetType;if (value instanceof CharSequence) {body = value.toString();valueType = String.class;targetType = String.class;}else {body = value;valueType = getReturnValueType(body, returnType);targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());}// 34. 返回結果的類型實現了Resource接口時,用Resource的方式處理if (isResourceType(value, returnType)) {outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes");if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null &&outputMessage.getServletResponse().getStatus() == 200) {Resource resource = (Resource) value;try {List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value());body = HttpRange.toResourceRegions(httpRanges, resource);valueType = body.getClass();targetType = RESOURCE_REGION_LIST_TYPE;}catch (IllegalArgumentException ex) {outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength());outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value());}}}MediaType selectedMediaType = null;MediaType contentType = outputMessage.getHeaders().getContentType();boolean isContentTypePreset = contentType != null && contentType.isConcrete();if (isContentTypePreset) {if (logger.isDebugEnabled()) {logger.debug("Found 'Content-Type:" + contentType + "' in response");}selectedMediaType = contentType;}else {HttpServletRequest request = inputMessage.getServletRequest();List<MediaType> acceptableTypes;try {// acceptableTypes:text/html, application/xhtml+xml, image/avif, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.7, */*;q=0.8acceptableTypes = getAcceptableMediaTypes(request);}catch (HttpMediaTypeNotAcceptableException ex) {int series = outputMessage.getServletResponse().getStatus() / 100;if (body == null || series == 4 || series == 5) {if (logger.isDebugEnabled()) {logger.debug("Ignoring error response content (if any). " + ex);}return;}throw ex;}// producibleTypes:text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+jsonList<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);if (body != null && producibleTypes.isEmpty()) {throw new HttpMessageNotWritableException("No converter found for return value of type: " + valueType);}List<MediaType> mediaTypesToUse = new ArrayList<>();for (MediaType requestedType : acceptableTypes) {for (MediaType producibleType : producibleTypes) {if (requestedType.isCompatibleWith(producibleType)) {mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType));}}}if (mediaTypesToUse.isEmpty()) {if (logger.isDebugEnabled()) {logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);}if (body != null) {throw new HttpMediaTypeNotAcceptableException(producibleTypes);}return;}MediaType.sortBySpecificityAndQuality(mediaTypesToUse);for (MediaType mediaType : mediaTypesToUse) {if (mediaType.isConcrete()) {selectedMediaType = mediaType;break;}else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) {selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;break;}}if (logger.isDebugEnabled()) {logger.debug("Using '" + selectedMediaType + "', given " +acceptableTypes + " and supported " + producibleTypes);}}if (selectedMediaType != null) {selectedMediaType = selectedMediaType.removeQualityValue();for (HttpMessageConverter<?> converter : this.messageConverters) {GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?(GenericHttpMessageConverter<?>) converter : null);if (genericConverter != null ?((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :converter.canWrite(valueType, selectedMediaType)) {body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,(Class<? extends HttpMessageConverter<?>>) converter.getClass(),inputMessage, outputMessage);if (body != null) {Object theBody = body;LogFormatUtils.traceDebug(logger, traceOn ->"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");addContentDispositionHeader(inputMessage, outputMessage);if (genericConverter != null) {genericConverter.write(body, targetType, selectedMediaType, outputMessage);}else {// 35. 匹配到messageConverter后進行寫入數據,這里以StringHttpMessageConverter為例((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);}}else {if (logger.isDebugEnabled()) {logger.debug("Nothing to write: null body");}}return;}}}if (body != null) {Set<MediaType> producibleMediaTypes =(Set<MediaType>) inputMessage.getServletRequest().getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) {throw new HttpMessageNotWritableException("No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'");}throw new HttpMediaTypeNotAcceptableException(getSupportedMediaTypes(body.getClass()));}
}// 源碼位置:org.springframework.http.converter.AbstractHttpMessageConverter
public final void write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {final HttpHeaders headers = outputMessage.getHeaders();addDefaultHeaders(headers, t, contentType);if (outputMessage instanceof StreamingHttpOutputMessage) {StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage;streamingOutputMessage.setBody(outputStream -> writeInternal(t, new HttpOutputMessage() {@Overridepublic OutputStream getBody() {return outputStream;}@Overridepublic HttpHeaders getHeaders() {return headers;}}));}else {// 36. 寫入數據writeInternal(t, outputMessage);outputMessage.getBody().flush();}
}
protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException {HttpHeaders headers = outputMessage.getHeaders();if (this.writeAcceptCharset && headers.get(HttpHeaders.ACCEPT_CHARSET) == null) {headers.setAcceptCharset(getAcceptedCharsets());}Charset charset = getContentTypeCharset(headers.getContentType());// 37. 數據寫入到HttpOutputMessage的body里,參考12.4.2.2.1// outputMessage就是之前的ServletServerHttpResponse,繼承關系:ServletServerHttpResponse < ServerHttpResponse < HttpOutputMessageStreamUtils.copy(str, charset, outputMessage.getBody());
}// 回到RequestMappingHandlerAdapter的invokeHandlerMethod()處理View
// 源碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest = new ServletWebRequest(request, response);try {WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);// 17. 把HandlerMethod封裝到ServletInvocableHandlerMethod中,// 設置上方法參數處理器argumentResolvers、返回值處理器returnValueHandlers、參數名處理器parameterNameDiscovererServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}invocableMethod.setDataBinderFactory(binderFactory);invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);// 18. 創建ModelAndViewContainer,用于承載請求結果ModelAndViewContainer mavContainer = new ModelAndViewContainer();mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));modelFactory.initModel(webRequest, mavContainer, invocableMethod);mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);asyncWebRequest.setTimeout(this.asyncRequestTimeout);WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);asyncManager.setTaskExecutor(this.taskExecutor);asyncManager.setAsyncWebRequest(asyncWebRequest);asyncManager.registerCallableInterceptors(this.callableInterceptors);asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);if (asyncManager.hasConcurrentResult()) {Object result = asyncManager.getConcurrentResult();mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];asyncManager.clearConcurrentResult();LogFormatUtils.traceDebug(logger, traceOn -> {String formatted = LogFormatUtils.formatValue(result, !traceOn);return "Resume with async result [" + formatted + "]";});invocableMethod = invocableMethod.wrapConcurrentResult(result);}// 19. 調用ServletInvocableHandlerMethod的invokeAndHandle方法處理請求invocableMethod.invokeAndHandle(webRequest, mavContainer);if (asyncManager.isConcurrentHandlingStarted()) {return null;}// 38. 處理返回的結果,結果信息在mavContainerreturn getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}
}// 源碼位置:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {modelFactory.updateModel(webRequest, mavContainer);// 39. 參考步驟33,有些ReturnValueHandler會提前置為“請求已處理”,一般應用于直接返回數據而不需要渲染View的場景if (mavContainer.isRequestHandled()) {return null;}// 40. 初始化ModelAndView并返回ModelMap model = mavContainer.getModel();ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());if (!mavContainer.isViewReference()) {mav.setView((View) mavContainer.getView());}if (model instanceof RedirectAttributes) {Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);if (request != null) {RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);}}return mav;
}// 源碼位置:org.springframework.web.servlet.DispatcherServlet
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,@Nullable Exception exception) throws Exception {boolean errorView = false;// 41. 如果有一次,則處理為異常ModelAndViewif (exception != null) {if (exception instanceof ModelAndViewDefiningException) {logger.debug("ModelAndViewDefiningException encountered", exception);mv = ((ModelAndViewDefiningException) exception).getModelAndView();}else {Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);mv = processHandlerException(request, response, handler, exception);errorView = (mv != null);}}if (mv != null && !mv.wasCleared()) {// 42. 渲染ModelAndView,如果是返回數據的則mv=null,不需要渲染render(mv, request, response);if (errorView) {WebUtils.clearErrorRequestAttributes(request);}}else {if (logger.isTraceEnabled()) {logger.trace("No view rendering, null ModelAndView returned.");}}if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {return;}if (mappedHandler != null) {mappedHandler.triggerAfterCompletion(request, response, null);}
}

2.2 源碼分析

如果簡略一點地理解,則可以理解為:
  • 由HandlerMapping匹配到Controller的方法HandlerMethod;
  • 由HandlerMethodArgumentResolver把請求的參數處理為Controller方法的參數;
  • 由ReturnValueHandler把Controller方法的返回值處理為響應數據,其里面包含了MessageConverter對Controller方法的返回值進行轉換處理;
  • 由ViewResolver把Controller方法的返回值處理為響應的視圖,由
  • 由DispatcherServlet進行協調HandlerMapping、HandlerAdapter、ViewResolver這些對象的關系;
  • HandlerAdapter則協調HandlerMethodArgumentResolver、ReturnValueHandler的關系;
  • DispatcherServlet對外暴露service接口,由tomcat服務器在接收請求后,通過service接口派發請求。

2.2.1 DispatcherServlet初始化

在發起請求的時候,tomcat處理請求會調到StandardWrapperValve的invoke方法,在這個方法才初始化DispatcherServlet:
// 源碼位置:org.apache.catalina.core.StandardWrapperValve
public void invoke(Request request, Response response) throws IOException, ServletException {       boolean unavailable = false;Throwable throwable = null;long t1 = System.currentTimeMillis();requestCount.incrementAndGet();StandardWrapper wrapper = (StandardWrapper) getContainer();Servlet servlet = null;Context context = (Context) wrapper.getParent();// 省略部分代碼try {if (!unavailable) {// 1. 創建DispatcherServletservlet = wrapper.allocate();}} // 省略部分代碼MessageBytes requestPathMB = request.getRequestPathMB();DispatcherType dispatcherType = DispatcherType.REQUEST;if (request.getDispatcherType() == DispatcherType.ASYNC) {dispatcherType = DispatcherType.ASYNC;}request.setAttribute(Globals.DISPATCHER_TYPE_ATTR, dispatcherType);request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR, requestPathMB);ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);// 省略其它代碼
}// 源碼位置:org.apache.catalina.core.StandardWrapper
public Servlet allocate() throws ServletException {if (unloading) {throw new ServletException(sm.getString("standardWrapper.unloading", getName()));}boolean newInstance = false;if (!singleThreadModel) {if (instance == null || !instanceInitialized) {synchronized (this) {if (instance == null) {try {if (log.isDebugEnabled()) {log.debug("Allocating non-STM instance");}instance = loadServlet();newInstance = true;if (!singleThreadModel) {countAllocated.incrementAndGet();}} catch (ServletException e) {throw e;} catch (Throwable e) {ExceptionUtils.handleThrowable(e);throw new ServletException(sm.getString("standardWrapper.allocate"), e);}}if (!instanceInitialized) {// 2. 初始化DispatcherServletinitServlet(instance);}}}// 省略其它代碼}// 省略其它代碼
}
private synchronized void initServlet(Servlet servlet) throws ServletException {if (instanceInitialized && !singleThreadModel) {return;}try {if (Globals.IS_SECURITY_ENABLED) {boolean success = false;try {Object[] args = new Object[] { facade };SecurityUtil.doAsPrivilege("init", servlet, classType, args);success = true;} finally {if (!success) {SecurityUtil.remove(servlet);}}} else {// 3. 初始化DispatcherServletservlet.init(facade);}instanceInitialized = true;}// 省略其它代碼
}// 回到StandardWrapperValve的invoke()繼續處理
// 源碼位置:org.apache.catalina.core.StandardWrapperValve
public void invoke(Request request, Response response) throws IOException, ServletException {       boolean unavailable = false;Throwable throwable = null;long t1 = System.currentTimeMillis();requestCount.incrementAndGet();StandardWrapper wrapper = (StandardWrapper) getContainer();Servlet servlet = null;Context context = (Context) wrapper.getParent();// 省略部分代碼try {if (!unavailable) {// 1. 創建DispatcherServletservlet = wrapper.allocate();}} // 省略部分代碼MessageBytes requestPathMB = request.getRequestPathMB();DispatcherType dispatcherType = DispatcherType.REQUEST;if (request.getDispatcherType() == DispatcherType.ASYNC) {dispatcherType = DispatcherType.ASYNC;}request.setAttribute(Globals.DISPATCHER_TYPE_ATTR, dispatcherType);request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR, requestPathMB);// 4. 把DispatcherServlet設置到ApplicationFilterChain中進行服務響應ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);// 省略其它代碼
}

2.2.2 HandlerMapping和HandlerInterceptor的初始化

HandlerMapping和HandlerInterceptor都是在WebMvcConfigurationSupport初始化的:

// 源碼位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
// WebMvcConfigurationSupport會創建HandlerMapping,大致有以下幾個(源碼只列了兩個為例):
//    org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
//    org.springframework.boot.autoconfigure.web.servlet.WelcomePageHandlerMapping
//    org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
//    org.springframework.web.servlet.function.support.RouterFunctionMapping
//    org.springframework.boot.autoconfigure.web.servlet.WelcomePageNotAcceptableHandlerMapping
//    org.springframework.web.servlet.handler.SimpleUrlHandlerMapping
@Bean
@SuppressWarnings("deprecation")
public RequestMappingHandlerMapping requestMappingHandlerMapping(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {// 1. 創建RequestMappingHandlerMappingRequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();mapping.setOrder(0);mapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider)); // 設置HandlerInterceptormapping.setContentNegotiationManager(contentNegotiationManager);mapping.setCorsConfigurations(getCorsConfigurations());// 省略其它代碼
}
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {// 2. 創建RequestMappingHandlerMappingreturn new RequestMappingHandlerMapping();
}// 回到WebMvcConfigurationSupport的requestMappingHandlerMapping()繼續處理
// 源碼位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
public RequestMappingHandlerMapping requestMappingHandlerMapping(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {// 1. 創建RequestMappingHandlerMappingRequestMappingHandlerMapping mapping = createRequestMappingHandlerMapping();mapping.setOrder(0);// 3. 設置HandlerInterceptormapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider)); mapping.setContentNegotiationManager(contentNegotiationManager);mapping.setCorsConfigurations(getCorsConfigurations());// 省略其它代碼
}
protected final Object[] getInterceptors(FormattingConversionService mvcConversionService,ResourceUrlProvider mvcResourceUrlProvider) {if (this.interceptors == null) {InterceptorRegistry registry = new InterceptorRegistry();// 4. 加載自定義的HandlerInterceptoraddInterceptors(registry);// 默認的兩個HandlerInterceptorregistry.addInterceptor(new ConversionServiceExposingInterceptor(mvcConversionService));registry.addInterceptor(new ResourceUrlProviderExposingInterceptor(mvcResourceUrlProvider));this.interceptors = registry.getInterceptors();}return this.interceptors.toArray();
}// 源碼位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
@Bean
public HandlerMapping viewControllerHandlerMapping(@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {ViewControllerRegistry registry = new ViewControllerRegistry(this.applicationContext);addViewControllers(registry);// 5. 創建HandlerMappingAbstractHandlerMapping handlerMapping = registry.buildHandlerMapping();if (handlerMapping == null) {return null;}PathMatchConfigurer pathConfig = getPathMatchConfigurer();if (pathConfig.getPatternParser() != null) {handlerMapping.setPatternParser(pathConfig.getPatternParser());}else {handlerMapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());handlerMapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());}handlerMapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));handlerMapping.setCorsConfigurations(getCorsConfigurations());return handlerMapping;
}
protected SimpleUrlHandlerMapping buildHandlerMapping() {if (this.registrations.isEmpty() && this.redirectRegistrations.isEmpty()) {return null;}Map<String, Object> urlMap = new LinkedHashMap<>();for (ViewControllerRegistration registration : this.registrations) {urlMap.put(registration.getUrlPath(), registration.getViewController());}for (RedirectViewControllerRegistration registration : this.redirectRegistrations) {urlMap.put(registration.getUrlPath(), registration.getViewController());}// 6. 創建SimpleUrlHandlerMappingreturn new SimpleUrlHandlerMapping(urlMap, this.order);
}// 回到WebMvcConfigurationSupport的viewControllerHandlerMapping()繼續處理
// 源碼位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
@Bean
public HandlerMapping viewControllerHandlerMapping(@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {ViewControllerRegistry registry = new ViewControllerRegistry(this.applicationContext);addViewControllers(registry);// 5. 創建HandlerMappingAbstractHandlerMapping handlerMapping = registry.buildHandlerMapping();if (handlerMapping == null) {return null;}PathMatchConfigurer pathConfig = getPathMatchConfigurer();if (pathConfig.getPatternParser() != null) {handlerMapping.setPatternParser(pathConfig.getPatternParser());}else {handlerMapping.setUrlPathHelper(pathConfig.getUrlPathHelperOrDefault());handlerMapping.setPathMatcher(pathConfig.getPathMatcherOrDefault());}// 7. 設置HandlerInterceptor,把同樣的Interceptor設置到handlerMapping里handlerMapping.setInterceptors(getInterceptors(conversionService, resourceUrlProvider));handlerMapping.setCorsConfigurations(getCorsConfigurations());return handlerMapping;
}// DispatcherServlet初始化的時候,會初始化HandlerMapping
// 源碼位置:org.springframework.web.servlet.DispatcherServlet
private void initHandlerMappings(ApplicationContext context) {this.handlerMappings = null;// 8. 初始化HandlerMapping,先找接口HandlerMapping,如果沒有則從Context里取名稱位handlerMapping的if (this.detectAllHandlerMappings) {Map<String, HandlerMapping> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerMappings = new ArrayList<>(matchingBeans.values());AnnotationAwareOrderComparator.sort(this.handlerMappings);}}else {try {// HANDLER_MAPPING_BEAN_NAME = "handlerMapping"HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);this.handlerMappings = Collections.singletonList(hm);}catch (NoSuchBeanDefinitionException ex) {}}if (this.handlerMappings == null) {this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);if (logger.isTraceEnabled()) {logger.trace("No HandlerMappings declared for servlet '" + getServletName() +"': using default strategies from DispatcherServlet.properties");}}for (HandlerMapping mapping : this.handlerMappings) {if (mapping.usesPathPatterns()) {this.parseRequestPath = true;break;}}
}// DispatcherServlet在響應請求的時候,會使用HandlerMapping匹配Controller方法,同時執行HandlerInterceptor
// 源碼位置:org.springframework.web.servlet.DispatcherServlet
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);// 9. mappedHandler為HandlerExecutionChain,里面包含了HandlerMethod和HandlerInterceptor// 通過HandlerMapping找到HandlerMethod,創建HandlerExecutionChain時把HandlerMethod設置到里面// 在初始化的時候每個HandlerMapping都設置了所有的HandlerInterceptor,取出來設置到HandlerExecutionChain里面mappedHandler = getHandler(processedRequest);if (mappedHandler == null) {noHandlerFound(processedRequest, response);return;}HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());String method = request.getMethod();boolean isGet = HttpMethod.GET.matches(method);if (isGet || HttpMethod.HEAD.matches(method)) {long lastModified = ha.getLastModified(request, mappedHandler.getHandler());if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {return;}}// 10. 執行HandlerInterceptor攔截器的preHandle()方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// Actually invoke the handler.mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);// 11. 執行HandlerInterceptor攔截器的postHandle()方法mappedHandler.applyPostHandle(processedRequest, response, mv);}// 省略其它代碼
}

2.2.3 RequestMappingHandlerAdapter初始化

RequestMappingHandlerAdapter是在啟動的時候,加載WebMvcAutoConfiguration這個自動配置時作為一個bean初始化的,在DispatcherServlet初始化的時候通過bean查找設置到DispatcherServlet里:

// 源碼位置:org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration
@AutoConfiguration(after = { DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class })
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
public class WebMvcAutoConfiguration {@Configuration(proxyBeanMethods = false)@EnableConfigurationProperties(WebProperties.class)public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware {@Bean@Overridepublic RequestMappingHandlerAdapter requestMappingHandlerAdapter(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcValidator") Validator validator) {// 調父類方法創建RequestMappingHandlerAdapterRequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter(contentNegotiationManager, conversionService, validator);adapter.setIgnoreDefaultModelOnRedirect(this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());return adapter;}}
}
// 源碼位置:org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,@Qualifier("mvcConversionService") FormattingConversionService conversionService,@Qualifier("mvcValidator") Validator validator) {// 創建RequestMappingHandlerAdapterRequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();adapter.setContentNegotiationManager(contentNegotiationManager);adapter.setMessageConverters(getMessageConverters());adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));adapter.setCustomArgumentResolvers(getArgumentResolvers());adapter.setCustomReturnValueHandlers(getReturnValueHandlers());if (jackson2Present) {adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));}AsyncSupportConfigurer configurer = getAsyncSupportConfigurer();if (configurer.getTaskExecutor() != null) {adapter.setTaskExecutor(configurer.getTaskExecutor());}if (configurer.getTimeout() != null) {adapter.setAsyncRequestTimeout(configurer.getTimeout());}adapter.setCallableInterceptors(configurer.getCallableInterceptors());adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());return adapter;
}
protected RequestMappingHandlerAdapter createRequestMappingHandlerAdapter() {// 繼承關系:RequestMappingHandlerAdapter < AbstractHandlerMethodAdapter < HandlerAdapterreturn new RequestMappingHandlerAdapter();
}// 源碼位置:org.springframework.web.servlet.DispatcherServlet
private void initHandlerAdapters(ApplicationContext context) {this.handlerAdapters = null;if (this.detectAllHandlerAdapters) {// 用Spring工具,查找實現了HandlerAdapter接口的類,RequestMappingHandlerAdapter實現了HandlerAdapter接口Map<String, HandlerAdapter> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);if (!matchingBeans.isEmpty()) {this.handlerAdapters = new ArrayList<>(matchingBeans.values());AnnotationAwareOrderComparator.sort(this.handlerAdapters);}}else {try {// HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter"// 根據名稱和類型查找對應的beanHandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);this.handlerAdapters = Collections.singletonList(ha);}catch (NoSuchBeanDefinitionException ex) {}}if (this.handlerAdapters == null) {// 查找在DispatcherServlet.properties(與DispatcherServlet同目錄)中配置的HandlerAdapterthis.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);if (logger.isTraceEnabled()) {logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +"': using default strategies from DispatcherServlet.properties");}}
}

2.2.4 ViewResolver初始化

ViewResolver也是在WebMvcAutoConfiguration中自動配置為Bean,然后在DispatcherServlet中用Spring工具查找所有實現了ViewResolver接口得到:

// org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {InternalResourceViewResolver resolver = new InternalResourceViewResolver();resolver.setPrefix(this.mvcProperties.getView().getPrefix());resolver.setSuffix(this.mvcProperties.getView().getSuffix());return resolver;
}
@Bean
@ConditionalOnBean(View.class)
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver() {BeanNameViewResolver resolver = new BeanNameViewResolver();resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10);return resolver;
}
@Bean
@ConditionalOnBean(ViewResolver.class)
@ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class)
public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) {ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class));// ContentNegotiatingViewResolver uses all the other view resolvers to locateresolver.setOrder(Ordered.HIGHEST_PRECEDENCE);return resolver;
}// 源碼位置:org.springframework.web.servlet.DispatcherServlet
private void initViewResolvers(ApplicationContext context) {this.viewResolvers = null;if (this.detectAllViewResolvers) {// 根據ViewResolver接口獲取到已經注冊的BeanMap<String, ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);if (!matchingBeans.isEmpty()) {this.viewResolvers = new ArrayList<>(matchingBeans.values());AnnotationAwareOrderComparator.sort(this.viewResolvers);}}else {try {ViewResolver vr = context.getBean(VIEW_RESOLVER_BEAN_NAME, ViewResolver.class);this.viewResolvers = Collections.singletonList(vr);}catch (NoSuchBeanDefinitionException ex) {}}if (this.viewResolvers == null) {this.viewResolvers = getDefaultStrategies(context, ViewResolver.class);if (logger.isTraceEnabled()) {logger.trace("No ViewResolvers declared for servlet '" + getServletName() +"': using default strategies from DispatcherServlet.properties");}}
}

3 架構一小步

SpringMVC在調Controller的接口方法前后會分別先調HandlerInterceptor的preHandle()和postHandle(),后面可以用來做擴展。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/915244.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/915244.shtml
英文地址,請注明出處:http://en.pswp.cn/news/915244.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

論文分享 | LABRADOR:響應引導的針對物聯網設備的黑盒模糊測試

由于固件仿真以及重托管的技術挑戰&#xff0c;部分企業級 IoT 設備只能在黑盒環境下進行模糊測試。分享一篇發表于 2024 年 S&P 會議的論文 Labrador&#xff0c;它利用響應來引導請求變異&#xff0c;實現了針對 IoT 設備的高效黑盒模糊測試。 猴先生說&#xff1a;這篇論…

WPF為啟動界面(Splash Screen)添加背景音樂

1. 添加音頻文件到項目 將音頻文件&#xff08;如.mp3/.wav&#xff09;放入項目文件夾&#xff08;如Resources&#xff09;在解決方案資源管理器中右鍵文件 → 屬性&#xff1a; 生成操作&#xff1a;選擇Resource&#xff08;嵌入資源&#xff09;或Content&#xff08;內容…

【Jmeter】報錯:An error occured:Unknown arg

問題 調試Jmeter時&#xff0c;報錯&#xff1a;‘An error occurred: Unknown arg: l’&#xff0c;腳本如下&#xff1a; $JMETER_PATH -n -t "$target_jmx" -l "$SCENARIO_REPORT_DIR/result_${threads}.jtl" -e -o "$SCENARIO_REPORT_DIR/htm…

vue3使用KeepAlive組件及一些注意事項

目錄 一、KeepAlive的作用 二、緩存組件配置 2.1、過濾緩存組件 2.2、最大緩存實例數 三、KeepAlive組件的生命周期 四、錯誤用法 4.1、緩存v-if包裹的動態組件 4.2、拼寫錯誤 一、KeepAlive組件的作用 首先&#xff0c;keep-alive是一個vue的內置組件&#xff0c;官網…

辛普森悖論

辛普森悖論第一步&#xff1a;概念拆解想象你在比較兩個班級的考試成績&#xff1a;?第一天?&#xff1a;實驗組&#xff08;1個學生考了90分&#xff09;&#xff0c;對照組&#xff08;99個學生平均考了80分&#xff09;?第二天?&#xff1a;實驗組&#xff08;50個學生平…

有效的括號數據結構oj題(力口20)

目錄 目錄 題目描述 題目分析解析 解決代碼 寫題感悟&#xff1a; 題目描述 還有實例 題目分析解析 對于這個題目&#xff0c;我們首先有效字符串需要滿足什么&#xff0c;第一個左右括號使用相同類型的括號&#xff0c;這好理解&#xff0c;無非就是小括號和小括號大括號…

Mock 單元測試

作者&#xff1a;小凱 沉淀、分享、成長&#xff0c;讓自己和他人都能有所收獲&#xff01; 本文的宗旨在于通過簡單干凈實踐的方式教會讀者&#xff0c;如何使用 Mock (opens new window)進行工程的單元測試&#xff0c;以便于驗證系統中的獨立模塊功能的健壯性。 從整個工程所…

MySQL 深度性能優化配置實戰指南

?? 一、硬件與系統層優化:夯實性能基石 ??硬件選型策略?? ??CPU??:讀密集型場景選擇多核CPU(如32核);寫密集型場景選擇高主頻CPU(如3.5GHz+)。 ??內存??:建議≥64GB,??緩沖池命中率≥99%?? 是性能關鍵指標。 ??存儲??:??必用NVMe SSD??,I…

Visual Studio Code(VSCode)中設置中文界面

在VS Code中設置中文界面主要有兩種方法&#xff1a;通過擴展市場安裝中文語言包或通過命令面板直接切換語言。?方法一&#xff1a;通過擴展市場安裝中文語言包?打開VS Code&#xff0c;點擊左側活動欄的"擴展"圖標&#xff08;或按CtrlShiftX&#xff09;。在搜索…

叉車機器人如何實現托盤精準定位?這項核心技術的原理和應用是什么?

隨著智慧物流和智能制造的加速發展&#xff0c;智能化轉型成為提升效率、降低成本的關鍵路徑&#xff0c;叉車機器人&#xff08;AGV/AMR叉車&#xff09;在倉儲、制造、零售等行業中的應用日益廣泛。 其中&#xff0c;托盤定位技術是實現其高效、穩定作業的核心環節之一&…

NO.6數據結構樹|二叉樹|滿二叉樹|完全二叉樹|順序存儲|鏈式存儲|先序|中序|后序|層序遍歷

樹與二叉樹的基本知識 樹的術語結點&#xff1a; 樹中的每個元素都稱為結點&#xff0c; 例如上圖中的 A,B,C…根結點&#xff1a; 位于樹頂部的結點&#xff0c; 它沒有父結點,比如 A 結點。父結點&#xff1a; 若一個結點有子結點&#xff0c; 那么這個結點就稱為其子結點的父…

數據集下載網站

名稱簡介鏈接Kaggle世界上最大的數據科學競賽平臺之一&#xff0c;有大量結構化、圖像、文本等數據集可直接下載?支持一鍵下載、APIPapers with Code可按任務&#xff08;如圖像分類、文本生成等&#xff09;查找模型與數據集&#xff0c;標注 SOTA?與論文強關聯Hugging Face…

Tomcat 生產 40 條軍規:容量規劃、調優、故障演練與安全加固

&#xff08;一&#xff09;容量規劃 6 條 軍規 1&#xff1a;線程池公式 maxThreads ((并發峰值 平均 RT) / 1000) 冗余 20 %&#xff1b; 踩坑&#xff1a;壓測 2000 QPS、RT 200 ms&#xff0c;理論 maxThreads500&#xff0c;線上卻設 150 導致排隊。軍規 2&#xff1a;…

深入解析 Amazon Q:AWS 推出的企業級生成式 AI 助手

在人工智能助手競爭激烈的當下&#xff0c;AWS 重磅推出的 Amazon Q 憑借其強大的企業級整合能力&#xff0c;正成為開發者提升生產力的新利器。隨著生成式 AI 技術席卷全球&#xff0c;各大云廠商紛紛布局智能助手領域。在 2023 年 re:Invent 大會上&#xff0c;AWS 正式推出了…

物流自動化WMS和WCS技術文檔

導語大家好&#xff0c;我是社長&#xff0c;老K。專注分享智能制造和智能倉儲物流等內容。歡迎大家使用我們的倉儲物流技術AI智能體。新書《智能物流系統構成與技術實踐》新書《智能倉儲項目出海-英語手冊&#xff0c;必備&#xff01;》完整版文件和更多學習資料&#xff0c;…

Web3.0 實戰項目、簡歷打造、精準投遞+面試準備

目錄 一、獲取真實企業級 Web3.0 項目的 5 種方式 1. 參與開源項目&#xff08;推薦指數&#xff1a;?????&#xff09; 2. 參與黑客松&#xff08;Hackathon&#xff09; 3. 遠程實習 & DAO 協作項目&#xff08;兼職也可&#xff09; 4. Web3 Startup 實戰項目合…

pymongo庫:簡易方式存取數據

文檔 基礎使用 前提&#xff1a;開發機器已安裝mongo配置環境&#xff0c;已啟動服務。 macOS啟動服務&#xff1a;brew services start mongodb-community8.0 macOS停止服務&#xff1a;brew services stop mongodb-community8.0安裝&#xff1a;python3 -m pip install pym…

Java 線程池與多線程并發編程實戰全解析:從異步任務調度到設計模式落地,200 + 核心技巧、避坑指南與業務場景結合

多線程編程在現代軟件開發中扮演著至關重要的角色&#xff0c;它能夠顯著提升應用程序的性能和響應能力。通過合理利用異步線程、多線程以及線程池等技術&#xff0c;我們可以更高效地處理復雜任務&#xff0c;優化系統資源的使用。同時&#xff0c;在實際應用中&#xff0c;我…

gitee 分支切換

ssh-keygen -t rsa -C "pengchengzhangcplaser.com.cn" ssh -T gitgitee.comgit remote add origin 倉庫地址git config --global user.email "youexample.com"git config --global user.name "Your Name"# 1. 更新遠程信息 git fetch origin# …

Vue3生命周期函數

在 Vue 3 中&#xff0c;生命周期鉤子函數是指組件從創建到銷毀的整個過程中&#xff0c;Vue 自動調用的一些特定函數。它們讓你能夠在組件的不同階段執行一些自定義操作。Vue 3 提供了組合式 API 和選項式 API 兩種方式來定義生命周期鉤子。1. onBeforeMount (組合式 API)作用…