1. 程式人生 > >SpringBoot嵌入式servlet容器啟動原理

SpringBoot嵌入式servlet容器啟動原理

轉載自caychen的部落格SpringBoot嵌入式servlet容器啟動原理
從原始碼的角度分析SpringBoot內嵌的servlet容器是怎麼啟動的

  1. Spring Boot應用啟動執行run方法:
public ConfigurableApplicationContext run(String... args) {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    ConfigurableApplicationContext context = null;
    FailureAnalyzers analyzers = null;
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting();
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
            args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                                                                 applicationArguments);
        Banner printedBanner = printBanner(environment);
        
        //建立一個ApplicationContext容器
        context = createApplicationContext();
        analyzers = new FailureAnalyzers(context);
        prepareContext(context, environment, listeners, applicationArguments,
                       printedBanner);
        //重新整理IOC容器
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        listeners.finished(context, null);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                .logStarted(getApplicationLog(), stopWatch);
        }
        return context;
    }
    catch (Throwable ex) {
        handleRunFailure(context, listeners, analyzers, ex);
        throw new IllegalStateException(ex);
    }
}
  1. createApplicationContext();建立IOC容器,如果是web應用,則建立AnnotationConfigEmbeddedWebApplicationContext的IOC容器;如果不是,則建立AnnotationConfigApplicationContext的IOC容器:
public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
			+ "annotation.AnnotationConfigApplicationContext";
 
public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
			+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
 
//SpringApplication#createApplicationContext
protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            //根據應用環境,建立不同的IOC容器
            contextClass = Class.forName(this.webEnvironment
                                         ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
        }
        catch (ClassNotFoundException ex) {
            throw new IllegalStateException(
                "Unable create a default ApplicationContext, "
                + "please specify an ApplicationContextClass",
                ex);
        }
    }
    return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
  1. refreshContext(context);Spring Boot重新整理IOC容器【建立IOC容器物件,並初始化容器,建立容器中每一個元件】:
//SpringApplication#refreshContext
private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
   
    //Other code...
}
  1. refresh(context);重新整理剛才建立的IOC容器:
//SpringApplication#refresh
protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext) applicationContext).refresh();
}
  1. 呼叫抽象父類的refresh()方法:
//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) {
           //...
        }
 
        finally {
            //...
        }
    }
}
  1. 抽象父類AbstractApplicationContext類的子類EmbeddedWebApplicationContext的onRefresh方法:
//EmbeddedWebApplicationContext#onRefresh
@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        createEmbeddedServletContainer();
    }
    catch (Throwable ex) {
        throw new ApplicationContextException("Unable to start embedded container",
                                              ex);
    }
}
  1. 在createEmbeddedServletContainer方法中會獲取嵌入式的Servlet容器工廠,並通過工廠來獲取Servlet容器:
//EmbeddedWebApplicationContext#createEmbeddedServletContainer
private void createEmbeddedServletContainer() {
    EmbeddedServletContainer localContainer = this.embeddedServletContainer;
    ServletContext localServletContext = getServletContext();
    if (localContainer == null && localServletContext == null) {
        //獲取嵌入式Servlet容器工廠
        EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();
        //根據容器工廠來獲取對應的嵌入式Servlet容器
        this.embeddedServletContainer = containerFactory
            .getEmbeddedServletContainer(getSelfInitializer());
    }
    else if (localServletContext != null) {
        try {
            getSelfInitializer().onStartup(localServletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context",
                                                  ex);
        }
    }
    initPropertySources();
}
  1. 從IOC容器中獲取嵌入式Servlet容器工廠:
//EmbeddedWebApplicationContext#getEmbeddedServletContainerFactory
protected EmbeddedServletContainerFactory getEmbeddedServletContainerFactory() {
    // Use bean names so that we don't consider the hierarchy
    String[] beanNames = getBeanFactory()
        .getBeanNamesForType(EmbeddedServletContainerFactory.class);
    if (beanNames.length == 0) {
        throw new ApplicationContextException(
            "Unable to start EmbeddedWebApplicationContext due to missing "
            + "EmbeddedServletContainerFactory bean.");
    }
    if (beanNames.length > 1) {
        throw new ApplicationContextException(
            "Unable to start EmbeddedWebApplicationContext due to multiple "
            + "EmbeddedServletContainerFactory beans : "
            + StringUtils.arrayToCommaDelimitedString(beanNames));
    }
    return getBeanFactory().getBean(beanNames[0],
                                    EmbeddedServletContainerFactory.class);
}

在上文中解釋到,如果加入了嵌入式的Servlet容器依賴,Spring Boot就會自動配置一個嵌入式Servlet容器工廠。接著就使用後置處理器,來獲取所有的定製器來定製配置,所以上述原始碼中會從IOC容器中獲取EmbeddedServletContainerFactory型別的容器工廠,並返回。

  1. 使用Servlet容器工廠獲取嵌入式的Servlet容器,具體使用哪個容器工廠需要看配置環境依賴:
this.embeddedServletContainer = containerFactory
            .getEmbeddedServletContainer(getSelfInitializer());

獲取嵌入式的Servlet容器後,會自動啟動Servlet容器。

  1. 上述過程是首先啟動IOC容器,接著啟動嵌入式的Servlet容器,再接著將IOC容器中剩下沒有建立的物件獲取出來,比如自己建立的controller等等:
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

看看finishBeanFactoryInitialization方法:

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    //other code...
 
    // Instantiate all remaining (non-lazy-init) singletons.
    beanFactory.preInstantiateSingletons();
}

大概看看preInstantiateSingletons方法:

@Override
public void preInstantiateSingletons() throws BeansException {
   
    List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
 
    // Trigger initialization of all non-lazy singleton beans...
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            if (isFactoryBean(beanName)) {
                final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
                boolean isEagerInit;
                if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                    isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                        @Override
                        public Boolean run() {
                            return ((SmartFactoryBean<?>) factory).isEagerInit();
                        }
                    }, getAccessControlContext());
                }
                else {
                    isEagerInit = (factory instanceof SmartFactoryBean &&
                                   ((SmartFactoryBean<?>) factory).isEagerInit());
                }
                if (isEagerInit) {
                    getBean(beanName);
                }
            }
            else {
                //註冊bean
                getBean(beanName);
            }
        }
    }
 
    // Trigger post-initialization callback for all applicable beans...
    for (String beanName : beanNames) {
        Object singletonInstance = getSingleton(beanName);
        if (singletonInstance instanceof SmartInitializingSingleton) {
            final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    @Override
                    public Object run() {
                        smartSingleton.afterSingletonsInstantiated();
                        return null;
                    }
                }, getAccessControlContext());
            }
            else {
                smartSingleton.afterSingletonsInstantiated();
            }
        }
    }
}