springboot專題二:springboot的啟動流程


一、從熟知的啟動類開始

下面這個啟動類是我們熟悉的springboot的啟動類:代碼是自己寫的一個簡單的springboot的demo: https://gitee.com/leijisong/springcloud-demo

@SpringBootApplication(scanBasePackages = {"com.mall"})
@EntityScan(basePackages = {"com.mall.domain.dataobject"})
@EnableJpaRepositories(basePackages = "com.mall.domain")
@EnableDiscoveryClient
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
@EnableRabbit
public class MallApplication {

    public static void main(String[] args) {
        SpringApplication.run(MallApplication.class, args);
    }

}

1. 調用SpringApplication.run(MallApplication.class, args); 啟動了我們的SpringBoot應用:

2. 查看run方法:使用自定義SpringApplication進行啟動

public static ConfigurableApplicationContext run(Class<?>[] primarySources,
            String[] args) {
        return new SpringApplication(primarySources).run(args);
}

2.1 創建SpringApplication  -->  new SpringApplication(primarySources)

源碼如下:

    /**
     * Create a new {@link SpringApplication} instance. The application context will load
     * beans from the specified primary sources (see {@link SpringApplication class-level}
     * documentation for details. The instance can be customized before calling
     * {@link #run(String...)}.
     * @param resourceLoader the resource loader to use
     * @param primarySources the primary bean sources
     * @see #run(Class, String[])
     * @see #setSources(Set)
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
     // 將啟動類放入到primarySources
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 根據classpath下的類,感知當前web應用類型,如WebApplicationType.SERVLET,WebApplicationType.REACTIVE
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 從spring.factories中獲取所有的key為:org.springframework.context.ApplicationContextInitializer   setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.
class));
     // 從spring.factories中獲取所有的key為:org.springframework.context.ApplicationListener setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.
class));
// 根據main方法得到mainApplicationClass
this.mainApplicationClass = deduceMainApplicationClass(); }

我們看下spring.factories這個文件(這個文件通過第一個專題應該很熟悉了)。看看ApplicationContextInitializer和ApplicationListener都定義了哪些?

實際上,遠不止這些,通過擴展的還有:

ApplicationContextInitializer:

ApplicationListener:

 

 總結:這里主要是做了一些初始化動作

  • 獲取啟動類加載ioc容器
  • 獲取web應用類型
  • 通過spring.factories讀取對外擴展的ApplicationContextInitializer ,ApplicationListener
  • 根據main推算出所在類

2.2 啟動

 啟動的核心邏輯源碼如下:

    /**
     * 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主要用來記錄Springboot的啟動耗時 StopWatch stopWatch
= new StopWatch();
// 啟動的開始時間 stopWatch.start();
// Spring上下文接口,繼承ApplicationContext ConfigurableApplicationContext context
= null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 開啟了headless模式 configureHeadlessProperty();
// 從spring.factories讀取SpringApplicationRunListener組件,發布事件/運行監聽 SpringApplicationRunListeners listeners
= getRunListeners(args);
// 發布事件:ApplicationStartingEvent listeners.starting();
try {
// 根據命令行參數,實例化ApplicationArguments ApplicationArguments applicationArguments
= new DefaultApplicationArguments( args);
// 基於監聽器與初始化環境:包括環境變量和配置文件的信息 ConfigurableEnvironment environment
= prepareEnvironment(listeners, applicationArguments);
// 忽略beaninfo的Bean configureIgnoreBeanInfo(environment);
// 打印Bananer橫幅 Banner printedBanner
= printBanner(environment);
// 根據webApplicationType創建上下文 context
= createApplicationContext(); exceptionReporters = getSpringFactoriesInstances( SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context);
       // 初始化Spring上下文 prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 關鍵:加載IOC容器;加載自動配置類 refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop();
if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass) .logStarted(getApplicationLog(), stopWatch); } listeners.started(context); 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; }
 2.2.1 prepareEnvironment(listeners,applicationArguments);

這個主要做的是基於監聽器的環境初始化

源碼:

private ConfigurableEnvironment prepareEnvironment(
            SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // 根據webApplicationType創建環境,創建時就會讀取java環境變量和系統變量
        ConfigurableEnvironment environment = getOrCreateEnvironment();
// 將命令行參數讀取環境變量中 configureEnvironment(environment, applicationArguments.getSourceArgs()); listeners.environmentPrepared(environment);
     // 將spring.main開頭的信息都綁定SpringApplication bindToSpringApplication(environment);
if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()) .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); }
// 更新PropertySource ConfigurationPropertySources.attach(environment);
return environment; }
private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
            return this.environment;
        }
        switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
        }
    }
protected void configureEnvironment(ConfigurableEnvironment environment,
            String[] args) {
        if (this.addConversionService) {
            ConversionService conversionService = ApplicationConversionService
                    .getSharedInstance();
            environment.setConversionService(
                    (ConfigurableConversionService) conversionService);
        }
        configurePropertySources(environment, args);
        configureProfiles(environment, args);
    }
protected void bindToSpringApplication(ConfigurableEnvironment environment) {
        try {
            Binder.get(environment).bind("spring.main", Bindable.ofInstance(this));
        }
        catch (Exception ex) {
            throw new IllegalStateException("Cannot bind to SpringApplication", ex);
        }
    }
2.2.2 prepareContext

源碼:

private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
// 上下文后置處理 postProcessApplicationContext(context);
// 將之前拿到的所有ApplicationContextInitializer,遍歷執行initialize方法 applyInitializers(context);
// 發布ApplicationContextInitializedEvent listeners.contextPrepared(context);
if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // 獲取Spring上下文的Bean工廠 ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); }
// 如果出現兩個同名bean。直接拋異常
if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // 加載資源 Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty");
// 讀取主啟動類,注冊成Bean load(context, sources.toArray(
new Object[0]));
// 讀取完配置類之后,發送ApplicationPreparedEvent listeners.contextLoaded(context); }
    /**
     * Apply any {@link ApplicationContextInitializer}s to the context before it is
     * refreshed.
     * @param context the configured ApplicationContext (not refreshed yet)
     * @see ConfigurableApplicationContext#refresh()
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    protected void applyInitializers(ConfigurableApplicationContext context) {
        for (ApplicationContextInitializer initializer : getInitializers()) {
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(
                    initializer.getClass(), ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            initializer.initialize(context);
        }
    }
public void contextPrepared(ConfigurableApplicationContext context) {
        for (SpringApplicationRunListener listener : this.listeners) {
            listener.contextPrepared(context);
        }
    }
    /**
     * Return an immutable set of all the sources that will be added to an
     * ApplicationContext when {@link #run(String...)} is called. This method combines any
     * primary sources specified in the constructor with any additional ones that have
     * been {@link #setSources(Set) explicitly set}.
     * @return an immutable set of all sources
     */
    public Set<Object> getAllSources() {
        Set<Object> allSources = new LinkedHashSet<>();
        if (!CollectionUtils.isEmpty(this.primarySources)) {
            allSources.addAll(this.primarySources);
        }
        if (!CollectionUtils.isEmpty(this.sources)) {
            allSources.addAll(this.sources);
        }
        return Collections.unmodifiableSet(allSources);
    }
