【面向过程】springboot接受到一个请求后做了什么

2023-12-16

从启动开始

先从springboot怎么启动开始,启动完成之后建立web容器才能在容器中处理http请求

什么是 springboot

image.png

在spring的官网上,对springboot这样描述到:

Spring Boot 可以轻松创建独立的、生产级的基于 Spring 的应用程序,您可以“直接运行”。 我们对 Spring 平台和第三方库采取固执己见的观点,因此您可以轻松上手。 大多数 Spring Boot 应用程序需要最少的 Spring 配置。

springboot启动的时候怎么加载web容器

启动一个 springboot 应用,我们通常是这样启动


  

java

复制代码

package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }

我们可以看到的是,在程序main函数内,我们只进行了 SpringApplication.run 操作,我们来看看 #run 方法之后,进行了什么,在SpringApplication类中,这样定义了 #run 方法


  

java

复制代码

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class[]{primarySource}, args); }

最后执行的是这个方法


  

java

复制代码

public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList(); this.configureHeadlessProperty(); SpringApplicationRunListeners listeners = this.getRunListeners(args); listeners.starting(); Collection exceptionReporters; try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments); this.configureIgnoreBeanInfo(environment); Banner printedBanner = this.printBanner(environment); context = this.createApplicationContext(); exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); this.prepareContext(context, environment, listeners, applicationArguments, printedBanner); this.refreshContext(context); this.afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { (new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context); this.callRunners(context, applicationArguments); } catch (Throwable var10) { this.handleRunFailure(context, var10, exceptionReporters, listeners); throw new IllegalStateException(var10); } try { listeners.running(context); return context; } catch (Throwable var9) { this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null); throw new IllegalStateException(var9); } }

怎么构建一个容web器

主要的建立容器的方法在 context = this.createApplicationContext(); 这样代码,这行代码创建了spirng的上下文,来看看他进行了什么操作


  

java

复制代码

protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { switch (this.webApplicationType) { case SERVLET: contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"); break; case REACTIVE: contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"); break; default: contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext"); } } catch (ClassNotFoundException var3) { throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3); } } return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass); }

选择什么类型的web容器

这之中根据 webApplicationType 的值来构建容器类型,这个值是主要定义的


  

java

复制代码

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.sources = new LinkedHashSet(); this.bannerMode = Mode.CONSOLE; this.logStartupInfo = true; this.addCommandLineProperties = true; this.addConversionService = true; this.headless = true; this.registerShutdownHook = true; this.additionalProfiles = new HashSet(); this.isCustomEnvironment = false; this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass(); }

WebApplicationType.deduceFromClasspath(); 方法如下


  

java

复制代码

private static final String[] SERVLET_INDICATOR_CLASSES = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"}; static WebApplicationType deduceFromClasspath() { if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) { return REACTIVE; } else { String[] var0 = SERVLET_INDICATOR_CLASSES; int var1 = var0.length; for(int var2 = 0; var2 < var1; ++var2) { String className = var0[var2]; if (!ClassUtils.isPresent(className, (ClassLoader)null)) { return NONE; } } return SERVLET; } }

总结

由源码中可以看到的是,启动时根据 DispatcherServlet 版本来启动springboot应用,我们按 SERVLET 类型的 DispatcherServlet 来进行分析, org.springframework.web.servlet.DispatcherServlet 类位于 spring-webmvc 包下,类注解是这样描述它自己的

Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers or HTTP-based remote service exporters. Dispatches to registered handlers for processing a web request, providing convenient mapping and exception handling facilities. This servlet is very flexible: It can be used with just about any workflow, with the installation of the appropriate adapter classes.

HTTP 请求处理程序/控制器的中央调度程序,例如 适用于 Web UI 控制器或基于 HTTP 的远程服务导出器。 分派到已注册的处理程序来处理 Web 请求,提供方便的映射和异常处理设施。 该 servlet 非常灵活:通过安装适当的适配器类,它可以与几乎任何工作流程一起使用。

怎么处理HTTP请求

Servlet

我们都知道springboot默认基于tomcat容器,通过IDEA对 DispatcherServlet 类的关系分析,我们可以看到他继承的父类 FrameworkServlet 继承了 HttpServlet HttpServlet 继承了 HttpServlet

image.png

作为javaboy的我们,熟知 HttpServlet 干的就是处理http请求,比如 doGet doPost 等,那我们就可以知道 DispatcherServlet 干的活,就是处理请求, FrameworkServlet 还实现了 ApplicationContextAware ,阅读 FrameworkServlet 的源码,我们可以看到他重写了处理请求的方法,且调用的都是 processRequest(HttpServletRequest request, HttpServletResponse response) 方法

image.png

image.png

处理请求的方法

来分析下 processRequest 方法


  

java

复制代码

/** * Process this request, publishing an event regardless of the outcome. * <p>The actual event handling is performed by the abstract * {@link #doService} template method. */ protected final void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null; //获取当前线程的上下文(threadLocal实现) LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); //构建当前请求的 localeContext 用于本地话语言实现 LocaleContext localeContext = buildLocaleContext(request); //获取当前线程的 RequestAttributes RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); //构建 ServletRequestAttributes ,ServletRequestAttributes有四个属性[request,response,sessionMutex,sessionId] ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); //获取当前请求的 WebAsyncManager[管理异步请求处理的中心类,异步场景从线程(T1)中的请求处理开始。并发请求处理可以通过调用startCallableProcessing或startDeferredResultProcessing来启动,两者都在单独的线程(T2)中产生结果。保存结果并将请求分派给容器,以便在第三个线程(T3)中继续使用保存的结果进行处理。在分派的线程(T3)中,可以通过getConcurrentResult()访问保存的结果,也可以通过hasConcurrentResult()检测它的存在。] WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); //把当前请求注册给异步请求处理器 asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); //初始化contextHolder,就是set LocaleContext和RequestContextHolder的RequestAttributes initContextHolders(request, localeContext, requestAttributes); try { //子类实现 doService(request, response); } catch (ServletException | IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed", ex); } finally { resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null) { requestAttributes.requestCompleted(); } logResult(request, response, failureCause, asyncManager); publishRequestHandledEvent(request, response, startTime, failureCause); } }

源码中可以看到的是主要的处理是 #doService 方法, DispatcherServlet 中重写了这个方法


  

java

复制代码

/** * Exposes the DispatcherServlet-specific request attributes and delegates to {@link #doDispatch} * for the actual dispatching. */ @Override protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { //输出请求日志 logRequest(request); // Keep a snapshot of the request attributes in case of an include, // to be able to restore the original attributes after the include. 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); } //以上代码设置请求的attribute try { doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { // Restore the original attribute snapshot, in case of an include. if (attributesSnapshot != null) { restoreAttributesAfterInclude(request, attributesSnapshot); } } } }

dispatch

DispatcherServlet #doService 方法中,对request进行了处理后,主要的处理方法是在 #doDispatch


  

java

复制代码

/** * 处理对处理程序的实际调度。 * 处理程序将通过按顺序应用 servlet 的 HandlerMappings 来获得。 * HandlerAdapter 将通过查询 servlet 已安装的 HandlerAdapter 来找到第一个支持处理程序类的 HandlerAdapter。 * 所有 HTTP 方法均由该方法处理。 由 HandlerAdapter 或处理程序本身来决定哪些方法是可接受的。 * * @param request current HTTP request * @param response current HTTP response * @throws Exception in case of any kind of processing failure */ 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); // 确定当前请求的处理程序。 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { //没找到响应处理器,返回 SC_NOT_FOUND = 404; noHandlerFound(processedRequest, response); return; } //取得当前请求的处理程序适配器,如果没找到会抛出ServletException - No adapter for handler HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 获取请求方法 String method = request.getMethod(); //处理last-modified boolean isGet = "GET".equals(method); if (isGet || "HEAD".equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // Actually invoke the handler. invoke给对应的handler,handler处理完request和reponse后会返回对应的 mv(ModelAndView) 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); } //↑ 以上catch处理对应的错误 //↓ 处理dispath结果,结果不是个modelView就是个要处理成modelView的exception processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { // Instead of postHandle and afterCompletion if (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { // Clean up any resources used by a multipart request. if (multipartRequestParsed) { //清楚分包请求的所有占用的资源 cleanupMultipart(processedRequest); } } } }

