四时宝库

程序员的知识宝库

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


从启动开始

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

什么是 springboot

在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; @SpringBootApplicationpublicclassDemoApplication {publicstaticvoidmain(String[] args) { SpringApplication.run(DemoApplication.class, args); }}

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

java

复制代码

publicstatic ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(newClass[]{primarySource}, args);}

最后执行的是这个方法

java

复制代码

public ConfigurableApplicationContext run(String... args) {StopWatchstopWatch=newStopWatch(); stopWatch.start();ConfigurableApplicationContextcontext=null; Collection<SpringBootExceptionReporter> exceptionReporters = newArrayList();this.configureHeadlessProperty();SpringApplicationRunListenerslisteners=this.getRunListeners(args); listeners.starting(); Collection exceptionReporters;try {ApplicationArgumentsapplicationArguments=newDefaultApplicationArguments(args);ConfigurableEnvironmentenvironment=this.prepareEnvironment(listeners, applicationArguments);this.configureIgnoreBeanInfo(environment);BannerprintedBanner=this.printBanner(environment); context = this.createApplicationContext(); exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, newClass[]{ConfigurableApplicationContext.class}, context);this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);this.refreshContext(context);this.afterRefresh(context, applicationArguments); stopWatch.stop();if (this.logStartupInfo) { (newStartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch); } listeners.started(context);this.callRunners(context, applicationArguments); } catch (Throwable var10) {this.handleRunFailure(context, var10, exceptionReporters, listeners);thrownewIllegalStateException(var10); }try { listeners.running(context);return context; } catch (Throwable var9) {this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);thrownewIllegalStateException(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) {thrownewIllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3); } }return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);}

选择什么类型的web容器

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

java

复制代码

publicSpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.sources = newLinkedHashSet();this.bannerMode = Mode.CONSOLE;this.logStartupInfo = true;this.addCommandLineProperties = true;this.addConversionService = true;this.headless = true;this.registerShutdownHook = true;this.additionalProfiles = newHashSet();this.isCustomEnvironment = false;this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = newLinkedHashSet(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

复制代码

privatestaticfinal String[] SERVLET_INDICATOR_CLASSES = newString[]{"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;intvar1= var0.length;for(intvar2=0; var2 < var1; ++var2) {StringclassName= 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

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

处理请求的方法

来分析下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. */protectedfinalvoidprocessRequest(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {longstartTime= System.currentTimeMillis();ThrowablefailureCause=null;//获取当前线程的上下文(threadLocal实现)LocaleContextpreviousLocaleContext= LocaleContextHolder.getLocaleContext();//构建当前请求的 localeContext 用于本地话语言实现LocaleContextlocaleContext= buildLocaleContext(request);//获取当前线程的 RequestAttributesRequestAttributespreviousAttributes= RequestContextHolder.getRequestAttributes();//构建 ServletRequestAttributes ,ServletRequestAttributes有四个属性[request,response,sessionMutex,sessionId]ServletRequestAttributesrequestAttributes= buildRequestAttributes(request, response, previousAttributes);//获取当前请求的 WebAsyncManager[管理异步请求处理的中心类,异步场景从线程(T1)中的请求处理开始。并发请求处理可以通过调用startCallableProcessing或startDeferredResultProcessing来启动,两者都在单独的线程(T2)中产生结果。保存结果并将请求分派给容器,以便在第三个线程(T3)中继续使用保存的结果进行处理。在分派的线程(T3)中,可以通过getConcurrentResult()访问保存的结果,也可以通过hasConcurrentResult()检测它的存在。]WebAsyncManagerasyncManager= WebAsyncUtils.getAsyncManager(request);//把当前请求注册给异步请求处理器 asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), newRequestBindingInterceptor());//初始化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;thrownewNestedServletException("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. */@OverrideprotectedvoiddoService(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 = newHashMap<>(); Enumeration<?> attrNames = request.getAttributeNames();while (attrNames.hasMoreElements()) {StringattrName= (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) {FlashMapinputFlashMap=this.flashMapManager.retrieveAndUpdate(request, response);if (inputFlashMap != null) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, newFlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); }//以上代码设置请求的attributetry { 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 */protectedvoiddoDispatch(HttpServletRequest request, HttpServletResponse response)throws Exception {HttpServletRequestprocessedRequest= request;HandlerExecutionChainmappedHandler=null;booleanmultipartRequestParsed=false;WebAsyncManagerasyncManager= WebAsyncUtils.getAsyncManager(request);try {ModelAndViewmv=null;ExceptiondispatchException=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 handlerHandlerAdapterha= getHandlerAdapter(mappedHandler.getHandler());// 获取请求方法Stringmethod= request.getMethod();//处理last-modifiedbooleanisGet="GET".equals(method);if (isGet || "HEAD".equals(method)) {longlastModified= ha.getLastModified(request, mappedHandler.getHandler());if (newServletWebRequest(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 = newNestedServletException("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,newNestedServletException("Handler processing failed", err)); }finally {if (asyncManager.isConcurrentHandlingStarted()) {// Instead of postHandle and afterCompletionif (mappedHandler != null) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } }else {// Clean up any resources used by a multipart request.if (multipartRequestParsed) {//清楚分包请求的所有占用的资源 cleanupMultipart(processedRequest); } } }}

总结

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

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接