源码阅读-springboot如何启动内嵌tomcat

本文详细解析了Spring Boot的启动过程,从SpringApplication的实例化、run方法的执行,到ApplicationContext的创建与刷新,以及Web服务器的初始化。深入理解这些步骤有助于更好地掌握Spring Boot的运行机制。

1.实例化SpringApplication

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
         //资源加载器赋值
          this.resourceLoader = resourceLoader;
          //判断主类是否为空
          Assert.notNull(primarySources, "PrimarySources must not be null");
          this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
          //根据classPath下面的类,判断是servlet还是响应式web
          this.webApplicationType = WebApplicationType.deduceFromClasspath();
          //通过SpringFactoryLoader
          setInitializers((Collection) getSpringFactoriesInstances(
                ApplicationContextInitializer.class));
          //应用监听器实现类的发现,实例化以及设置
          setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
          this.mainApplicationClass = deduceMainApplicationClass();
       }

2.运行run方法,创建应用程序上下文

public ConfigurableApplicationContext run(String... args) {
          //StopWatch主要是用来统计每项任务执行时长,例如Spring Boot启动占用总时长。
          StopWatch stopWatch = new StopWatch();
          stopWatch.start();
          ConfigurableApplicationContext context = null;
          Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
          configureHeadlessProperty();
          //从META-INF/spring.factories下面查找SpringApplicationRunListener.class的实现类,并进行实例化
          SpringApplicationRunListeners listeners = getRunListeners(args);
          //启动SpringApplicationRunListeners,调用了EventPublishingRunListener类的starting()方法
          listeners.starting();
          try {

             ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                   args);
             //构造容器环境,加载系统变量,环境变量,配置文件
             ConfigurableEnvironment environment = prepareEnvironment(listeners,
                   applicationArguments);
             //设置需要忽略的bean
             configureIgnoreBeanInfo(environment);
             //打印banner
             Banner printedBanner = printBanner(environment);
             //创建容器,根据webApplicationType确定容器类型
             context = createApplicationContext();
             //实例化SpringBootExceptionReporter.class,用来支持报告关于启动的错误
             exceptionReporters = getSpringFactoriesInstances(
                   SpringBootExceptionReporter.class,
                   new Class[] { ConfigurableApplicationContext.class }, context);
             //准备容器 这一步主要是在容器刷新之前的准备动作。包含一个非常关键的操作:将启动类注入容器,为后续开启自动化配置奠定基础。
             prepareContext(context, environment, listeners, applicationArguments,
                   printedBanner);
              //刷新容器 springBoot相关的处理工作已经结束,接下的工作就交给了spring。 内部会调用spring的refresh方法,
              //refresh方法在spring整个源码体系中举足轻重,是实现 ioc 和 aop的关键,并且注册shutdown钩子函数
             refreshContext(context);

             afterRefresh(context, applicationArguments);
             stopWatch.stop();
             if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                      .logStarted(getApplicationLog(), stopWatch);
             }
             ////发布应用已经启动的事件
             listeners.started(context);
             /*
              * 遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。
              * 我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。
              */
             callRunners(context, applicationArguments);
          }
          catch (Throwable ex) {
             handleRunFailure(context, ex, exceptionReporters, listeners);
             throw new IllegalStateException(ex);
          }

          try {
             listeners.running(context);
          }
          catch (Throwable ex) {
             handleRunFailure(context, ex, exceptionReporters, null);
             throw new IllegalStateException(ex);
          }
          return context;
       }

3.在refreshContext(context);中该方法最终会调用到AbstractApplicationContext类的refresh()方法
   

@Override
       public void refresh() throws BeansException, IllegalStateException {
          synchronized (this.startupShutdownMonitor) {
             // Prepare this context for refreshing.
             prepareRefresh();

             // Tell the subclass to refresh the internal bean factory.
             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

             // Prepare the bean factory for use in this context.
             prepareBeanFactory(beanFactory);

             try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                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();
             }
          }
       }

4.onRefresh()会调用到ServletWebServerApplicationContext中的onRefresh()

 @Override
       protected void onRefresh() {
          super.onRefresh();
          try {
             createWebServer();
          }
          catch (Throwable ex) {
             throw new ApplicationContextException("Unable to start web server", ex);
          }
       }


     //omcat,Jetty都实现了这个getWebServer方法,
     //我们看TomcatServletWebServerFactory中的getWebServer(ServletContextInitializer… initializers).
     private void createWebServer() {
          WebServer webServer = this.webServer;
          ServletContext servletContext = getServletContext();
          if (webServer == null && servletContext == null) {
             ServletWebServerFactory factory = getWebServerFactory();
             this.webServer = factory.getWebServer(getSelfInitializer());
          }
          else if (servletContext != null) {
             try {
                getSelfInitializer().onStartup(servletContext);
             }
             catch (ServletException ex) {
                throw new ApplicationContextException("Cannot initialize servlet context",
                      ex);
             }
          }
          initPropertySources();
       }

      ServletWebServerFactory有三个实现:
                 Jetty
                 Tomcat
                 Undertow
     @Override
       public WebServer getWebServer(ServletContextInitializer... initializers) {
          Tomcat tomcat = new 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);
          tomcat.setConnector(connector);
          tomcat.getHost().setAutoDeploy(false);
          configureEngine(tomcat.getEngine());
          for (Connector additionalConnector : this.additionalTomcatConnectors) {
             tomcat.getService().addConnector(additionalConnector);
          }
          prepareContext(tomcat.getHost(), initializers);
          return getTomcatWebServer(tomcat);
       }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值