從原始碼看Spring Boot 2.0.1
Spring Boot 命名配置很少,卻可以做到和其他配置複雜的框架相同的功能工作,從原始碼來看是怎麼做到的。
我這裡使用的Spring Boot版本是 2.0.1.RELEASE
Spring Boot最重要的註解: @SpringBootApplication
開啟它:
其中的幾個註解:
@SpringBootConfiguration 標註這個類是一個配置類,類似Spring的@Configuration
@EnableAutoConfiguration 開啟自動配置
@ComponentScan 開啟元件掃描
開啟@SpringBootConfiguration:
可以看作是springboot將spring的Configuration註解進行一個包裝
@EnableAutoConfiguration
@AutoConfigurationPackage 自動掃描包的註解
@Import({AutoConfigurationImportSelector.class}) 引入元件
這個@AutoConfigurationPackage註解就是掃描跟主配置類同級目錄以及子目錄下的包,這也是什麼Spring Boot其他的包必須在主配置類同級或者子目錄以下的原因。
這裡最關鍵的就是@Import註解向容器裡匯入了什麼元件,匯入的AutoConfigurationImportSelector.class,開啟這個類,注意:在Spring Boot1.5版本中這裡匯入的類是:EnableAutoConfigurationImportSelector,而EnableAutoConfigurationImportSelector是繼承自AutoConfigurationImportSelector的。
裡面有一個 selectImports() 方法很重要。
public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return NO_IMPORTS; } else { AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader); AnnotationAttributes attributes = this.getAttributes(annotationMetadata); List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes); configurations = this.removeDuplicates(configurations); Set<String> exclusions = this.getExclusions(annotationMetadata, attributes); this.checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = this.filter(configurations, autoConfigurationMetadata); this.fireAutoConfigurationImportEvents(configurations, exclusions); return StringUtils.toStringArray(configurations); } }
getCandidateConfigurations() 方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.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; }
SpringFactoryLoader去載入一些元件的名字,看載入那些元件的名字,繼續點開loadFactoryNames方法
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); }
loadSpringFactories方法:
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(); List<String> factoryClassNames = Arrays.asList(StringUtils.commaDelimitedListToStringArray((String)entry.getValue())); result.addAll((String)entry.getKey(), factoryClassNames); } } cache.put(classLoader, result); return result; } catch (IOException var9) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var9); } } }
標黃部分將url轉何曾properties,通過傳入的鍵獲取值,在將值切割成一個個字串,轉成Array存到result裡。
META-INF/spring.factories:
找到其中的
# Auto Configure
可以看到:
上圖裡面這麼多的xxxAutoConfiguration就是我們的這麼久得出的結果,最終就是載入這麼多的類的全路徑,然後springboot內部就是例項化這些類並載入到容器裡面,完成springboot應用啟動時的自動配置。
通過斷點我們可以看到:
總結一下流程:
Spring Boot啟動 >
@SpringBootApplication >
@EnableAutoConfiguration >
@AutoConfigurationPackage + @Import({AutoConfigurationImportSelector.class}) >
public String[] selectImports() {List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);} >
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(); >
classLoader.getResources("META-INF/spring.factories") >
org/springframework/boot/spring-boot-autoconfigure/2.0.1.RELEASE/spring-boot-autoconfigure-2.0.1.RELEASE.jar!/META-INF/spring.factories >
spring.factories:各種xxxxAutoConfiguration的全類名