欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring boot啟動原理及相關組件整理

 更新時間:2024年12月19日 14:46:47   作者:程序猿進階  
這篇文章主要介紹了Spring boot啟動原理及相關組件整理,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友一起看看吧

一、Spring Boot應用啟動

一個Spring Boot應用的啟動通常如下:

@SpringBootApplication
@Slf4j
public class ApplicationMain {
    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = SpringApplication.run(ApplicationMain.class, args);
    }
}

執(zhí)行如上代碼,Spring Boot程序啟動成功。事實上啟動Spring Boot應用離不開SpringApplication。
所以,我們跟隨SpringApplication的腳步,開始從源碼角度分析Spring Boot的初始化過程。

btw,可參看例子一節(jié),我對Spring Boot啟動的拓展點都做了demo,可參照下面源碼分析進行理解。

文檔有一句話說了SpringApplication做了什么(目的):

Create an appropriate ApplicationContext instance (depending on your classpath) Register a CommandLinePropertySource to expose command line arguments as Spring properties Refresh the application context, loading all singleton beans Trigger any CommandLineRunner beans

二、SpringApplication構造函數(shù)

啟動代碼先創(chuàng)建SpringApplication示例,在執(zhí)行run方法:

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

如下是SpringApplication的構造函數(shù)代碼分析。

this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//通過Classloader探測不同web應用核心類是否存在,進而設置web應用類型
this.webApplicationType = WebApplicationType.deduceFromClasspath(); 
//找出所有spring.factories中聲明的ApplicationContextInitializer并設置,
//ApplicationContextInitializer定義了回調接口,在refresh()前初始化調用(即在prepareContext的applyInitializers方法中調用)
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//找出所有spring.factories中聲明的ApplicationListener(細節(jié)往后再敘),ApplicationListener繼承了
//java.util.EventListener,實現(xiàn)了類似觀察者模式的形式,通過實現(xiàn)ApplicationListener、SmartApplicationListener,能夠監(jiān)聽Spring上下文的refresh、Prepared等事件或者是自定義事件
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//找出主啟動類(有趣的是,是通過new一個runtime異常然后在異常棧里面找出來的)
this.mainApplicationClass = deduceMainApplicationClass();

在構造期間,主要做了:
1、判定應用類型,為后面創(chuàng)建不同類型的spring context做準備。
2、初始化ApplicationContextInitializer和ApplicationListener。
3、找出啟動類。

三、run()源碼解析

介紹run()方法前,先說說貫穿run方法的ApplicationRunListener,它有助于理解整個run()的運行周期。
寫在這里:Spring Application事件機制

run()方法分析如下:

