0


Spring Boot启动流程详解

Spring Boot是一个基于Spring框架的快速开发工具,它可以帮助我们快速搭建一个可运行的Spring应用。本文将详细介绍Spring Boot的启动流程,帮助大家更好地理解Spring Boot的工作原理。

一、Spring Boot启动流程概述

Spring Boot的启动流程可以分为以下几个阶段:

  • 初始化配置
  • 创建应用程序上下文
  • 刷新上下文(启动核心)
  • 通知监听者-启动程序完成
publicConfigurableApplicationContextrun(String... args){long startTime =System.nanoTime();DefaultBootstrapContext bootstrapContext =this.createBootstrapContext();ConfigurableApplicationContext context =null;this.configureHeadlessProperty();SpringApplicationRunListeners listeners =this.getRunListeners(args);
        listeners.starting(bootstrapContext,this.mainApplicationClass);try{// 获取args参数对象ApplicationArguments applicationArguments =newDefaultApplicationArguments(args);// 读取Springboot配置文件并创建Environment对象// 这里创建的Environment对象实际为ConfigurableEnvironmentConfigurableEnvironment environment =this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);this.configureIgnoreBeanInfo(environment);// 打印Banner图标Banner printedBanner =this.printBanner(environment);// 创建ApplicationContext应用行下文,即创建容器
            context =this.createApplicationContext();
            context.setApplicationStartup(this.applicationStartup);// 准备容器this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);// 初始化容器this.refreshContext(context);this.afterRefresh(context, applicationArguments);Duration timeTakenToStartup =Duration.ofNanos(System.nanoTime()- startTime);if(this.logStartupInfo){(newStartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);}// 调用运行时监听器的started()方法// 该方法需要在应用程序启动后,CommandLineRunners和ApplicationRunners被调用前执行
            listeners.started(context, timeTakenToStartup);this.callRunners(context, applicationArguments);}catch(Throwable var12){this.handleRunFailure(context, var12, listeners);thrownewIllegalStateException(var12);}try{Duration timeTakenToReady =Duration.ofNanos(System.nanoTime()- startTime);
            listeners.ready(context, timeTakenToReady);return context;}catch(Throwable var11){this.handleRunFailure(context, var11,(SpringApplicationRunListeners)null);thrownewIllegalStateException(var11);}}

这段代码是Spring Boot启动时的核心方法,它的主要作用是创建和配置Spring应用程序的上下文。

首先,记录启动开始时间,并创建一个DefaultBootstrapContext对象和一个空的ConfigurableApplicationContext对象。然后,获取运行监听器并调用其starting()方法,表示应用程序正在启动。

接下来,创建一个ApplicationArguments对象,用于存储应用程序的命令行参数。然后,调用prepareEnvironment()方法准备环境,包括加载配置文件、扫描并注册Bean、初始化Spring容器等。

接着,打印应用程序的Banner信息,并创建一个新的ConfigurableApplicationContext对象。设置应用程序的启动类和启动配置,并调用prepareContext()方法准备上下文。

然后,调用refreshContext()方法刷新上下文,并调用afterRefresh()方法执行一些后续操作。计算应用程序启动所花费的时间,并在日志中记录启动信息。

最后,调用运行监听器的started()方法,表示应用程序已经启动。然后,调用callRunners()方法执行所有的CommandLineRunner和ApplicationRunner接口实现。

如果在启动过程中出现异常,会调用handleRunFailure()方法处理异常,并抛出一个IllegalStateException异常。

最后,调用运行监听器的ready()方法,表示应用程序已经准备好,并返回创建的ConfigurableApplicationContext对象。

二、详细解析

初始化配置

Spring Boot启动时,会首先加载配置文件(如application.properties或application.yml)中的配置信息。这些配置信息包括数据源、缓存、日志等相关信息。

privateConfigurableEnvironmentprepareEnvironment(SpringApplicationRunListeners listeners,DefaultBootstrapContext bootstrapContext,ApplicationArguments applicationArguments){ConfigurableEnvironment environment =this.getOrCreateEnvironment();this.configureEnvironment(environment, applicationArguments.getSourceArgs());ConfigurationPropertySources.attach(environment);
        listeners.environmentPrepared(bootstrapContext, environment);DefaultPropertiesPropertySource.moveToEnd(environment);Assert.state(!environment.containsProperty("spring.main.environment-prefix"),"Environment prefix cannot be set via properties.");this.bindToSpringApplication(environment);if(!this.isCustomEnvironment){EnvironmentConverter environmentConverter =newEnvironmentConverter(this.getClassLoader());
            environment = environmentConverter.convertEnvironmentIfNecessary(environment,this.deduceEnvironmentClass());}ConfigurationPropertySources.attach(environment);return environment;}

这段代码是一个名为prepareEnvironment的方法,它接收三个参数:listeners(SpringApplicationRunListeners类型)、bootstrapContext(DefaultBootstrapContext类型)和applicationArguments(ApplicationArguments类型)。该方法的主要作用是准备和配置Spring应用程序的环境。

首先,通过调用this.getOrCreateEnvironment()方法获取或创建一个ConfigurableEnvironment对象,并将其赋值给变量environment。

接下来,调用this.configureEnvironment(environment, applicationArguments.getSourceArgs())方法对环境进行配置,传入environment和applicationArguments.getSourceArgs()作为参数。

然后,调用ConfigurationPropertySources.attach(environment)方法将环境附加到配置属性源。

接着,调用listeners.environmentPrepared(bootstrapContext, environment)方法通知监听器环境已准备好。

然后,调用DefaultPropertiesPropertySource.moveToEnd(environment)方法将默认属性源移动到环境的末尾。

接下来,使用断言语句Assert.state(!environment.containsProperty(“spring.main.environment-prefix”), “Environment prefix cannot be set via properties.”)确保环境中不包含名为"spring.main.environment-prefix"的属性。

然后,调用this.bindToSpringApplication(environment)方法将环境绑定到Spring应用程序。

如果this.isCustomEnvironment为false,则创建一个EnvironmentConverter对象,并调用其convertEnvironmentIfNecessary(environment, this.deduceEnvironmentClass())方法根据需要转换环境。

最后,再次调用ConfigurationPropertySources.attach(environment)方法将环境附加到配置属性源,并将environment返回。

创建应用程序上下文

初始化和配置好后,开始创建应用程序上下文,createApplicationContext ,关键的工厂BeanFactory就是此处创建

protectedConfigurableApplicationContextcreateApplicationContext(){returnthis.applicationContextFactory.create(this.webApplicationType);}

刷新上下文(启动核心)

工厂配置,bean处理器配置,类的扫描,解析,bean定义,bean类信息缓存,服务器创建,bean实例化,动态代理对象的创建等,

privatevoidrefreshContext(ConfigurableApplicationContext context){if(this.registerShutdownHook){
            shutdownHook.registerApplicationContext(context);}this.refresh(context);}

该方法的主要作用是刷新Spring应用程序的上下文。

首先,如果this.registerShutdownHook为true,则调用shutdownHook.registerApplicationContext(context)方法将应用程序上下文注册到关闭钩子中。

然后,调用this.refresh(context)方法刷新上下文。

publicvoidrefresh()throwsBeansException,IllegalStateException{synchronized(this.startupShutdownMonitor){StartupStep contextRefresh =this.applicationStartup.start("spring.context.refresh");this.prepareRefresh();ConfigurableListableBeanFactory beanFactory =this.obtainFreshBeanFactory();this.prepareBeanFactory(beanFactory);try{this.postProcessBeanFactory(beanFactory);StartupStep beanPostProcess =this.applicationStartup.start("spring.context.beans.post-process");//注册并实例化bean工厂处理器,并调用他们this.invokeBeanFactoryPostProcessors(beanFactory);//注册并实例化bean处理器this.registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();this.initMessageSource();this.initApplicationEventMulticaster();//初始化一些与上下文有特别关系的bean对象(创建tomcat)this.onRefresh();this.registerListeners();// 实例化所有bean工厂缓存的bean对象(剩下的).this.finishBeanFactoryInitialization(beanFactory);//发布通知-通知上下文刷新完成(包括启动tomcat)this.finishRefresh();}catch(BeansException var10){if(this.logger.isWarnEnabled()){this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: "+ var10);}this.destroyBeans();this.cancelRefresh(var10);throw var10;}finally{this.resetCommonCaches();
                contextRefresh.end();}}}

该方法的主要作用是刷新Spring应用程序的上下文。

首先,通过同步块来确保线程安全。然后,创建一个名为contextRefresh的StartupStep对象,并调用this.prepareRefresh()方法准备刷新。接着,获取一个新的BeanFactory对象,并调用this.prepareBeanFactory(beanFactory)方法准备BeanFactory。

接下来,在try-catch语句中执行以下操作:

  • 调用this.postProcessBeanFactory(beanFactory)方法对BeanFactory进行后处理。
  • 创建一个名为beanPostProcess的StartupStep对象,并调用this.invokeBeanFactoryPostProcessors(beanFactory)方法执行BeanFactory的后处理器。
  • 调用this.registerBeanPostProcessors(beanFactory)方法注册Bean的后处理器。
  • 结束beanPostProcess的计时。
  • 调用this.initMessageSource()方法初始化消息源。
  • 调用this.initApplicationEventMulticaster()方法初始化应用程序事件多播器。
  • 调用this.onRefresh()方法执行刷新操作。
  • 调用this.registerListeners()方法注册监听器。
  • 调用this.finishBeanFactoryInitialization(beanFactory)方法完成BeanFactory的初始化。
  • 调用this.finishRefresh()方法完成刷新操作。

如果在执行过程中出现BeansException异常,会记录警告日志,销毁Beans,取消刷新操作,并抛出该异常。最后,在finally语句中重置缓存和计时器。

通知监听者-启动程序完成

发布通知监听器启动完成,监听器会根据事件类型做个性化操作

voidstarting(ConfigurableBootstrapContext bootstrapContext,Class<?> mainApplicationClass){this.doWithListeners("spring.boot.application.starting",(listener)->{
            listener.starting(bootstrapContext);},(step)->{if(mainApplicationClass !=null){
                step.tag("mainApplicationClass", mainApplicationClass.getName());}});}voidenvironmentPrepared(ConfigurableBootstrapContext bootstrapContext,ConfigurableEnvironment environment){this.doWithListeners("spring.boot.application.environment-prepared",(listener)->{
            listener.environmentPrepared(bootstrapContext, environment);});}voidcontextPrepared(ConfigurableApplicationContext context){this.doWithListeners("spring.boot.application.context-prepared",(listener)->{
            listener.contextPrepared(context);});}voidcontextLoaded(ConfigurableApplicationContext context){this.doWithListeners("spring.boot.application.context-loaded",(listener)->{
            listener.contextLoaded(context);});}voidstarted(ConfigurableApplicationContext context,Duration timeTaken){this.doWithListeners("spring.boot.application.started",(listener)->{
            listener.started(context, timeTaken);});}voidready(ConfigurableApplicationContext context,Duration timeTaken){this.doWithListeners("spring.boot.application.ready",(listener)->{
            listener.ready(context, timeTaken);});}voidfailed(ConfigurableApplicationContext context,Throwable exception){this.doWithListeners("spring.boot.application.failed",(listener)->{this.callFailedListener(listener, context, exception);},(step)->{
            step.tag("exception", exception.getClass().toString());
            step.tag("message", exception.getMessage());});}privatevoidcallFailedListener(SpringApplicationRunListener listener,ConfigurableApplicationContext context,Throwable exception){try{
            listener.failed(context, exception);}catch(Throwable var6){if(exception ==null){ReflectionUtils.rethrowRuntimeException(var6);}if(this.log.isDebugEnabled()){this.log.error("Error handling failed", var6);}else{String message = var6.getMessage();
                message = message !=null? message :"no error message";this.log.warn("Error handling failed ("+ message +")");}}}privatevoiddoWithListeners(String stepName,Consumer<SpringApplicationRunListener> listenerAction){this.doWithListeners(stepName, listenerAction,(Consumer)null);}privatevoiddoWithListeners(String stepName,Consumer<SpringApplicationRunListener> listenerAction,Consumer<StartupStep> stepAction){StartupStep step =this.applicationStartup.start(stepName);this.listeners.forEach(listenerAction);if(stepAction !=null){
            stepAction.accept(step);}

        step.end();}

SpringApplicationRunListeners是Spring Boot框架中的一个接口,用于监听应用程序的运行过程。它提供了一系列的回调方法,可以在应用程序的不同阶段执行自定义的逻辑。

以下是SpringApplicationRunListeners接口中定义的一些主要方法:

  • starting(): 在应用程序启动之前调用,可以执行一些初始化操作。
  • environmentPrepared(): 在环境准备完成后调用,可以获取到应用程序的环境信息。
  • contextPrepared(): 在上下文准备完成后调用,可以获取到应用程序的上下文对象。
  • contextLoaded(): 在上下文加载完成后调用,可以执行一些额外的配置操作。
  • started(): 在应用程序启动完成后调用,可以执行一些后续处理操作。
  • running(): 在应用程序运行期间持续调用,可以执行一些周期性的任务。
  • failed(): 在应用程序启动失败时调用,可以执行一些错误处理操作。
  • stopped(): 在应用程序停止后调用,可以执行一些清理操作。

总结

通过以上介绍,我们了解了Spring Boot的启动流程。Spring Boot通过自动化的配置和简化的部署流程,使得我们能够快速搭建一个可运行的Spring应用。希望本文能够帮助大家更好地理解Spring Boot的工作原理。

标签: spring boot 后端 java

本文转载自: https://blog.csdn.net/Luck_gun/article/details/136441023
版权归原作者 吴代庄 所有, 如有侵权,请联系我们删除。

“Spring Boot启动流程详解”的评论:

还没有评论