Spring Boot 源碼分析(一)
sschrodinger
2019/05/28
Spring boot 簡介
Spring boot 采用約定大于配置的思想對應用程序進行默認配置撕贞,減少了大量的配置時間饭于。
Spring Boot 包含的特性如下:
- 創(chuàng)建可以獨立運行的 Spring 應用
- 直接嵌入 Tomcat 或 Jetty 服務器壹无,不需要部署 WAR 文件
- 提供推薦的基礎 POM 文件來簡化 Apache Maven 配置
- 盡可能的根據項目依賴來自動配置 Spring 框架
- 提供可以直接在生產環(huán)境中使用的功能,如性能指標、應用信息和應用健康檢查
- 沒有代碼生成,也沒有 XML 配置文件
通過 Spring Boot矫渔,創(chuàng)建新的 Spring 應用變得非常容易,而且創(chuàng)建出的 Spring 應用符合通用的最佳實踐摧莽。只需要簡單的幾個步驟就可以創(chuàng)建出一個 Web 應用庙洼。對于 spring boot 的創(chuàng)建,參見 spring 官方啟動器镊辕。
Spring boot 加載
因為 Spring boot 內置了 Tomcat 或 Jetty 服務器油够,不需要直接部署 War 文件,所以 spring boot 的程序起點為一個普通的主函數丑蛤,主函數類如下:
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
看似和普通的程序沒有什么區(qū)別叠聋,其實他最主要的步驟都通過注解 @SpringBootApplication
和方法 SpringApplication.run()
完成了。
其實所有的依賴在這一步就可以完成注入受裹,主要的步驟是 ==spring 讀取所有依賴中 META-INF/spring.factories
文件碌补,該文件指明了哪些依賴可以被自動加載虏束,然后根據 ImportSelector
類選擇加載哪些依賴,使用 ConditionOn
系列注解排除掉不需要的配置文件厦章,最后將剩余的配置文件所代表的 bean 加載到 IoC 容器中==镇匀。
META-INF/spring.factories
文件的讀取
spring 所有的配置文件都分布在 jar 包的 META-INF/spring.factories
中,如下:
每一個 META-INF/spring.factories
為一個鍵值對列表袜啃,截取了部分 spring-boot
jar 包下的 spring.factories
文件汗侵,如下:
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
在程序中,這段列表會被解析為 Map<K, List<V>>
的格式群发,即一個 MultiValueMap
(一個鍵對應多個值的 map)晰韵,鍵和每一個值都是一個類的權限定名。
主函數中熟妓,只有一個方法雪猪,即 SpringApplication.run()
,如下:
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
我們來看一個讀取 META-INF/spring.factories
的例子起愈。
在程序初始化時只恨,即運行 new SpringApplication()
時,就會讀取一次配置文件抬虽,我們來分析是如何讀取配置文件的官觅。
SpringApplication.run(DemoApplication.class, args)
方法是 SpringApplication
類提供的一個靜態(tài)方法,調用的是 run(new Class<?>[] { primarySource }, args)
方法阐污。如下:
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
return new SpringApplication(primarySources).run(args);
}
其中休涤,讀取配置文件的操作就發(fā)生在 SpringApplication
類的實例化過程中。實例化的代碼如下:
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 把初始化的初始化器存到數組中
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 把初始化的監(jiān)聽器加入到數組中
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 獲得主類
this.mainApplicationClass = deduceMainApplicationClass();
}
在 getSpringFactoriesInstances(ApplicationContextInitializer.class))
中 spring 讀取了spring.factories
文件笛辟。
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 直接利用反射創(chuàng)建實例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
這里一定要記住 loadFactoryNames((Class<?> factoryClass, @Nullable ClassLoader classLoader)
這個函數滑绒,該函數是 SpringFactoriesLoader
類提供的一個靜態(tài)方法,用于讀取 factoryClass
的類全限定名這個鍵在 spring-factoies
中的值列表隘膘。
loadSpringFactories
讀取所有的配置文件中的屬性的鍵值對,并把緩存到一個 cache
中杠览,在需要讀取的時候進行讀取弯菊。
在運行完 loadSpringFactories(classLoader)
執(zhí)行語句后,在如上的 spring.factories
實例下踱阿,cache
可能變成如下形式:
cache :
key = classloader.toString()
->
value = {
"org.springframework.boot.env.PropertySourceLoader"
-> ["org.springframework.boot.env.PropertiesPropertySourceLoader", "
org.springframework.boot.env.YamlPropertySourceLoader"],
"org.springframework.boot.SpringApplicationRunListener"
-> ["org.springframework.boot.context.event.EventPublishingRunListener"],
"org.springframework.boot.SpringBootExceptionReporter"
-> ["org.springframework.boot.diagnostics.FailureAnalyzers"],
"org.springframework.context.ApplicationContextInitializer"
-> ["org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer", "org.springframework.boot.context.ContextIdApplicationContextInitializer", "org.springframework.boot.context.config.DelegatingApplicationContextInitializer", "org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer"],
}
// 以屬性文件的 key 作為鍵管钳,value 作為值,注意這個值用一個鏈表表示软舌,因為可能一個鍵對應多個值
具體加載和讀取配置文件如下所示:
解析配置文才漆,如下:
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
} try {
Enumeration<URL> urls = (classLoader != null ?
// FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
// output
// like:
// org.springframework.boot.autoconfigure.EnableAutoConfiguration -> {LinkedList@1446} size = 118
// |
// |- org.springframework.boot.autoconfigure.EnableAutoConfiguration
}
以指定的 factoryClass
的類全限定名為鍵,嘗試從緩存中取出值佛点,對于 ApplicationContextInitializer.class
這個 factoryClass
來說醇滥,會取出所有鍵為 org.springframework.context.ApplicationContextInitializer
的值黎比,對于以上的 spring.factories
,就是取出了 `
"org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer"
"org.springframework.boot.context.ContextIdApplicationContextInitializer"
"org.springframework.boot.context.config.DelegatingApplicationContextInitializer"
"org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer"
總結
- ==
SpringFactoriesLoader
類的靜態(tài)方法實現(xiàn)了依賴文件的讀取鸳玩,讀取所有的配置類的全限定名名稱==- ==整個 SpringApplication 在初始化時完成了最基本配置的讀取和實例化==
- ==我們需要記住這個時候的
cache
阅虫,這個cache
在上下文加載中非常有用==
文件上下文的加載
上一節(jié)展示了如何將配置文件所在的類加載到內存中,那么 spring boot 的自動配置如何辦到不跟?
如颓帝,在 spring mvc 中,我們需要手工配置如視圖解析器等基本配置窝革,需要如下的代碼配置:
@Bean
public ViewResolver viewResolver() {
return new ThymeleafViewResolver();
}
既然 spring boot 可以自動配置购城,那么在 spring boot 中,則一定有類似的語句加載默認的視圖解析器等虐译,我們來看看怎么加載的瘪板。
注解簡介
首先介紹幾個注解
@SpringBootApplication
@SpringBootApplication
是一個復合注解,如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 注解一
@EnableAutoConfiguration // 注解二
// 沒有配置 basepackages菱蔬,默認代表當前包及其子包
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM,
classes = AutoConfigurationExcludeFilter.class) }) // 注解三
public @interface SpringBootApplication {
// ...
}
注解一代表配置注解篷帅,如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {}
有了這個注解,我們就可以把由 @SpringBootApplication
注解修飾的類作為配置類來使用拴泌,即在類 DemoApplication
中由 @Bean
注解修飾的代碼都會被注入到 IoC 容器中由 Spring 統(tǒng)一管理魏身。
注解三會自動掃描該類所在包,將各種組件注入到 IoC 容器中讓其管理蚪腐,所以我們也可以類 DemoApplication
上增加諸如 @Controller
的注解箭昵,將其作為一個 Controller。
@EnableAutoConfiguration
我們注意到 SpringBootApplication
注解同時由 @EnableAutoConfiguration
注解修飾回季,我們繼續(xù)看 @EnableAutoConfiguration
家制。
@EnableAutoConfiguration
的定義如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 注解一
@Import(AutoConfigurationImportSelector.class) // 注解二
public @interface EnableAutoConfiguration {
// ...
}
該注解的核心是注解二,用于導入自動配置類泡一。
note
- 在 Spring 中颤殴,由 Enable 開頭的注解的理念和做事方式其實一脈相承,簡單概括一下就是鼻忠,借助@Import的支持涵但,收集和注冊特定場景相關的 bean 定義。
@Import
@Import
注解可以導入三種類:
-
@Configuration
注解修飾的類 -
ImportSelector
或ImportBeanDefinitionRegistrar
的實現(xiàn)類 - 普通的組件類帖蔓,即由
@Component
注解修飾的類矮瘟。
note
- 在 spring boot 中,自動配置的加載使用的是
AutoConfigurationImportSelector
這個類塑娇。
ImportSelector
ImportSelector
接口定義如下:
public interface ImportSelector {
// 返回哪些類需要被創(chuàng)建
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
該接口的主要作用是根據給定的選擇條件(通常是一個或多個注解屬性)導入哪個 @Configuration
類澈侠。
note
- 如果該接口的子類實現(xiàn)了如下四個接口,會先執(zhí)行如下四個接口的函數:
- EnvironmentAware
- BeanFactoryAware
- BeanClassLoaderAware
- ResourceLoaderAware
- 如果希望在所有的
@Configuration
類都導入后再導入該類埋酬,則使用其子接口DeferredImportSelector
filter
在 spring 中哨啃,有過濾器注解烧栋,如下:
@ConditionalOnBean // 在所有的 Bean 都加載好之后才能夠加載
@ConditionalOnMissingBean // 在沒有該 bean 的時候才能夠加載
@ConditionalOnSingleCandidate // 僅當指定類的Bean已包含在BeanFactory中并且可以確定單個候選項時才匹配的條件
@ConditionalOnClass // 所有的 class 都加載之后才能夠加載
@ConditionalOnMissingClass // 不存在 class 的時候才能夠加載
@ConditionalOnWebApplication // 在 web 環(huán)境下才能夠加載
@ConditionalOnNotWebApplication // 不在 web 環(huán)境下才能夠加載
這些注解使用 AutoConfigurationImportFilter
接口處理,排除掉不需要的對象棘催。在 spring 的實現(xiàn)中劲弦,分別是 OnBeanCondition
、OnClassCondition
和 OnWebApplicationCondition
醇坝。
加載過程
介紹完這些注解邑跪,我們回到加載過程。
SpringApplication
的實例方法 run
方法呼猪,在 run
方法中画畅,會創(chuàng)建一個新的上下文實例 context
,并調用 context
的 refresh
方法宋距,在 refresh
方法中轴踱,會自動配置我們需要的默認上下文。
下面是我們非常熟悉的容器初始化的步驟:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
// ...
}
}
invokeBeanFactoryPostProcessors
會解析 @Import
注解谚赎,并根據 @Import
的屬性進行下一步操作淫僻。
invokeBeanFactoryPostProcessors
的主要操作如下:
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// ...
}
下面是 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
的方法代碼,部分代碼如下:
public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
// Invoke BeanDefinitionRegistryPostProcessors first, if any.
Set<String> processedBeans = new HashSet<>();
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
// 記錄是否是定義類的 Processor 或者普通的 Processor
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
// Separate between BeanDefinitionRegistryPostProcessors that implement
// PriorityOrdered, Ordered, and the rest.
List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
// ...
// 應用 Bean 定義類的后置處理器
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
// ...
}
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
invokeBeanDefinitionRegistryPostProcessors
函數對每一個定義類的后置處理器分別進行應用壶唤, @Configure
的解析就在這個函數中雳灵。如下:
// 從注冊表中的配置類派生更多的bean定義
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
// ...
this.registriesPostProcessed.add(registryId);
// Build and validate a configuration model based on the registry of Configuration classes.
processConfigBeanDefinitions(registry);
}
進入最關鍵的類 ConfigurationClassPostProcessor
,這個類用戶來注冊所有的 @Configure
和 @Bean
闸盔。他的 processConfigBeanDefinitions
函數如下:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();
// 記錄所有候選的未加載的配置
// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
return;
}
// 按照 Ordered 對配置進行排序
// 加載自定義 bean 名命策略
if (this.environment == null) {
this.environment = new StandardEnvironment();
}
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
// 解譯候選集
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
this.reader.loadBeanDefinitions(configClasses);
alreadyParsed.addAll(configClasses);
// ...
} while (!candidates.isEmpty());
// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
}
if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
// Clear cache in externally provided MetadataReaderFactory; this is a no-op
// for a shared cache since it'll be cleared by the ApplicationContext.
((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
}
}
在解釋候選集 parser.parse(candidates)
中悯辙,會調用 sourceClass = doProcessConfigurationClass(configClass, sourceClass)
方法依次解析注解,得到所有的候選集迎吵。
note
doProcessConfigurationClass
順次解析@PropertySource
躲撰、@ComponentScan
、@Import
击费、@ImportResource
拢蛋、@Bean
、父類
注意 process
中的 this.deferredImportSelectorHandler.process()
方法:
public void parse(Set<BeanDefinitionHolder> configCandidates) {
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
this.deferredImportSelectorHandler.process();
}
解析完成之后蔫巩,會找到所有以 @PropertySource
瓤狐、@ComponentScan
、@Import
批幌、@ImportResource
、@Bean
注解的類及其對象嗓节,如果有 DeferredImportSelector
荧缘,會將其加入到 deferredImportSelectorHandler
中,并調用 this.deferredImportSelectorHandler.process()
對這些 DeferredImportSelector
進行處理拦宣。
AutoConfigurationImportSelector
實際上截粗,在 spring boot 中信姓,容器初始化的時候,主要就是對 AutoConfigurationImportSelector
進行處理绸罗。
Spring 會將 AutoConfigurationImportSelector
封裝成一個 AutoConfigurationGroup
意推,用于處理。最終會調用 AutoConfigurationGroup
的 process
方法珊蟀。
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
// 主要通過該函數找到所有需要自動配置的類
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
return configurations;
}
如上菊值,我們可以看到 process
最終調用了我們非常熟悉的函數 SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
,該方法以 EnableAutoConfiguration
類為鍵(org.springframework.boot.autoconfigure.EnableAutoConfiguration
)育灸,取得所有的值腻窒。
在該函數中,還會調用 configurations = filter(configurations, autoConfigurationMetadata)
方法磅崭,將不需要的候選集全部排除儿子。(該方法內部使用 AutoConfigurationImportFilter
的實現(xiàn)類排除)。
我們看一個常見的 configuration
砸喻,即 org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
柔逼,這個類中有大量的 @Bean
注解的方法,用來產生 bean割岛,如下:
@Bean
@Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
adapter.setIgnoreDefaultModelOnRedirect(
this.mvcProperties == null || this.mvcProperties.isIgnoreDefaultModelOnRedirect());
return adapter;
}
==spring 通過讀取所有所需要的 AutoConfiguration
愉适,就可以加載默認的上下文容器,實現(xiàn)自動注入蜂桶。==