//java.awt.headless,是J2SE的一種模式,用于在缺失顯示屏、鼠標或者鍵盤時的系統(tǒng)配置。
configureHeadlessProperty();
//將spring.factories中的SpringApplicationRunListener接口實現(xiàn)類拖出來,塞到SpringApplicationRunListeners(一個集合)中,統(tǒng)一批量執(zhí)行
SpringApplicationRunListeners listeners = getRunListeners(args);
//觸發(fā)runlistener的starting
listeners.starting();
try {
   ApplicationArguments applicationArguments = new DefaultApplicationArguments(
         args);
   ConfigurableEnvironment environment = prepareEnvironment(listeners,
         applicationArguments);
   //spring.beaninfo.ignore如果沒有設置值,則把它設為true,具體情況具體設置,
   //如果沒用的話,把它設為true可以ignore掉classloader對于不存在的BeanInfo的掃描,提高性能。
   configureIgnoreBeanInfo(environment);
   //banner打印。自定義banner挺好玩的
   Banner printedBanner = printBanner(environment);
   //根據(jù)webApplicationType(一開始推斷的應用類型)去新建applicationContext
   context = createApplicationContext();
   //獲取SpringBootExceptionReporter,回調接口類,提供啟動時的異常報告
   exceptionReporters = getSpringFactoriesInstances(
         SpringBootExceptionReporter.class,
         new Class[] { ConfigurableApplicationContext.class }, context);
   //下面會說
   prepareContext(context, environment, listeners, applicationArguments,
         printedBanner);
   refreshContext(context);
   //do nothing
   afterRefresh(context, applicationArguments);
   //計時停止
   stopWatch.stop();
   //打日志
   if (this.logStartupInfo) {
      new StartupInfoLogger(this.mainApplicationClass)
            .logStarted(getApplicationLog(), stopWatch);
   }
   //啟動
   listeners.started(context);
   //找出context的ApplicationRunner和CommandLineRunner,用AnnotationAwareOrderComparator排序,并執(zhí)行
   callRunners(context, applicationArguments);

下面再分別說說兩個方法(prepareEnvironment、refreshContext)的代碼。

四、prepareEnvironment

private ConfigurableEnvironment prepareEnvironment(
      SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments) {
   // Create and configure the environment
   ConfigurableEnvironment environment = getOrCreateEnvironment();
   configureEnvironment(environment, applicationArguments.getSourceArgs());
   //發(fā)布environment prepared事件
   listeners.environmentPrepared(environment);
   //將獲取到的environment中的spring.main配置綁定到SpringApplication中,
   //使用的是Binder這個spring boot2.0開始有的類
   bindToSpringApplication(environment);
   if (!this.isCustomEnvironment) {
      environment = new EnvironmentConverter(getClassLoader())
            .convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
   }
   //附加的解析器將動態(tài)跟蹤底層 Environment 屬性源的任何添加或刪除,
   //關于ConfigurationPropertySourcesPropertySource和MutablePropertiySource
   //將在Environment中作進一步講解
   ConfigurationPropertySources.attach(environment);
   return environment;
}

五、prepareContext

private void prepareContext(ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
   //為上下文設置environment(配置、profile)
   context.setEnvironment(environment);
   //對application做一些處理,設置一些組件,
   //比如BeanNameGenerator,ApplicationConversionService(包含一些默認的Converter和formatter)
   postProcessApplicationContext(context);
   // 加載并運行ApplicationContextInitializer
   applyInitializers(context);
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   // Add boot specific singleton beans
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   // Load the sources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   //Load beans(其實是由sources構建beanDefinition) into the application context. 
   //構建BeanDefinitionLoader并執(zhí)行BeanDefinitionLoader.load()
   load(context, sources.toArray(new Object[0]));
   //執(zhí)行contextLoaded事件
   listeners.contextLoaded(context);
}

六、容器refresh(refreshContext)

private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

refreshContext會做兩件事,
1、應用上下文刷新
2、注冊shutdown鉤子

我們來看看ServletWebServer的刷新。

// ServletWebServerApplicationContext 
public final void refresh() throws BeansException, IllegalStateException {
   try {
      super.refresh();
   }
   catch (RuntimeException ex) {
      //停止webserver
      stopAndReleaseWebServer();
      throw ex;
   }
}
org.springframework.context.support.AbstractApplicationContext refresh()
public void refresh() throws BeansException, IllegalStateException {
   // 單線程執(zhí)行
   synchronized (this.startupShutdownMonitor) {
      // Prepare this context for refreshing.
      // 1、設置Spring容器的啟動時間,撤銷關閉狀態(tài),開啟活躍狀態(tài)。2、初始化屬性源信息(Property)3、驗證環(huán)境信息里一些必須存在的屬性
      prepareRefresh();
      // Tell the subclass to refresh the internal bean factory.
      // 如果是RefreshtableApplicationContext會做了很多事情:
      // 1、讓子類刷新內部beanFactory ,創(chuàng)建IoC容器(DefaultListableBeanFactory--ConfigurableListableBeanFactory 的實現(xiàn)類)
      // 2、加載解析XML文件(最終存儲到Document對象中)
      // 3、讀取Document對象,并完成BeanDefinition的加載和注冊工作
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      // Prepare the bean factory for use in this context.
      //從Spring容器獲取BeanFactory(Spring Bean容器)并進行相關的設置為后續(xù)的使用做準備:
      //1、設置classloader(用于加載bean),設置表達式解析器(解析bean定義中的一些表達式),添加屬性編輯注冊器(注冊屬性編輯器)
      //2、添加ApplicationContextAwareProcessor這個BeanPostProcessor。取消ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、EnvironmentAware這5個接口的自動注入。因為ApplicationContextAwareProcessor把這5個接口的實現(xiàn)工作做了
      //3、設置特殊的類型對應的bean。BeanFactory對應剛剛獲取的BeanFactory;ResourceLoader、ApplicationEventPublisher、ApplicationContext這3個接口對應的bean都設置為當前的Spring容器
      //4、注入一些其它信息的bean,比如environment、systemProperties等
      prepareBeanFactory(beanFactory);
      try {
         // Allows post-processing of the bean factory in context subclasses.
         postProcessBeanFactory(beanFactory);
         // Invoke factory processors registered as beans in the context. 執(zhí)行beanfactoryPostProcessor
         invokeBeanFactoryPostProcessors(beanFactory);
         // Register bean processors that intercept bean creation. 
         // 注冊beanPostProcessor
         registerBeanPostProcessors(beanFactory);
         // Initialize message source for this context. 初始化messageSource
         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();
      }
   }
}

七、postProcessBeanFactory()

設置BeanFactory之后再進行后續(xù)的一些BeanFactory操作。

不同的Context會進行不同的操作。 比如,AnnotationConfigServletWebServerApplicationContext

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 父類實現(xiàn),會注冊web應用特有的factory scope, 
    super.postProcessBeanFactory(beanFactory);
    //查看basePackages屬性,如果設置了會使用ClassPathBeanDefinitionScanner去掃描basePackages包下的bean并注
    if (this.basePackages != null && this.basePackages.length > 0) {
        this.scanner.scan(this.basePackages);
    }
    // 查看annotatedClasses屬性,如果設置了會使用AnnotatedBeanDefinitionReader去注冊這些bean
    if (!this.annotatedClasses.isEmpty()) {
        this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
    }
}

