<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
主啟動類方法:
@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 //建立物件,然後裝入物件屬性中; 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(); //建立DefaultBootstrapContext bootstrapContext = new //DefaultBootstrapContext();物件,然後迴圈執行上面檔案中賦值的bootstrapRegistryInitializers集合中物件的方法,入參就是bootstrapContext物件,利用此處我們可以擴充套件改變bootstrapContext物件中的一項屬性值等,在這裡還不知道此bootstrapContext物件的用處。 DefaultBootstrapContext bootstrapContext = createBootstrapContext(); // 它是任何spring上下文的介面, 所以可以接收任何ApplicationContext實現 ConfigurableApplicationContext context = null; // 開啟了Headless模式,暫時不知道此模式的作用 configureHeadlessProperty(); //去spring.factroies中讀取了 org.springframework.boot.SpringApplicationRunListener為key的物件,預設是EventPublishingRunListener物件,然後封裝進SpringApplicationRunListeners 物件中,此物件還是比較有用的,用來發布springboot啟動進行中的各個狀態的事件,上面方法中讀取到的監聽器就可以監聽到這些事件,所以可以運用這些特性進行自己的擴充套件。 SpringApplicationRunListeners listeners = getRunListeners(args); // 釋出1.ApplicationStartingEvent事件,在執行開始時傳送 ,傳送springboot啟動開始事件; listeners.starting(bootstrapContext, this.mainApplicationClass); try { //根據啟動專案命令所帶的引數建立applicationArguments 物件 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); //初始化環境變數:讀取系統環境變數、傳送了一個ApplicationEnvironmentPreparedEvent事件,利用相關監聽器來解析專案中組態檔中的設定,(EnvironmentPostProcessorApplicationListener 監聽器解析的組態檔) ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments); // 忽略beaninfo的bean configureIgnoreBeanInfo(environment); // 列印Banner 橫幅 Banner printedBanner = printBanner(environment); //根據webApplicationType 容器型別,建立對應的spring上下文,一般是AnnotationConfigServletWebServerApplicationContext; context = createApplicationContext(); //給spring上下文賦值DefaultApplicationStartup物件 context.setApplicationStartup(this.applicationStartup); //預初始化上下文,這裡做了給上下文新增environment物件,上面獲取到的initializers集合中的ApplicationContextInitializer物件執行其入參為上下文initialize方法,對上下文做編輯,所以此處我們可以做擴充套件;傳送ApplicationContextInitializedEvent容器初始化事件;傳送BootstrapContextClosedEvent事件;最重要的一個方法就是把啟動設定類註冊成了beanDefinition;傳送ApplicationPreparedEvent事件,並把listeners集合屬性中的事件新增到上下文中; prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner); //重新整理容器:和spring中的refresh()方法的作用是一樣的,主要的作用就是讀取所有的bean轉成beanDefinition然後再建立bean物件;不過這個容器重寫了其中的onRefresh()方法,在此方法中,建立了springboot內建的tomcat物件並進行啟動;接下來特別說明一下這個內建tomcat refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); //列印啟動時間; if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //釋出容器啟動事件ApplicationStartedEvent;釋出AvailabilityChangeEvent容器可用實踐; listeners.started(context); //執行容器中ApplicationRunner、ApplicationRunner型別物件的run方法 callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, listeners); throw new IllegalStateException(ex); } try { //釋出ApplicationReadyEvent容器已經正常事件; listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, null); throw new IllegalStateException(ex); } return context; }
prepareEnvironment方法
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments) { // 根據webApplicationType 建立Environment 建立就會讀取: java環境變數和系統環境變數 ConfigurableEnvironment environment = getOrCreateEnvironment(); // 將命令列引數讀取環境變數中 configureEnvironment(environment, applicationArguments.getSourceArgs()); // 將@PropertieSource的設定資訊 放在第一位, 因為讀取組態檔@PropertieSource優先順序是最低的 ConfigurationPropertySources.attach(environment); // 釋出了ApplicationEnvironmentPreparedEvent 的監聽器 讀取了全域性組態檔 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預初始化上下文
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { context.setEnvironment(environment); postProcessApplicationContext(context); // 拿到之前讀取到所有ApplicationContextInitializer的元件, 迴圈呼叫initialize方法 applyInitializers(context); // 釋出了ApplicationContextInitializedEvent listeners.contextPrepared(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // 獲取當前spring上下文beanFactory (負責建立bean) ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } // 在Spring下 如果出現2個重名的bean, 則後讀取到的會覆蓋前面 // 在SpringBoot 在這裡設定了不允許覆蓋, 當出現2個重名的bean 會丟擲異常 if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } // 設定當前spring容器是不是要將所有的bean設定為懶載入 if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 讀取主啟動類,將它註冊為BD、就像我們以前register(啟動類);一個意思 (因為後續要根據設定類解析設定的所有bean) load(context, sources.toArray(new Object[0])); //4.讀取完設定類後傳送ApplicationPreparedEvent。 listeners.contextLoaded(context); }
1.初始化SpringApplication 從spring.factories 讀取 listener ApplicationContextInitializer 。
2.執行run方法
3.讀取 環境變數 設定資訊…
4.建立springApplication上下文:ServletWebServerApplicationContext
5.預初始化上下文 : 讀取啟動類
6.呼叫refresh 載入ioc容器
7.載入所有的自動設定類
8.建立servlet容器
9.ps.在這個過程中springboot會呼叫很多監聽器對外進行擴充套件
看一下內建tomcat如何啟動的:
Springboot的spring容器ServletWebServerApplicationContext物件重新了refresh()方法中的onRefresh()放用來啟動tomcat
@Override protected void onRefresh() { super.onRefresh(); try { //建立tomcat createWebServer(); } catch (Throwable ex) { throw new ApplicationContextException("Unable to start web server", ex); } }
createWebServer建立tomcat的方法(也可以是Jetty,根據設定)
private void createWebServer() { WebServer webServer = this.webServer; ServletContext servletContext = getServletContext(); //如果servletContext 為null說明是內建tomcat,不為null,則使用的是外接tomcat,這個servletContext 是tomcat建立傳入的; if (webServer == null && servletContext == null) { StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create"); //獲取servlet容器建立工廠,tomcat的是TomcatServletWebServerFactory ServletWebServerFactory factory = getWebServerFactory(); createWebServer.tag("factory", factory.getClass().toString()); //建立內建tomcat物件,並啟動;getSelfInitializer()這個方法也是很重要的,返回的是ServletWebServerApplicationContext#selfInitialize方法的參照函數,其作用是tomcat啟動時會回撥此方法,並傳入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); } }
到此這篇關於Springboot啟動原理詳細講解的文章就介紹到這了,更多相關Springboot啟動原理內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45