Springboot啟動原理詳細講解
主啟動類方法:
@SpringBootApplication public class MyJavaTestApplication { public static void main(String[] args) { SpringApplication.run(MyJavaTestApplication.class, args); } }
點擊進入方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }
先看看new SpringApplication(primarySources)里做了什么?
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //判斷當前web容器的類型,一般返回SERVLET,標識是web類型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); //獲取META-INF/spring.factories文件中以//org.springframework.boot.Bootstrapper和 //org.springframework.boot.BootstrapRegistryInitializer為key的class //創(chuàng)建對象,然后裝入對象屬性中; this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories(); setInitializers((Collection) //獲取META-INF/spring.factories文件中以 //org.springframework.context.ApplicationContextInitializer的key的對 //存入到initializers集合屬性中; getSpringFactoriesInstances(ApplicationContextInitializer.class)); //獲取META-INF/spring.factories文件中以 //org.springframework.context.ApplicationListener的key的對 //存入到listeners集合屬性中; setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //找到main方法的啟動主類class對象賦值到對象屬性中。 this.mainApplicationClass = deduceMainApplicationClass(); }
spring.factories讀取了對外擴展的ApplicationContextInitializer ,ApplicationListener 對外擴展, 對類解耦(比如全局配置文件、熱部署插件)
所以我們可以利用這一特性,在容器加載的各個階段進行擴展。
上面讀取到的對象getSpringFactoriesInstances(ApplicationContextInitializer.class))
存入到initializers集合中的對象
getSpringFactoriesInstances(ApplicationListener.class));
存入到listeners集合屬性中的對象
再看一下最重要的run方法:
public ConfigurableApplicationContext run(String... args) { // 用來記錄當前springboot啟動耗時 StopWatch stopWatch = new StopWatch(); // 就是記錄了啟動開始時間 stopWatch.start(); //創(chuàng)建DefaultBootstrapContext bootstrapContext = new //DefaultBootstrapContext();對象,然后循環(huán)執(zhí)行上面文件中賦值的bootstrapRegistryInitializers集合中對象的方法,入?yún)⒕褪莃ootstrapContext對象,利用此處我們可以擴展改變bootstrapContext對象中的一項屬性值等,在這里還不知道此bootstrapContext對象的用處。 DefaultBootstrapContext bootstrapContext = createBootstrapContext(); // 它是任何spring上下文的接口, 所以可以接收任何ApplicationContext實現(xiàn) ConfigurableApplicationContext context = null; // 開啟了Headless模式,暫時不知道此模式的作用 configureHeadlessProperty(); //去spring.factroies中讀取了 org.springframework.boot.SpringApplicationRunListener為key的對象,默認是EventPublishingRunListener對象,然后封裝進SpringApplicationRunListeners 對象中,此對象還是比較有用的,用來發(fā)布springboot啟動進行中的各個狀態(tài)的事件,上面方法中讀取到的監(jiān)聽器就可以監(jiān)聽到這些事件,所以可以運用這些特性進行自己的擴展。 SpringApplicationRunListeners listeners = getRunListeners(args); // 發(fā)布1.ApplicationStartingEvent事件,在運行開始時發(fā)送 ,發(fā)送springboot啟動開始事件; listeners.starting(bootstrapContext, this.mainApplicationClass); try { //根據(jù)啟動項目命令所帶的參數(shù)創(chuàng)建applicationArguments 對象 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //初始化環(huán)境變量:讀取系統(tǒng)環(huán)境變量、發(fā)送了一個ApplicationEnvironmentPreparedEvent事件,利用相關(guān)監(jiān)聽器來解析項目中配置文件中的配置,(EnvironmentPostProcessorApplicationListener 監(jiān)聽器解析的配置文件) ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); // 忽略beaninfo的bean configureIgnoreBeanInfo(environment); // 打印Banner 橫幅 Banner printedBanner = printBanner(environment); //根據(jù)webApplicationType 容器類型,創(chuàng)建對應(yīng)的spring上下文,一般是AnnotationConfigServletWebServerApplicationContext; context = createApplicationContext(); //給spring上下文賦值DefaultApplicationStartup對象 context.setApplicationStartup(this.applicationStartup); //預(yù)初始化上下文,這里做了給上下文添加environment對象,上面獲取到的initializers集合中的ApplicationContextInitializer對象執(zhí)行其入?yún)樯舷挛膇nitialize方法,對上下文做編輯,所以此處我們可以做擴展;發(fā)送ApplicationContextInitializedEvent容器初始化事件;發(fā)送BootstrapContextClosedEvent事件;最重要的一個方法就是把啟動配置類注冊成了beanDefinition;發(fā)送ApplicationPreparedEvent事件,并把listeners集合屬性中的事件添加到上下文中; prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); //刷新容器:和spring中的refresh()方法的作用是一樣的,主要的作用就是讀取所有的bean轉(zhuǎn)成beanDefinition然后再創(chuàng)建bean對象;不過這個容器重寫了其中的onRefresh()方法,在此方法中,創(chuàng)建了springboot內(nèi)置的tomcat對象并進行啟動;接下來特別說明一下這個內(nèi)置tomcat refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); //打印啟動時間; if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //發(fā)布容器啟動事件ApplicationStartedEvent;發(fā)布AvailabilityChangeEvent容器可用實踐; listeners.started(context); //執(zhí)行容器中ApplicationRunner、ApplicationRunner類型對象的run方法 callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { //發(fā)布ApplicationReadyEvent容器已經(jīng)正常事件; listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } return context; }
prepareEnvironment方法
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 根據(jù)webApplicationType 創(chuàng)建Environment 創(chuàng)建就會讀取: java環(huán)境變量和系統(tǒng)環(huán)境變量 ConfigurableEnvironment environment = getOrCreateEnvironment(); // 將命令行參數(shù)讀取環(huán)境變量中 configureEnvironment(environment, applicationArguments.getSourceArgs()); // 將@PropertieSource的配置信息 放在第一位, 因為讀取配置文件@PropertieSource優(yōu)先級是最低的 ConfigurationPropertySources.attach(environment); // 發(fā)布了ApplicationEnvironmentPreparedEvent 的監(jiān)聽器 讀取了全局配置文件 listeners.environmentPrepared(environment); // 將所有spring.main 開頭的配置信息綁定SpringApplication bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment, deduceEnvironmentClass()); } //更新PropertySources ConfigurationPropertySources.attach(environment); return environment; }
lprepareContext
l預(yù)初始化上下文
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); // 拿到之前讀取到所有ApplicationContextInitializer的組件, 循環(huán)調(diào)用initialize方法 applyInitializers(context); // 發(fā)布了ApplicationContextInitializedEvent listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // 獲取當前spring上下文beanFactory (負責創(chuàng)建bean) ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } // 在Spring下 如果出現(xiàn)2個重名的bean, 則后讀取到的會覆蓋前面 // 在SpringBoot 在這里設(shè)置了不允許覆蓋, 當出現(xiàn)2個重名的bean 會拋出異常 if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // 設(shè)置當前spring容器是不是要將所有的bean設(shè)置為懶加載 if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 讀取主啟動類,將它注冊為BD、就像我們以前register(啟動類);一個意思 (因為后續(xù)要根據(jù)配置類解析配置的所有bean) load(context, sources.toArray(new Object[0])); //4.讀取完配置類后發(fā)送ApplicationPreparedEvent。 listeners.contextLoaded(context); }
1.初始化SpringApplication 從spring.factories 讀取 listener ApplicationContextInitializer 。
2.運行run方法
3.讀取 環(huán)境變量 配置信息…
4.創(chuàng)建springApplication上下文:ServletWebServerApplicationContext
5.預(yù)初始化上下文 : 讀取啟動類
6.調(diào)用refresh 加載ioc容器
7.加載所有的自動配置類
8.創(chuàng)建servlet容器
9.ps.在這個過程中springboot會調(diào)用很多監(jiān)聽器對外進行擴展
看一下內(nèi)置tomcat如何啟動的:
Springboot的spring容器ServletWebServerApplicationContext對象重新了refresh()方法中的onRefresh()放用來啟動tomcat
@Override protected void onRefresh() { super.onRefresh(); try { //創(chuàng)建tomcat createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }
createWebServer創(chuàng)建tomcat的方法(也可以是Jetty,根據(jù)配置)
private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); //如果servletContext 為null說明是內(nèi)置tomcat,不為null,則使用的是外置tomcat,這個servletContext 是tomcat創(chuàng)建傳入的; if (webServer == null && servletContext == null) { StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create"); //獲取servlet容器創(chuàng)建工廠,tomcat的是TomcatServletWebServerFactory ServletWebServerFactory factory = getWebServerFactory(); createWebServer.tag("factory", factory.getClass().toString()); //創(chuàng)建內(nèi)置tomcat對象,并啟動;getSelfInitializer()這個方法也是很重要的,返回的是ServletWebServerApplicationContext#selfInitialize方法的引用函數(shù),其作用是tomcat啟動時會回調(diào)此方法,并傳入servletContext對象,進行DispacherServlet添加到servletContext中,把當前spring容器和filter過濾器也添加到servletContext中 this.webServer = factory.getWebServer(getSelfInitializer()); createWebServer.end(); getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer)); getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer)); } else if (servletContext != null) { try { getSelfInitializer().onStartup(servletContext); } catch (ServletException ex) { throw new ApplicationContextException("Cannot initialize servlet context", ex); } } initPropertySources(); }
selfInitializ方法
private void selfInitialize(ServletContext servletContext) throws ServletException { //把spring容器和servletContext進行相互引用 prepareWebApplicationContext(servletContext); registerApplicationScope(servletContext); WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext); //servletContext中添加Dispacherservlet和多個fileter for (ServletContextInitializer beans : getServletContextInitializerBeans()) { beans.onStartup(servletContext); } }
到此這篇關(guān)于Springboot啟動原理詳細講解的文章就介紹到這了,更多相關(guān)Springboot啟動原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
常用json與javabean互轉(zhuǎn)的方法實現(xiàn)
這篇文章主要介紹了常用json與javabean互轉(zhuǎn)的方法實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友們下面隨著小編來一起學(xué)習學(xué)習吧2021-04-04SpringCloud Open feign 使用okhttp 優(yōu)化詳解
這篇文章主要介紹了SpringCloud Open feign 使用okhttp 優(yōu)化詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02