从启动开始
先从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; @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
作为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. */ 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一致,只是在简化了很多配置性的操作,让应用可以做到开箱机箱,但是处理请求一样是从接受到请求开始根据路径指向到对应的服务去处理并返回