Spring Boot 源碼分析(一)

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 中,如下:

image.png

每一個 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 注解可以導入三種類:

  1. @Configuration 注解修飾的類
  2. ImportSelectorImportBeanDefinitionRegistrar 的實現(xiàn)類
  3. 普通的組件類帖蔓,即由 @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)中劲弦,分別是 OnBeanConditionOnClassConditionOnWebApplicationCondition醇坝。

加載過程

介紹完這些注解邑跪,我們回到加載過程。

SpringApplication 的實例方法 run 方法呼猪,在 run 方法中画畅,會創(chuàng)建一個新的上下文實例 context,并調用 contextrefresh 方法宋距,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意推,用于處理。最終會調用 AutoConfigurationGroupprocess 方法珊蟀。

@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)自動注入蜂桶。==

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末儡毕,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子扑媚,更是在濱河造成了極大的恐慌腰湾,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疆股,死亡現(xiàn)場離奇詭異费坊,居然都是意外死亡,警方通過查閱死者的電腦和手機旬痹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門附井,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人两残,你說我怎么就攤上這事永毅。” “怎么了人弓?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵沼死,是天一觀的道長。 經常有香客問我崔赌,道長意蛀,這世上最難降的妖魔是什么耸别? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮县钥,結果婚禮上秀姐,老公的妹妹穿的比我還像新娘。我一直安慰自己若贮,他們只是感情好省有,可當我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著兜看,像睡著了一般锥咸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上细移,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天搏予,我揣著相機與錄音,去河邊找鬼弧轧。 笑死雪侥,一個胖子當著我的面吹牛,可吹牛的內容都是我干的精绎。 我是一名探鬼主播速缨,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼代乃!你這毒婦竟也來了旬牲?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤搁吓,失蹤者是張志新(化名)和其女友劉穎原茅,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體堕仔,經...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡擂橘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了摩骨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片通贞。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖恼五,靈堂內的尸體忽然破棺而出昌罩,到底是詐尸還是另有隱情,我是刑警寧澤灾馒,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布峡迷,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏绘搞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一傅物、第九天 我趴在偏房一處隱蔽的房頂上張望夯辖。 院中可真熱鬧,春花似錦董饰、人聲如沸蒿褂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽啄栓。三九已至,卻和暖如春也祠,著一層夾襖步出監(jiān)牢的瞬間昙楚,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工诈嘿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留堪旧,地道東北人。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓奖亚,卻偏偏與公主長得像淳梦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子昔字,可洞房花燭夜當晚...
    茶點故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內容