1. 程式人生 > >從原始碼看Spring Boot 2.0.1

從原始碼看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的全類名