什么是SpringBoot
日常开发中采用的是开源的若依框架,也就是SpringBoot框架,那么什么是SpringBoot框架呢?
SpringBoot是一款开箱即用框架,提供各种默认配置来简化项目配置,让我们的Spring应用变的更轻量化、更快的入门,在主程序执行main函数就可以运行,也可以打包你的应用为jar并通过使用java -jar来运行你的Web应用。 使用SpringBoot只需很少的配置,大部分的时候直接使用默认的配置即可。同时后续也可以与Spring Cloud的微服务无缝结合。
SpringBoot启动流程
SpringBoot启动流程涉及到的步骤相对来说容易理解,这里我先准备一个启动类
启动类需要标注@SpringBootApplication的注解,然后就可以直接以main函数的方式执行SpringApplication.run(DemoApplication.class, args);就可以启动项目,非常简单,下面我们再逐步分析每一步执行流程,main函数代码
publicclassDemoApplication{ publicstaticvoidmain(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
SpringApplication.run
首先我们跟进SpringApplication.run(DemoApplication.class, args);方法可以看到
run方法是一个static方法,继续点击run(new Class[] { primarySource }, args);方法可以看到调用了另一个run方法
SpringApplication初始化
在第二个static静态run方法中可以看到new了一个SpringApplication对象,同时继续调用其的run方法来启动SpringBoot项目,下面我们先来看一下SpringApplication对象是如何构建的,进入new SpringApplication(primarySources)的构造方法可以看到
其中primarySources就是启动类的class对象,继续跟进可以看到SpringApplication构造类加载信息
WebApplicationType
在SpringApplication的构造类中通过WebApplicationType.deduceFromClasspath();判断当前应用程序的容器,一共有三种容器,更进去可以看到WebApplicationType如图
可以看到包含三种容器REACTIVE、NONE、SERVLET,默认用的是WebApplicationType.SERVLET容器。
加载spring.factories
再回到SpringApplication对象继续往下看,可以看到this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();方法getBootstrapRegistryInitializersFromSpringFactories();从spring.factories中获取BootstrapRegistryInitializer,源码
加入源码解析
"deprecation") (privateList<BootstrapRegistryInitializer>getBootstrapRegistryInitializersFromSpringFactories() { ArrayList<BootstrapRegistryInitializer>initializers=newArrayList<>(); //从spring.factories中获取Bootstrapper集合,遍历转化为BootstrapRegistryInitializer并存入initializersgetSpringFactoriesInstances(Bootstrapper.class).stream() .map((bootstrapper) -> ((BootstrapRegistryInitializer) bootstrapper::initialize)) .forEach(initializers::add); //从spring.factories中获取BootstrapRegistryInitializer集合并存入initializers,最后返回initializers集合initializers.addAll(getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); returninitializers; }
继续回到SpringApplication对象继续往下看会看到
其中getSpringFactoriesInstances(ApplicationContextInitializer.class)和getSpringFactoriesInstances(ApplicationListener.class)分别表示从spring.factories中获取容器上下文相关初始化ApplicationContextInitializer和容器监听器相关初始化ApplicationListener
获取完容器上下文初始化和监听器初始化器之后通过setInitializers((Collection)和setListeners((Collection)分别放入List initializers和List listeners,这里我们来启动一下程序看一下是否是这样
可以看到已经将spring.factories中的配置加载进来了。
deduceMainApplicationClass
后面继续跟进可以看到deduceMainApplicationClass()理解为推断推论主程序类,debug可以看到获取了main函数所在的主程序类
自定义spring.factories
增加一个类ApplicationInit实现上下文初始化接口ApplicationContextInitializer,重写initialize方法,
publicclassApplicationInitimplementsApplicationContextInitializer { publicvoidinitialize(ConfigurableApplicationContextapplicationContext) { System.out.println("加载自定义ApplicationContextInitializer..."); } }
同时增加项目spring.factories配置
#ApplicationContextInitializersorg.springframework.context.ApplicationContextInitializer=\com.ruoyi.web.controller.common.ApplicationInit
启动应用程序可以看到
初始化完成SpringApplication之后就可以运行run方法了
SpringBoot启动run
初始化完成之后就可以正式进入run阶段了
结合run阶段的源码来看看启动流程
publicConfigurableApplicationContextrun(String... args) { //实例化一个计时器,统计项目启动时间StopWatchstopWatch=newStopWatch(); //启动计时器stopWatch.start(); //初始化上下文对象DefaultBootstrapContextbootstrapContext=createBootstrapContext(); //定义可配置上下文ConfigurableApplicationContextcontext=null; //设置系统headless属性默认值是trueconfigureHeadlessProperty(); //从spring.factories中获取运行监听器 getRunListenersSpringApplicationRunListenerslisteners=getRunListeners(args); //启动监听器listeners.starting(bootstrapContext, this.mainApplicationClass); try { //在命令行下启动应用带的参数ApplicationArgumentsapplicationArguments=newDefaultApplicationArguments(args); //准备环境 prepareEnvironmentConfigurableEnvironmentenvironment=prepareEnvironment(listeners, bootstrapContext, applicationArguments); // 配置忽略的beanconfigureIgnoreBeanInfo(environment); //打印在src/main/resources下放入名字是banner的自定义文件BannerprintedBanner=printBanner(environment); //根据webApplicationType调用工厂类创建应用程序上下文容器context=createApplicationContext(); //设置一个启动器,设置应用程序启动context.setApplicationStartup(this.applicationStartup); //配置容器的基本信息prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); //刷新容器refreshContext(context); //在刷新上下文后调用afterRefresh(context, applicationArguments); //计时器停止stopWatch.stop(); if (this.logStartupInfo) { //打印启动完成的日志newStartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //监听应用上下文启动完成所有的运行监听器调用 started() 方法listeners.started(context); //遍历所有的 runner,调用 run 方法callRunners(context, applicationArguments); } catch (Throwableex) { //异常处理,如果run过程发生异常handleRunFailure(context, ex, listeners); thrownewIllegalStateException(ex); } try { //所有的运行监听器调用running()方法,监听应用上下文listeners.running(context); } catch (Throwableex) { //异常处理handleRunFailure(context, ex, null); thrownewIllegalStateException(ex); } //返回最终构建的容器对象ConfigurableApplicationContextreturncontext; }
run方法开始先初始化一个计时器,开启计时,之后会初始化一个上下文对象
createBootstrapContext
初始化上下文对象,这里将初始化SpringApplication是从spring.factories中获取的bootstrapRegistryInitializers进行初始化
之后继续向下看到configureHeadlessProperty();该方法主要设置系统headless属性默认值是true,查看源码
getRunListeners
从spring.factories中获取运行监听器EventPublishingRunListener
debug该方法可以看到从配置中加载的运行监听器方法
后续继续启动监听器listeners.starting(),调用starting()方法
prepareEnvironment
我们继续看prepareEnvironment方法,跟进去可以看到当前方法涉及getOrCreateEnvironment、configureEnvironment、ConfigurationPropertySources、DefaultPropertiesPropertySource、bindToSpringApplication、convertEnvironment,我们来看一下源码
privateConfigurableEnvironmentprepareEnvironment(SpringApplicationRunListenerslisteners, DefaultBootstrapContextbootstrapContext, ApplicationArgumentsapplicationArguments) { // 创建和配置环境ConfigurableEnvironmentenvironment=getOrCreateEnvironment(); //配置 property sources 和 profilesconfigureEnvironment(environment, applicationArguments.getSourceArgs()); //将environment.getPropertySources()放在第一个位置ConfigurationPropertySources.attach(environment); //运行监听器通知所有监听器环境准备完成listeners.environmentPrepared(bootstrapContext, environment); //Move the 'defaultProperties' property source so that it's the last sourceDefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties."); //Bind the environment to the {@link SpringApplication}bindToSpringApplication(environment); //环境转换成StandardEnvironmentif (!this.isCustomEnvironment) { environment=convertEnvironment(environment); } ConfigurationPropertySources.attach(environment); //返回环境配置对象ConfigurableEnvironmentreturnenvironment; }
这里我们来debug看一下执行过程中环境加载情况
加载完成之后执行configureIgnoreBeanInfo方法配置忽略的bean信息,继续往下看
printBanner
打印配置的banner文本信息
banner文件路径在\src\main\resources\banner.txt,可以通过更该文件内容展示不同的启动成功信息。
继续向下看,看到createApplicationContext方法创建应用程序的上下文容器,创建完之后,继续看prepareContext,该方法主要配置容器的基本信息
prepareContext
prepareContext也是一个重要的方法,我们来看一下源码方法
privatevoidprepareContext(DefaultBootstrapContextbootstrapContext, ConfigurableApplicationContextcontext, ConfigurableEnvironmentenvironment, SpringApplicationRunListenerslisteners, ApplicationArgumentsapplicationArguments, BannerprintedBanner) { //设置容器的环境变量context.setEnvironment(environment); //设置容器的ResourceLoader、ClassLoader、ConversionServicepostProcessApplicationContext(context); //获取所有初始化器调用initialize()初始化applyInitializers(context); //触发所有 SpringApplicationRunListener监听器的contextPrepared事件方法listeners.contextPrepared(context); bootstrapContext.close(context); if (this.logStartupInfo) { //打印启动日志logStartupInfo(context.getParent() ==null); logStartupProfileInfo(context); } // Add boot specific singleton beans 添加启动特定的单例beanConfigurableListableBeanFactorybeanFactory=context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner!=null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactoryinstanceofDefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(newLazyInitializationBeanFactoryPostProcessor()); } // Load the sources 加载所有的资源Set<Object>sources=getAllSources(); //断言资源必须非空Assert.notEmpty(sources, "Sources must not be empty"); //Load beans into the application context 加载启动类 the context to load beans into 将启动类注入容器load(context, sources.toArray(newObject[0])); //触发所有SpringApplicationRunListener监听器contextLoaded方法listeners.contextLoaded(context); }
refreshContext
配置完容器基本信息后,刷新容器上下文refreshContext方法,
继续跟进去可以看到
这里我们看web容器的类,
源码如下,注释比较容易理解,这里不再详细介绍里面的每一步加载了,先写主流程
publicvoidrefresh() throwsBeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { StartupStepcontextRefresh=this.applicationStartup.start("spring.context.refresh"); // Prepare this context for refreshing.prepareRefresh(); // Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactorybeanFactory=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); StartupStepbeanPostProcess=this.applicationStartup.start("spring.context.beans.post-process"); // Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory); beanPostProcess.end(); // 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 (BeansExceptionex) { 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.throwex; } finally { // Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches(); contextRefresh.end(); } } }
刷新容器上下文refreshContext方法之后看到afterRefresh是一个空方法,主要用于开发者拓展使用
listeners.started(context)
容器配置都完成之后,这时监听应用上下文启动完成所有的运行监听器调用 started() 方法,发布监听应用的启动事件,如图
后续继续执行callRunners方法遍历所有runner,调用run方法
上述都完成之后到了最后一步,执行listener.running方法
运行所有运行监听器,该方法执行以后SpringApplication.run(DemoApplication.class, args)也就算执行完成了,那么SpringBoot的ApplicationContext也就启动完成了。
总结
SpringBoot的执行流程整体上分为两个部分,也就是SpringApplication的初始化和SpringApplication.run方法,所有的启动加载过程都在这两个方法中,一篇文章写的太多不方便阅读,另外个人觉得太长的文章也没有人有耐心看完,所以中间一些细节没有细究,后面会继续补充里面细节的内容,感谢大家的阅读,欢迎有问题的评论区留言,共同学习共同成长。