西部開源-秦疆老師:基於SpringBoot 2.1.6 的博客教程web
秦老師交流Q羣號: 664386224spring
未受權禁止轉載!編輯不易 , 轉發請註明出處!防君子不防小人,共勉!springboot
咱們以前寫的HelloSpring,究竟是怎麼運行的呢less
咱們來看pom.xml文件spring-boot
進入父項目,這裏纔是真正管理SpringBoot應用裏面全部依賴版本的地方,SpringBoot的版本控制中心;this
之後咱們導入依賴默認是不須要寫版本;可是若是導入的包沒有在依賴中管理着就須要手動配置版本了;url
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
springboot-boot-starter:就是spring-boot的場景啓動器spa
這裏的 spring-boot-starter-web 幫咱們導入了web模塊正常運行所依賴的組件;命令行
SpringBoot將全部的功能場景都抽取出來,作成一個個的starter (啓動器),只須要在項目中引入這些starter便可,全部相關的依賴都會導入進來 , 咱們要用什麼功能就導入什麼樣的場景啓動器便可 ;3d
package com.kuang.springbootdemo02; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; //@SpringBootApplication 來標註一個主程序類 , 說明這是一個Spring Boot應用 @SpringBootApplication public class SpringbootDemo02Application { public static void main(String[] args) { //將Spring應用啓動起來 SpringApplication.run(SpringbootDemo02Application.class, args); } }
一個簡單的啓動類並不簡單!
@SpringBootApplication :SpringBoot應用標註在某個類上說明這個類是SpringBoot的主配置類 , SpringBoot就應該運行這個類的main方法來啓動SpringBoot應用;進入這個註解:
這個註解在Spring中很重要 , 它對應XML配置中的元素。@ComponentScan的功能就是自動掃描並加載符合條件的組件或者bean , 將這個bean定義加載到IOC容器中 ;
SpringBoot的配置類 ;標註在某個類上 , 表示這是一個SpringBoot的配置類;咱們繼續進去這個註解查看
@Configuration:配置類上來標註這個註解 ,配置類-----配置文件;咱們繼續點進去,發現配置類也是容器中的一個組件。@Component
咱們回到 SpringBootApplication 註解中繼續看。
@EnableAutoConfiguration :開啓自動配置功能
之前咱們須要配置的東西,SpringBoot能夠自動幫咱們配置 ; @EnableAutoConfiguration告訴SpringBoot開啓自動配置功能,這樣自動配置才能生效;
咱們點擊去查看
@AutoConfigurationPackage : 自動配置包 , 點進去看到一個 @Import({Registrar.class})
@import :Spring底層註解@import , 給容器中導入一個組件 ,導入的組件由 {Registrar.class} 將主配置類 【即@SpringBootApplication標註的類】的所在包及包下面全部子包裏面的全部組件掃描到Spring容器 ;
而後回來繼續看:@Import({AutoConfigurationImportSelector.class}) :給容器導入組件 ;
AutoConfigurationImportSelector : 導入哪些組件的選擇器 ;
它將全部須要導入的組件以全類名的方式返回 , 這些組件就會被添加到容器中 ;
它會給容器中導入很是多的自動配置類 (xxxAutoConfiguration), 就是給容器中導入這個場景須要的全部組件 , 並配置好這些組件 ;
有了自動配置類 , 免去了咱們手動編寫配置注入功能組件等的工做;
//AutoConfigurationImportSelector中獲取配置文件信息;
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguation.class,classLoader);
這裏的getSpringFactoriesLoaderFactoryClass()方法返回的就是咱們最開始看的啓動自動導入配置文件的註解類;
進入 SpringFactoriesLoader 查看;
找到對應的方法:發現最終會去讀取一個配置文件 : META-INF/Spring.factories 的文件 。
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) { Entry<?, ?> entry = (Entry)var6.next(); String factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { String factoryName = var9[var11]; result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } } }
咱們打開spring.factories的配置文件 , 看到了不少自動配置的文件;這就是自動配置根源所在!
SpringBoot在啓動的時候從類路徑下的META-INF/spring.factories中獲取EnableAutoConfiguration指定的值,將這些值做爲自動配置類導入容器 , 自動配置類就生效 , 幫咱們進行自動配置工做;
之前咱們須要本身配置的東西 , 自動配置類都幫咱們解決了
整個J2EE的總體解決方案和自動配置都在springboot-autoconfigure的jar包中;
咱們找一個打開看看 : WebMvcAutoConfiguration
因此,真正實現是從classpath中搜尋全部的META-INF/spring.factories配置文件 ,並將其中對應的 org.springframework.boot.autoconfigure. 包下的配置項經過反射實例化爲對應標註了 @Configuration的JavaConfig形式的IOC容器配置類 , 而後將這些都彙總成爲一個實例並加載到IOC容器中。
我最初覺得就是運行了一個main方法,沒想到卻開啓了一個服務;
@SpringBootApplication public class SpringbootDemo02Application { public static void main(String[] args) {
//該方法返回一個ConfigurableApplicationContext對象
//參數一:應用入口的類 參數類:命令行參數 SpringApplication.run(SpringbootDemo02Application.class, args); } }
分析該方法主要分兩部分,一部分是SpringApplication的實例化,二是run方法的執行;
1. 推斷應用的類型是普通的項目仍是Web項目
2.查找並加載全部可用初始化器 , 設置到initializers屬性中
3.找出全部的應用程序監聽器,設置到listeners屬性中
4.推斷並設置main方法的定義類,找到運行的主類
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { this.sources = new LinkedHashSet(); this.bannerMode = Mode.CONSOLE; this.logStartupInfo = true; this.addCommandLineProperties = true; this.addConversionService = true; this.headless = true; this.registerShutdownHook = true; this.additionalProfiles = new HashSet(); this.isCustomEnvironment = false; this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass(); }