八、invokeBeanFactoryPostProcessors()

/**
 * Instantiate and invoke all registered BeanFactoryPostProcessor beans,
 * respecting explicit order if given.
 * <p>Must be called before singleton instantiation.
 */
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    //執(zhí)行AbstractContext持有的BeanFactory后置處理器
    //這些處理器是之前ContextInitializer
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
    // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
    // 如果通過-javaagent參數(shù)設置了LTW的織入器類包,那么增加LTW的BeanProcessor。
    if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
}

從容器中找出BeanDefinitionRegistryPostProcessor、BeanFactoryPostProcessor(二者的區(qū)別是,一個使用BeanDefinitionRegistry作處理,一個使用BeanFactory做處理), 并按一定的規(guī)則順序執(zhí)行。

ConfigurationClassPostProcessor的優(yōu)先級為最高,它會對項目中的@Configuration注解修飾的類(@Component、@ComponentScan、@Import、@ImportResource修飾的類也會被處理)進行解析,解析完成之后把這些bean注冊到BeanFactory中。 需要注意的是這個時候注冊進來的bean還沒有實例化。

ConfigurationClassPostProcessor的流程之后會獨立進行分析。

九、registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory)方法

···java
/**
Instantiate and invoke all registered BeanPostProcessor beans,respecting explicit order if given.
Must be called before any instantiation of application beans.
*/
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
//委派PostProcessorRegistrationDelegate去做
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
從Spring容器中按一定順序(PriorityOrdered、Ordered、非PriorityOrdered非Ordered)找出實現(xiàn)了BeanPostProcessor接口的bean,并設置到BeanFactory的屬性中。之后bean被實例化的時候會調用這個BeanPostProcessor。
## 十、initMessageSource()
初始化一些國際化相關的屬性。
Spring boot的國際化配置可閱讀MessageSourceAutoConfiguration。 默認情況會設置一個DelegatingMessageSource,是一個空實現(xiàn),因為ApplicationContext接口拓展了MessageSource接口,所以Spring容器都有getMessage方法, 可是,在實現(xiàn)上又允許空MessageSource,所以,通過一個DelegatingMessageSource去適配。
## 十一、initApplicationEventMulticaster()
Initialize event multicaster for this context. 初始化事件廣播器。默認實現(xiàn)是SimpleApplicationEventMulticaster。
onRefresh()
模板方法,給不同的Spring應用容器去實例化一些特殊的類。
比如,AnnotationConfigServletWebServerApplicationContext、AnnotationConfigReactiveWebServerApplicationContext會去創(chuàng)建web server(createWebServer())。 spring boot的mvc內置支持有tomcat、Undertow、jetty三種server,而reactive web server則內置支持tomcat、jetty、netty三種。
java
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();

btw,如果是tomcat server的話,spring boot會啟動多一個線程防止退出。

十二、registerListeners()

把BeanFactory的ApplicationListener拿出來塞到事件廣播器里。

如果ApplicationContext的earlyApplicationEvents屬性有值,則廣播該屬性持有的early事件。

十三、finishBeanFactoryInitialization(beanFactory)

實例化BeanFactory中已經(jīng)被注冊但是未實例化的所有實例(懶加載的不需要實例化)。

比如invokeBeanFactoryPostProcessors方法中根據(jù)各種注解解析出來的類,在這個時候都會被初始化。

十四、finishRefresh()

// ReactiveWebServerApplicationContext
    @Override
    protected void finishRefresh() {
        super.finishRefresh();
        WebServer webServer = startReactiveWebServer();
        if (webServer != null) {
            publishEvent(new ReactiveWebServerInitializedEvent(webServer, this));
        }
    }