public void started(ConfigurableApplicationContext context) {
     for (SpringApplicationRunListener listener : this.listeners) {
         listener.started(context);
     }
}

@Override
public void started(ConfigurableApplicationContext context) {
context.publishEvent(
new ApplicationStartedEvent(this.application, this.args, context));
}
 

2.2.3 refreshContext

源碼:

private void refreshContext(ConfigurableApplicationContext context) {
        refresh(context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            }
            catch (AccessControlException ex) {
                // Not allowed in some environments.
            }
        }
    }
    @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.            
            // 從spring容器獲取BeanFactory並進行相關設置為后續使用做准備
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.                
                // 繼上一步beanfactory設置之后進行后續操作,不同spring容器進行不同操作
                postProcessBeanFactory(beanFactory);

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

                // Register bean processors that intercept bean creation.                
                // 使用了PostProcessorRegistrationDelegate類的registerBeanPostProcessors方法執行          
                // 1)先找出實現了PriorityOrdered接口的BeanPostProcessor並排序后加到BeanFactory的BeanPostProcessor集合中                          
                // 2)找出實現了Ordered接口的BeanPostProcessor並排序后加到BeanFactory的BeanPostProcessor集合中                          
                // 3)沒有實現PriorityOrdered和Ordered接口的BeanPostProcessor加到BeanFactory的BeanPostProcessor集合中
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.                
                // 初始化國際化屬性
                initMessageSource();

                // Initialize event multicaster for this context.                
                // 初始化事件廣播器,用於發布事件。 EventPublishingRunlistener會監聽事件,在run函數之前contextPrepared時候已經注入了。                 
                // 這個時候不需要注冊,只要拿到BeanFactory的廣播器直接設置到spring容器,如果沒有再自己初始化
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.                
                // 不同容器各自實現,比如ConfigEmbeddedWebApplicationContext中會調用
          // createEmbeddedServletContainer方法去創建內置的Servlet容器, // 目前支持 tomcat,jetty,undertow。
onRefresh(); // Check for listener beans and register them. // 把spring容器內的listener和beanfactory的listener都添加到廣播器中 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. // 實例化BeanFactory 中已經被注冊但是沒被實例化的所有實例,懶加載除外。 // 比如invokeBeanFactoryPostProcessors方法中根據各種注解解析出來的類,都會初始化。
// 初始化的過程中各種BeanPostProcessor開始起作用
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. // 初始化生命周期處理器LifecycleProcessor並調用其onrefresh方法,           // 找到SmartLifecycle接口的所有實現類並調用start方法,發布事件告知listener, // 如果設置了JMX相關屬性,還會調用LiveBeansView的registerApplicationContext方法 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(); } } }

 

總結:

  • 初始化SpringApplication 從spring.factories 讀取 listener ApplicationContextInitializer 。
  • 運行run方法
  • 讀取 環境變量 配置信息
  • 創建springApplication上下文:ServletWebServerApplicationContext
  • 預初始化上下文 : 讀取啟動類
  • 調用refresh 加載ioc容器
  • 加載所有的自動配置類
  • 創建servlet容器
  • 在這個過程中springboot會調用很多監聽器對外進行擴展


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM