小架构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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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)作用…