// AbstractApplicationContext
    /**
     * Finish the refresh of this context, invoking the LifecycleProcessor's
     * onRefresh() method and publishing the
     * {@link org.springframework.context.event.ContextRefreshedEvent}.
     */
    protected void finishRefresh() {
        // Clear context-level resource caches (such as ASM metadata from scanning).
        // 容器完成刷新,清除資源緩存
        clearResourceCaches();
        // Initialize lifecycle processor for this context.
        // 初始化lifeCycleProcessor, 默認實現(xiàn)是DefaultLifeCycleProcessor,實現(xiàn)了BeanFactoryAware接口,通過BeanFactory找出LifeCycle bean
        // 可通過自定義實現(xiàn)LifeCycle接口的Bean,來監(jiān)聽容器的生命周期。
        initLifecycleProcessor();
        // Propagate refresh to lifecycle processor first.
        //粗發(fā)生命周期處理器的onRefresh方法,順帶一說,在程序正常退出時,會粗發(fā)shutdownHook,那時會粗發(fā)生命周期處理器的onClose方法
        getLifecycleProcessor().onRefresh();
        // Publish the final event.
        // 廣播ContextRefreshed事件
        publishEvent(new ContextRefreshedEvent(this));
        // Participate in LiveBeansView MBean, if active.
        // 將ApplicationContext注冊到Spring tool suite里
        LiveBeansView.registerApplicationContext(this);
    }

十五、resetCommonCaches()

// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();

最后會在finally執(zhí)行resetCommonCaches(),清除一些Spring core、beans加載和解析的Bean信息緩存(因為對于singleton bean來說已經(jīng)不需要了)。

十六、流程整理

最后,按照啟動階段整理一幅全景圖。

十七、例子

在github里,我把Spring Boot應用啟動的拓展組件(自定義的應用初始器、監(jiān)聽器、事件、ApplicationRunner)都寫了例子,可參照閱讀。 代碼在這 | spring-boot-none-startup

日志如下:

2020-05-20 18:30:11.625 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.SimpleRunListener : environmentPrepared, env:StandardEnvironment {activeProfiles=[dev], defaultProfiles=[default], propertySources=[MapPropertySource {name='systemProperties'}, OriginAwareSystemEnvironmentPropertySource {name='systemEnvironment'}, RandomValuePropertySource {name='random'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application-dev.yml]'}, OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml]'}]}

. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.1.3.RELEASE)

2020-05-20 18:30:11.832 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.SimpleRunListener : contextPrepared, ctx:org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Thu May 01 08:00:00 CST 1970
2020-05-20 18:30:11.838 INFO 81568 --- [ main] n.t.d.s.b.startup.none.ApplicationMain : Starting ApplicationMain on DESKTOP-OLDGHC1 with PID 81568 ( started by teash in )
2020-05-20 18:30:11.838 INFO 81568 --- [ main] n.t.d.s.b.startup.none.ApplicationMain : The following profiles are active: dev
2020-05-20 18:30:11.894 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.SimpleRunListener : contextLoaded, context: org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Thu May 01 08:00:00 CST 1970
2020-05-20 18:30:12.404 INFO 81568 --- [ main] .s.b.s.n.s.SimpleApplicationContextAware : SimpleApplicationContextAware and send SimpleAppEvent
2020-05-20 18:30:12.441 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.e.SimpleEventListener : event: net.teaho.demo.spring.boot.startup.none.spring.event.SimpleAppEvent[source=event source], source: event source
2020-05-20 18:30:12.444 INFO 81568 --- [ main] n.t.d.s.b.s.n.config.BeanConfiguration : [net.teaho.demo.spring.boot.startup.none.spring.spi.DemoSpringLoaderImpl@c96a4ea]
2020-05-20 18:30:12.484 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.l.LoggingLifeCycle : In Life cycle bean start().
2020-05-20 18:30:12.496 INFO 81568 --- [ main] n.t.d.s.b.startup.none.ApplicationMain : Started ApplicationMain in 1.573 seconds (JVM running for 3.195)
2020-05-20 18:30:12.496 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.SimpleRunListener : started, context: org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Mon May 25 18:30:11 CST 2020
2020-05-20 18:30:12.497 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.EchoApplicationRunner : EchoApplicationRunner running, args:org.springframework.boot.DefaultApplicationArguments@45673f68
2020-05-20 18:30:12.497 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.EchoCommandLineRunner : EchoCommandLineRunner running
2020-05-20 18:30:12.497 INFO 81568 --- [ main] n.t.d.s.b.s.n.s.r.SimpleRunListener : running, context: org.springframework.context.annotation.AnnotationConfigApplicationContext@1d730606, started on Mon May 25 18:30:11 CST 2020
2020-05-20 18:30:12.500 INFO 81568 --- [ Thread-3] n.t.d.s.b.s.n.s.l.LoggingLifeCycle : In Life cycle bean stop().

到此這篇關于Spring boot啟動原理及相關組件的文章就介紹到這了,更多相關Spring boot啟動原理內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論