1、背景

  SpringBoot是一个框架,一种全新的编程标准,他的产生简化了框架的应用,同时也提供了很多便捷的性能,比方内置tomcat就是其中一项,他让咱们省去了搭建tomcat容器,生成war,部署,启动tomcat。因为内置了启动容器,应用程序能够间接通过Maven命令将我的项目编译成可执行的jar包,通过java-jar命令间接启动,不须要再像以前一样,打包成War包,而后部署在Tomcat中,那么内置tomcat是如何实现的呢?

  2、tomcat启动过程及原理

  2.1、下载一个springboot我的项目

  在这里下载一个我的项目https://start.spring.io/也能够在idea新建SpringBoot-Web工程。

  <dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-web</artifactId>    </dependency>

  点击pom.xml会有tomcat依赖

  <dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-tomcat</artifactId>    <version>2.1.2.RELEASE</version>    <scope>compile</scope>    </dependency>

  2.2、从启动入口开始一步步摸索

  点击进入run办法

  public static ConfigurableApplicationContext run(Class<?> primarySource,    String... args) {    return run(new Class<?>[] { primarySource }, args);    }    //持续点击进入run办法    public static ConfigurableApplicationContext run(Class<?>[] primarySources,    String[] args) {    return new SpringApplication(primarySources).run(args);    }

  进入到这个run办法之后就能够看到,咱们意识的一些初始化事件。次要的过程也是在这里实现的。

  2.3、源码代码流程大抵是这样

  /**    * Run the Spring application, creating and refreshing a new    * {@link ApplicationContext}.    * @param args the application arguments (usually passed from a Java main method)    * @return a running {@link ApplicationContext}    */    public ConfigurableApplicationContext run(String... args) {    StopWatch stopWatch = new StopWatch();    stopWatch.start();    ConfigurableApplicationContext context = null;    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();    /**1、配置零碎属性*/    configureHeadlessProperty();    /**2.获取监听器*/    SpringApplicationRunListeners listeners = getRunListeners(args);    /**公布利用开始启动事件 */    listeners.starting();    try {    /** 3.初始化参数 */    ApplicationArguments applicationArguments = new DefaultApplicationArguments(    args);    /** 4.配置环境*/    ConfigurableEnvironment environment = prepareEnvironment(listeners,    applicationArguments);    configureIgnoreBeanInfo(environment);    Banner printedBanner = printBanner(environment);    /**5.创立利用上下文*/    context = createApplicationContext();    exceptionReporters = getSpringFactoriesInstances(    SpringBootExceptionReporter.class,    new Class[] { ConfigurableApplicationContext.class }, context);    /**6.预处理上下文*/    prepareContext(context, environment, listeners, applicationArguments,    printedBanner);    /**6.刷新上下文*/    refreshContext(context);    afterRefresh(context, applicationArguments);    stopWatch.stop();    if (this.logStartupInfo) {    new StartupInfoLogger(this.mainApplicationClass)    .logStarted(getApplicationLog(), stopWatch);    }    /** 8.公布利用曾经启动事件 */    listeners.started(context);    callRunners(context, applicationArguments);    }    catch (Throwable ex) {    handleRunFailure(context, ex, exceptionReporters, listeners);    throw new IllegalStateException(ex);    }    try {    /** 9.公布利用曾经启动实现的监听事件 */    listeners.running(context);    }    catch (Throwable ex) {    handleRunFailure(context, ex, exceptionReporters, null);    throw new IllegalStateException(ex);    }    return context;    }

  代码中次要就是通过switch语句,依据webApplicationType的类型来创立不同的ApplicationContext:

  ●DEFAULT_SERVLET_WEB_CONTEXT_CLASS:Web类型,实例化AnnotationConfigServletWebServerApplicationContext

  ●DEFAULT_REACTIVE_WEB_CONTEXT_CLASS:响应式Web类型,实例化AnnotationConfigReactiveWebServerApplicationContext

  ●DEFAULT_CONTEXT_CLASS:非Web类型,实例化AnnotationConfigApplicationContext

  2.4、创立完利用上下文之后,咱们在看刷新上下文办法

  一步步通过断点点击办法进去查看,咱们看到很相熟代码spring的相干代码。

  @Override    public void refresh() throws BeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) {    // Prepare this context for refreshing.    //初始化前的筹备工作,次要是一些零碎属性、环境变量的校验,比方Spring启动须要某些环境变量,能够在这个中央进行设置和校验    prepareRefresh();    // Tell the subclass to refresh the internal bean factory.    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();    // Prepare the bean factory for use in this context.    //筹备bean工厂 注册了局部类    prepareBeanFactory(beanFactory);    try {    // Allows post-processing of the bean factory in context subclasses.    postProcessBeanFactory(beanFactory);    // Invoke factory processors registered as beans in the context.    //注册bean工厂后置处理器,并解析java代码配置bean定义    invokeBeanFactoryPostProcessors(beanFactory);    // Register bean processors that intercept bean creation.    //注册bean后置处理器,并不会执行后置处理器,在前面实例化的时候执行    registerBeanPostProcessors(beanFactory);    // Initialize message source for this context.    initMessageSource();    // Initialize event multicaster for this context.    //初始化事件监听多路播送器    initApplicationEventMulticaster();    // Initialize other special beans in specific context subclasses.    //待子类实现,springBoot在这里实现创立内置的tomact容器    onRefresh();    // Check for listener beans and register them.    registerListeners();    // Instantiate all remaining (non-lazy-init) singletons.    finishBeanFactoryInitialization(beanFactory);    // Last step: publish corresponding event.    finishRefresh();    }    catch (BeansException ex) {    if (logger.isWarnEnabled()) {    logger.warn("Exception encountered during context initialization - " +    "cancelling refresh attempt: " + ex);    }    // Destroy already created singletons to avoid dangling resources.    destroyBeans();    // Reset 'active' flag.    cancelRefresh(ex);    // Propagate exception to caller.    throw ex;    }    finally {    // Reset common introspection caches in Spring's core, since we    // might not ever need metadata for singleton beans anymore...    resetCommonCaches();    }    }    }

  2.5、onRefresh()办法是调用其子类实现的

  也就是ServletWebServerApplicationContext

  /** 失去Servlet工厂 **/    this.webServer = factory.getWebServer(getSelfInitializer());

  其中createWebServer()办法是用来启动web服务的,然而还没有真正启动Tomcat,只是通过ServletWebServerFactory创立了一个WebServer,持续来看这个ServletWebServerFactory:

  this.webServer=factory.getWebServer(getSelfInitializer());这个办法能够看出TomcatServletWebServerFactory的实现。相干Tomcat的实现。

  2.6、TomcatServletWebServerFactory的getWebServer()办法

  清晰的看到new进去了一个Tomcat.

  2.7、Tomcat创立之后,持续剖析Tomcat的相干设置和参数

  @Override    public WebServer getWebServer(ServletContextInitializer... initializers) {    /** 1、创立Tomcat实例 **/    Tomcat tomcat = new Tomcat();    //创立Tomcat工作目录    File baseDir = (this.baseDirectory != null) ? this.baseDirectory    : createTempDir("tomcat");    tomcat.setBaseDir(baseDir.getAbsolutePath());    Connector connector = new Connector(this.protocol);    tomcat.getService().addConnector(connector);    customizeConnector(connector);    /** 2、给创立好的tomcat设置连接器connector **/    tomcat.setConnector(connector);    /** 3.设置不主动部署 **/    tomcat.getHost().setAutoDeploy(false);    /** 4.配置Tomcat容器引擎 **/    configureEngine(tomcat.getEngine());    for (Connector additionalConnector : this.additionalTomcatConnectors) {    tomcat.getService().addConnector(additionalConnector);    }    /**筹备Tomcat的StandardContext,并增加到Tomcat中*/    prepareContext(tomcat.getHost(), initializers);    /** 将创立好的Tomcat包装成WebServer返回**/    return getTomcatWebServer(tomcat);    }

  2.8、持续点击getTomcatWebServer办法,找到initialize()办法,能够看到tomcat.start();启动tomcat服务办法。

  // Start the server to trigger initialization listeners    //启动tomcat服务    this.tomcat.start();    //开启阻塞非守护过程    startDaemonAwaitThread();

  2.9、TomcatWebServer.java控制台会打印这句话

  Tomcat started on port(s):8080(http)with context path

  3、总结

  SpringBoot的启动次要是通过实例化SpringApplication来启动的,启动过程次要做了如下几件事件:

  配置零碎属性、获取监听器,公布利用开始启动事件、初始化参数、配置环境、创立利用上下文、预处理上下文、刷新上下文、再次刷新上下文、公布利用曾经启动事件、公布利用启动实现事件。而启动Tomcat是刷新上下文这一步。

  Spring Boot创立Tomcat时,会先创立一个上下文,将WebApplicationContext传给Tomcat;

  启动Web容器,须要调用getWebserver(),因为默认的Web环境就是TomcatServletWebServerFactory,所以会创立Tomcat的Webserver,这里会把根上下文作为参数给TomcatServletWebServerFactory的getWebServer();启动Tomcat,调用Tomcat中Host、Engine的启动办法。