SpringBoot學習之路---簡單記錄嵌入式Servlet容器的啓動原理

在我的上一篇博客中簡單記錄了SpringBoot嵌入式Servlet容器是如何實現自動配置的,並且如何讀取我們自己編寫的Customizer,這一篇博客來介紹一下這個嵌入式Servlet容器是如何啓動的,並且聯動之前的博客,來說明一下整個SpringBoot項目是如何跑起來的。


這一篇博客與上一篇嵌入式自動配置的博客關係比較大,合起來可以大致明白整個SpringBoot項目的嵌入式Servlet容器的啓動原理和自動配置生效原理,大家如果感興趣的話可以翻看一下我之前的博客哦。接下來進入正題。

Springboot項目要啓動,那就要運行主程序類中的run()方法,我們點進去看一下,裏面有幾個方法值得關注:

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);
    }

createApplicationContext:觀看它的源碼,可以看到裏面有個switch語句,裏面根據當前應用是什麼類型,而來創建什麼樣的IOC容器,如果是web應用,則創建AnnotationConfigServletWebServerApplicationContext類型的IOC容器,反之則創建AnnotationConfigApplicationContext類型的IOC容器

在run方法內還有一個方法:refreshContext(context)

private void refreshContext(ConfigurableApplicationContext context) {
        this.refresh(context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            } catch (AccessControlException var3) {
            }
        }

    }

該方法的作用是創建IOC容器對象,並初始化容器,創建容器中的每一個組件

再點進去,發現refresh方法,它是一個接口

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();
      }
   }
}

裏面有一個onfresh()方法,那麼就要去找它的實現類了,這個實現類其實就是我們剛剛創建的web應用的IOC容器,它重寫了onfresh()方法。這裏其實就是個面向接口編程的思想,如果當前不是web應用,那麼它就執行默認的ioc容器內的onfresh方法

而webioc容器內部會創建嵌入式的Servlet容器createEmbeddedServletContainer();

又在這個方法的內部有一行:

EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();

看方法名,可以得出,獲取嵌入式的Servlet容器工廠。它是直接從容器中獲取對象,之後的步驟就和之前的博客連接起來了,這裏以tomcat爲例,SpringBoot會根據引入的依賴從而選擇Tomcat的嵌入式工廠,並把它放到容器中。上文提到的後置處理器一看是這個對象,就獲取所有的定製器來先定製Servlet容器的相關配置;

之後會執行到這條語句

this.embeddedServletContainer = containerFactory      .getEmbeddedServletContainer(getSelfInitializer());

調用工廠中的方法,從而獲取到嵌入式Servlet容器。

最終嵌入式的Servlet容器創建對象並啓動Servlet容器。

之後refresh方法會繼續執行,再將IOC容器中的其他組件創建出來。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章