总结

由上来看,springboot在使用非响应式容器时,处理方式和springMVC一致,只是在简化了很多配置性的操作,让应用可以做到开箱机箱,但是处理请求一样是从接受到请求开始根据路径指向到对应的服务去处理并返回

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【面向过程】springboot接受到一个请求后做了什么 的相关文章

随机推荐

  • Java版 招投标系统简介 招投标系统源码 java招投标系统 招投标系统功能设计

    项目说明 随着公司的快速发展 企业人员和经营规模不断壮大 公司对内部招采管理的提升提出了更高的要求 在企业里建立一个公平 公开 公正的采购环境 最大限度控制采购成本至关重要 符合国家电子招投标法律法规及相关规范 以及审计监督要求 通过电子化
  • java 版本企业招标投标管理系统源码+多个行业+tbms+及时准确+全程电子化

    项目说明 随着公司的快速发展 企业人员和经营规模不断壮大 公司对内部招采管理的提升提出了更高的要求 在企业里建立一个公平 公开 公正的采购环境 最大限度控制采购成本至关重要 符合国家电子招投标法律法规及相关规范 以及审计监督要求 通过电子化
  • 远程办公模式的流行:以灵活性应对当今工作环境

    随着科技的进步和全球化的发展 远程办公模式正变得越来越流行 本文将探讨远程办公模式的流行趋势 以及它如何为当今不断变化的工作环境带来灵活性和适应性 1 科技的进步 随着互联网 云计算和即时通讯工具的发展 远程办公变得更加便捷和高效 无论身在
  • 揭秘移动电源容量:虚标还是品质问题?

    在购买移动电源时 我们经常会听到关于移动电源容量的一些争议 有人认为移动电源的容量存在虚标 实际容量远远达不到标称值 有人认为移动电源的品质存在问题 转换率低下导致实际充电效果不佳 还有人说使用久了之后 电池会有损耗 导致充电效果打折 那么
  • jdk11启动jdk8 jar包报错

    一 前言 开发使用jdk8版本开发的服务打包后 使用jdk11版本的java启动服务 导致出现以下报错 java lang NoClassDefFoundError javax xml bind DatatypeConverter 二 解决
  • jenkins设置中文

    安装以下两个插件 Locale plugin Localization Chinese Simplified 在jenkins的system配置中找到locale配置项 在locale配置项的默认语言中填入以下内容保存 zh CN 重启je
  • Windows的最大威胁是尽然是“套皮安卓”的鸿蒙系统

    前言 自从鸿蒙问世以来 套皮安卓的言论就没有断过 但是那些现在还在说鸿蒙是套皮安卓的人 你知不知道微软已经组建了专门的 战略团队 来对付鸿蒙 因为这些专业的人早已经知道鸿蒙将是Windows的巨大威胁了 微软作为一个垄断操作系统多年的巨无霸
  • 短视频时代:如何设计吸引人的黄金3秒开头

    在短视频时代 一个好的开头对于视频的点击率和观看率至关重要 黄金3秒 作为短视频开头最关键的时刻 决定了观众是否愿意继续观看你的视频 那么 如何设计一个吸引人的黄金3秒开头呢 下面将为你揭秘抓住观众注意力的秘诀 一 利用好奇心 好奇心是人类
  • 从大厂到高校,鸿蒙人才“红透半边天”

    前言 近两个月来 纯血鸿蒙未发先火 连带让鸿蒙人才的培养和争夺 也红透半边天 最近 华为人才在线官网公示了2023年教育部产学合作协同育人项目华为第二批项目立项 其中 哈尔滨工业大学 天津大学 电子科技大学等16所高校鸿蒙项目立项已通过 这
  • 深入探讨Android启动优化策略

    深入探讨Android启动优化策略 在当今激烈竞争的移动应用市场 应用的启动速度直接影响着用户的第一印象和满意度 作为主流的移动操作系统之一 Android的启动优化是开发者必须关注的关键领域 本文将详细介绍一些强大有效的Android启动
  • Java 17 & Java 11:新功能探索与改进措施知多少?

    7是Java编程语言的最新 LTS 长期支持 版本 于 2021年9月14日发布 如果您目前使用的是Java11 那么也许是时候考虑迁移到 Java 17啦 方便我们体验新功能以及了解新版本的改善措施 在本文中 我们将讨论 Java 17
  • 鸿蒙开发入门:应用配置文件概述(FA模型)

    应用配置文件概述 FA模型 每个应用项目必须在项目的代码目录下加入配置文件 这些配置文件会向HarmonyOS的编译工具 HarmonyOS操作系统和应用市场提供描述应用的基本信息 应用配置文件需申明以下内容 应用的软件包名称 应用的开发厂
  • 从面试官角度看Handler:掌握技巧,事半功倍!

    引言 在Android开发领域 Handler是一项关键技能 尤其在面试中 对Handler的深刻理解和熟练运用往往是衡量一位Android开发者水平的重要标志 本文将从面试官的角度出发 针对Android Handler技术展开详细的解析
  • 鸿蒙程序员突然走俏招聘市场,大厂为什么要争相鸿蒙高手?

    前言 近期 一股奇特的暖流席卷了国内的IT就业市场 如果你浏览一下各大招聘网站 你会发现一个令人惊讶的现象 鸿蒙程序员的招聘需求激增 众多大厂纷纷抛出橄榄枝 竞相争夺这些稀缺的人才 一时间 鸿蒙程序员成为了招聘市场的香饽饽 各大公司似乎都在
  • 鸿蒙开发一员难求,你知道现在鸿蒙工程师这个岗位有多火热吗?

    前言 你知道现在鸿蒙工程师这个岗位有多火热吗 只要有一年开发经验 我所在的公司都能开到35K的月薪 这本来是至少5年以上开发经验 还得有成功项目经验的 主程 才有的待遇 关键是这样了 还找不上人 本来有一个都答应入职了 第二天就要办手续 人
  • 机器学习的12个基础问题

    1 阐述批归一化的意义 算法 1 批归一化变换 在一个 mini batch 上应用于激活 x 批归一化是一种用于训练神经网络模型的有效方法 这种方法的目标是对特征进行归一化处理 使每层网络的输出都经过激活 得到标准差为 1 的零均值状态
  • 如何应对Android面试官->CAS基本原理

    基本原理 CAS基本原理 Compare And Swap 利用了现代处理器都支持 CAS 指令 循环这个指令 直到成功为止 什么是原子操作 如何实现原子操作 原子操作 要么全部完成 要么全部都不完成的操作 例如 synchronized
  • 人为制造redis的热key、大key引发的线上事故

    背景 Redis中间件 我们主要是用来做缓存 缓解数据库的访问压力 我们搭建的是redis集群 在一个风和日丽的下午 突然收到运维的报警信息 运维 小李 你们使用的redis中间件所在的服务器 有大量的流量流出 宽带快要占满了 网卡都冒烟了
  • 【性能优化】MySql查询性能优化必知必会

    本文内容主要包括以下几个方面 分析查询SQL MySQL查询优化器 数据库存储结构 索引 索引维护 索引设计 SQL优化 表结构设计 分库分表 查询功能架构设计 分析查询SQL MySQL提供了一个性能分析工具 EXPLAIN 它可以帮助我
  • 【面向过程】springboot接受到一个请求后做了什么

    从启动开始 先从springboot怎么启动开始 启动完成之后建立web容器才能在容器中处理http请求 什么是 springboot 在spring的官网上 对springboot这样描述到 Spring Boot 可以轻松创建独立的 生