您好,登錄后才能下訂單哦!
SpringBoot的啟動流程有哪些,針對這個問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
在IDE中啟動SpringBoot主類(XXXApplication)中的main方法
使用mvn spring-boot:run命令啟動
打成jar包之后使用java -jar xxx.jar運(yùn)行
打成war包之后放在web容器中運(yùn)行
2.SpringBoot啟動流程主要分為三步:
第一部分:SpringApplication初始化模塊,配置一些基本的環(huán)境變量,資源,監(jiān)聽器,構(gòu)造器;
第二部分:實現(xiàn)了應(yīng)用具體的啟動方案,包括流程的監(jiān)聽模塊,加載配置環(huán)境模塊以及創(chuàng)建上下文環(huán)境模塊
第三部分:自動化配置模塊,這個模塊是實現(xiàn)SpringBoot的自動配置
SpringBoot程序的主入口就是標(biāo)注了@SpringBootApplication注解的類,該類中有一個main方法,在main方法中調(diào)用SpringApplication的run()方法,這個run()方法來啟動整個程序
@SpringBootApplication public class CrmWebApiApplication extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(CrmWebApiApplication.class, args); } }
下面是@SpringBootApplication注解的頭部源碼
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {
這是一個組合注解,其中標(biāo)注的注解主要有以下作用
@EnableAutoConfiguration: 開啟SpringBoot自動配置,在程序啟動時會自動加載SpringBoot的默認(rèn)配置,如果有對一些參數(shù)進(jìn)行配置,則會在程序啟動時或調(diào)用時進(jìn)行追加或者覆蓋
@SpringBootConfiguration: 這個注解和@Configuration注解的作用一樣,用來表示被標(biāo)注的類是一個配置類,會將被標(biāo)注的類中一個或多個被@Bean注解修飾的方法添加到Spring容器中,實例的名字默認(rèn)是方法名
@ComponentScan: 包掃描注解,默認(rèn)掃描主類包路徑下的類
進(jìn)入run()方法后的代碼如下:
/** * Static helper that can be used to run a {@link SpringApplication} from the * specified sources using default settings and user supplied arguments. * @param sources the sources to load * @param args the application arguments (usually passed from a Java main method) * @return the running {@link ApplicationContext} */ public static ConfigurableApplicationContext run(Object[] sources, String[] args) { return new SpringApplication(sources).run(args); }
這里會創(chuàng)建一個SpringApplication類的實例,進(jìn)入SpringApplication類中可以看到構(gòu)造方法里調(diào)用了一個initialize(sources)方法
/** * Create a new {@link SpringApplication} instance. The application context will load * beans from the specified sources (see {@link SpringApplication class-level} * documentation for details. The instance can be customized before calling * {@link #run(String...)}. * @param sources the bean sources * @see #run(Object, String[]) * @see #SpringApplication(ResourceLoader, Object...) */ public SpringApplication(Object... sources) { initialize(sources); }
Initialize(sources)方法源碼如下:
@SuppressWarnings({ "unchecked", "rawtypes" }) private void initialize(Object[] sources) { if (sources != null && sources.length > 0) { //將sources設(shè)置到SpringApplication類的source屬性中,這時的source值只有主類 this.sources.addAll(Arrays.asList(sources)); } //判斷是不是web程序, this.webEnvironment = deduceWebEnvironment(); //從spring.factories文件中找出key為ApplicationContextInitializer的類進(jìn)行實例化,然后設(shè)置到SpringApplciation類的initializers屬性中,這個過程也是找出所有的應(yīng)用程序初始化器 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //從spring.factories文件中找出key為ApplicationListener的類并實例化后設(shè)置到SpringApplication的listeners屬性中。這個過程就是找出所有的應(yīng)用程序事件監(jiān)聽器 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //找出main類,也就是SpringBoot項目的主類 this.mainApplicationClass = deduceMainApplicationClass(); }
執(zhí)行完初始化之后回到run()方法中,完整代碼如下:
/** * 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 stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; FailureAnalyzers analyzers = null; configureHeadlessProperty(); //創(chuàng)建應(yīng)用監(jiān)聽器 SpringApplicationRunListeners listeners = getRunListeners(args); //開始監(jiān)聽 listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //加載SpringBoot配置環(huán)境ConfigurableEnvironment,見2.2配置ConfigurableEnvironment ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments); //打印banner Banner printedBanner = printBanner(environment); //創(chuàng)建應(yīng)用程序上下文,見2.3 創(chuàng)建應(yīng)用程序上下文 context = createApplicationContext(); analyzers = new FailureAnalyzers(context); prepareContext(context, environment, listeners, applicationArguments,printedBanner); 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); } }
加載SpringBoot配置環(huán)境ConfigurableEnvironment流程如下:
private ConfigurableEnvironment prepareEnvironment( SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // Create and configure the environment ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); listeners.environmentPrepared(environment); if (!this.webEnvironment) { environment = new EnvironmentConverter(getClassLoader()) .convertToStandardEnvironmentIfNecessary(environment); } return environment; }
在加載配置環(huán)境的過程中會判斷是否是web容器啟動,如果是容器啟動會加載StandardServletEnvironment
private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } if (this.webEnvironment) { return new StandardServletEnvironment(); } return new StandardEnvironment(); }
StandardServletEnvironment類的繼承關(guān)系如下,StandardServletEnvironment
PropertyResolver接口是用于解析任何基礎(chǔ)源的屬性的接口,在加載完配置之后會將配置環(huán)境加入到監(jiān)聽器對象SpringApplicationRunListeners中。
然后會創(chuàng)建應(yīng)用上下文對象,具體代碼如下:
protected ConfigurableApplicationContext createApplicationContext() { Class<?> contextClass = this.applicationContextClass; if (contextClass == null) { try { 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); }
方法會先顯式的獲取應(yīng)用上下文對象,如果對象為空,再加載默認(rèn)的環(huán)境配置,通過是否是webEnvironment進(jìn)行判斷,默認(rèn)選擇的是AnnotationConfigApplicationContext(注解上下文,通過掃秒注解來加載bean),然后通過BeanUtils來實例化應(yīng)用上下文對象然后返回,ConfigurableApplicationContext類繼承關(guān)系如下:
這里推薦一下我的另一篇博客,不太懂ConfigurableApplicationContext的可以去看一下,https://juejin.im/post/5d72055f5188256bab4c0b6d
回到run()方法中,會調(diào)用prepareContext()方法將environment, listeners,applicationArguments, printedBanner等組件與上下文對象進(jìn)行關(guān)聯(lián)
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); applyInitializers(context); listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans context.getBeanFactory().registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); } // Load the sources Set<Object> sources = getSources(); Assert.notEmpty(sources, "Sources must not be empty"); load(context, sources.toArray(new Object[sources.size()])); listeners.contextLoaded(context); }
然后會調(diào)用refreshContext()方法,實際調(diào)用org.springframework.context.support.AbstractApplicationContext.refresh()內(nèi)的相關(guān)方法。這個方法里會進(jìn)行redis,mybatis等的自動配置,包括spring.factories的加載,bean的實例化,BenFactoryPostProcessor接口的執(zhí)行,BeanPostProcessor接口的執(zhí)行,條件注解的解析,國際化功能的初始化等。
refreshContext()方法執(zhí)行完畢之后會執(zhí)行afterRefresh方法,當(dāng)run()方法執(zhí)行完之后Spring容器也就初始化完畢了
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) { callRunners(context, args); } private void callRunners(ApplicationContext context, ApplicationArguments args) { List<Object> runners = new ArrayList<Object>(); runners.addAll(context.getBeansOfType(ApplicationRunner.class).values()); runners.addAll(context.getBeansOfType(CommandLineRunner.class).values()); AnnotationAwareOrderComparator.sort(runners); for (Object runner : new LinkedHashSet<Object>(runners)) { if (runner instanceof ApplicationRunner) { callRunner((ApplicationRunner) runner, args); } if (runner instanceof CommandLineRunner) { callRunner((CommandLineRunner) runner, args); } } } private void callRunner(ApplicationRunner runner, ApplicationArguments args) { try { (runner).run(args); } catch (Exception ex) { throw new IllegalStateException("Failed to execute ApplicationRunner", ex); } } private void callRunner(CommandLineRunner runner, ApplicationArguments args) { try { (runner).run(args.getSourceArgs()); } catch (Exception ex) { throw new IllegalStateException("Failed to execute CommandLineRunner", ex); } }
關(guān)于SpringBoot的啟動流程有哪些問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。