大概是水平有限,最近跟讀代碼與相關書籍感受巨費時間,想深刻弄明白全部的東西很難,因此也只能帶着問題來學習springboot了,之後遇到確切的問題再作深刻了解把,給本身定個目標,暫時只弄清楚容器啓動大致流程,瞭解組件掃描,自動配置,解決循環依賴這幾個問題。 通常啓動的Main方法爲SpringApplication.run(啓動類.class, args);
,跟下去的話會發現調用的就是new SpringApplication(啓動類).run(args)因爲容器刷新內容最關鍵也最複雜,先來了解下除容器刷新以外的流程。java
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); //一般狀況下primarySources就是啓動類,暫時理解這裏就是將啓動類設置爲主配置資源來源 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //經過類路徑中尋找相關類,判斷當前環境是NONE(標準環境(classPath下沒有javax.servlet.Servlet以及org.springframework.web.context.ConfigurableWebApplicationContext)、SERVLET(Servlet環境)、REACTIVE(響應式) this.webApplicationType = WebApplicationType.deduceFromClasspath(); //添加initializers,設置初始化器,這些初始化器將在在容器刷新前回調,原理是經過SpringFactoriesLoader的loadFactoryNames方法在 spring.factories文件中找到的ApplicationContextInitializer接口的配置的實現類的全限定類名,並實例化。 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); //同上,設置ApplicationListener,添加 spring.factories文件中ApplicationListener配置的響應實現類。 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //經過構造一個運行時異常,而後去棧幀中尋找方法名爲main的方法來獲得入口類的名字並設置爲mainApplicationClass this.mainApplicationClass = deduceMainApplicationClass(); }
debug發現有這些:web
他們的做用是:spring
debug發現有這些:編程
他們的做用是:緩存
REACTIVE是響應式編程的東西,指的是應用WebFlux框架下的應用環境,是NIO同步非阻塞IO,將來可能替代當前的MVC,因爲是比較新的技術,應用場景比較有限,暫時不作深刻了解。springboot
public ConfigurableApplicationContext run(String... args) { //這個組件是用來監控啓動時間的,不是很重要 StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; //SpringBootExceptionReporter這個東西是一個異常解析器,實現類只有一個是FailureAnalyzers, //用於打印異常信息,這個集合在下面③處會初始化,集合裏面裝了針對各式各樣的解析器, //在catch到異常後,會遍歷這個集合,尋找合適的解析器,而後打印異常日誌 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); //刷新系統屬性java.awt.headless的值,若是沒有值則設爲true,這個值表示無頭模式(意指缺乏顯示設備,鍵盤或鼠標的系統配置), //在無頭模式下java.awt.Toolkit將使用特定的無頭模式下的實現類,由於就算沒有顯示設備,有些操做任可以被容許。 configureHeadlessProperty(); //① SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { //② ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //配置系統參數spring.beaninfo.ignore,默認值爲ture,字面意思是跳過搜索BeanInfo類,但具體是什麼我暫時也不清楚。 configureIgnoreBeanInfo(environment); //③ Banner printedBanner = printBanner(environment); //④ context = createApplicationContext(); //⑤ exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context); //⑥ prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); //刷新後的處理,是個空實現 afterRefresh(context, applicationArguments); //計時器結束 stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } //發佈started事件 listeners.started(context); //運行器回調,即實現了ApplicationRunner接口或CommandLineRunner接口的bean callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context; }
內部又是調getSpringFactoriesInstances方法,取spring.factories中全部的SpringApplicationRunListener,而後對外暴露SpringApplicationRunListeners。 SpringApplicationRunListeners封裝全部的SpringApplicationRunListener,用於容器啓動間的事件發佈到全部的SpringApplicationRunListener中。app
SpringApplicationRunListener中定義的方法有:框架
值得注意的是,started、running、failed方法是 SpringBoot2.0 才加入的。less
經過Debug,發現默認狀況下加載的listeners有一個,類型爲 EventPublishingRunListener。它在SpringBoot應用啓動的不一樣時間點發布不一樣應用事件類型(ApplicationEvent),若是有哪些事件監聽者(ApplicationListener)對這些事件感興趣,則能夠接收而且處理。SpringApplicationRunListener與ApplicationListener的區別是SpringApplicationRunListener比ApplicationListener更靠前,SpringApplicationRunListener監聽的是SpringApplication相關方法的執行,屬於第一層監聽器,他會發布相應的事件給ApplicationListener。學習
根據不一樣的webApplicationType完成Environment的初始化,通常是使用StandardServletEnvironment實現類,Environment用於描述應用程序當前的運行環境,其抽象了兩個方面的內容:配置文件(profile)和屬性(properties),其實就是對應的配置文件、環境變量、命令行參數裏面的內容。這裏Environment構建完成時發佈了environmentPrepared事件,而且將最新的配置值綁定到了SpringbootApplication中,也就是當前的對象中。好比yml裏面配的spring. main開頭的一些屬性值。
根據Enviroment中配置獲取對應的banners沒有則用默認的SpringbootBanner打印啓動信息,就是啓動應用時候控制檯打印的logo
根據WebApplicationType,反射建立不一樣的ApplicationContext實現(Servlet是AnnotationConfigServletWebServerApplicationContext)。這裏Servlet是AnnotationConfigServletWebServerApplicationContext,在他的父類GenericApplicationContext構造方法中,其中注入了一個DefaultListableBeanFactory,這個BeanFactory很關鍵,實際上AnnotationConfigServletWebServerApplicationContext的BeanFactory能力就是從DefaultListableBeanFactory擴展而來。 另外在這一步中也註冊了ConfigurationClassPostProcessor、DefaultEventListenerFactory、EventListenerMethodProcessor、AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor這些beanDefinition,做爲基礎組件。ConfigurationClassPostProcessor這個組件是最重要的,其餘的暫時沒有深究什麼做用,ConfigurationClassPostProcessor是BeanFactoryPostProcessor,負責在容器刷新時加載掃描配置類註解進行組件解析,註冊BeanDefinition。
建立一系列SpringBootExceptionReporter,建立流程是經過SpringFactoriesLoader獲取到全部實現SpringBootExceptionReporter接口的class,
初始化ApplicationContext,主要完成如下工做:
容器刷新前,整個流程分三個步驟: