1. 程式人生 > >SpringBoot原始碼---SpringBoot中的SPI實現方式

SpringBoot原始碼---SpringBoot中的SPI實現方式

    上一篇文章中提到SpringBoot中實現自動配置時,用到了SPI機制。不知道會不會有有心人去看看我推薦的那篇博文。本篇文章將從程式碼的層次深入解讀Springboot的SPI機制。

   首先,是一個很重要的註解@EnableAutoConfiguration,它的原始碼如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	Class<?>[] exclude() default {};

	String[] excludeName() default {};

}

  @EnableAutoConfiguration註解就是SpringBoot實現自動配置的關鍵了。因為註解在底層會被翻譯為介面,繼承註解本質上等同於繼承介面,所以@SpringBootApplication註解繼承了@EnableAutoConfiguration註解後,就有了@EnableAutoConfiguration註解的能力。

      我們再來說說@Import註解,上篇文章提到了它的作用機制。這篇文章中又要用到它的特性了。我們來看看EnableAutoConfigurationImportSelector類的部分原始碼:

public class EnableAutoConfigurationImportSelector implements DeferredImportSelector,
		BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware {

	private ConfigurableListableBeanFactory beanFactory;

	private Environment environment;

	private ClassLoader beanClassLoader;

	private ResourceLoader resourceLoader;

	@Override
	public String[] selectImports(AnnotationMetadata metadata) {
		try {
			AnnotationAttributes attributes = getAttributes(metadata);
			List<String> configurations = getCandidateConfigurations(metadata,
					attributes);
			configurations = removeDuplicates(configurations);
			Set<String> exclusions = getExclusions(metadata, attributes);
			configurations.removeAll(exclusions);
			configurations = sort(configurations);
			recordWithConditionEvaluationReport(configurations, exclusions);
			return configurations.toArray(new String[configurations.size()]);
		}
		catch (IOException ex) {
			throw new IllegalStateException(ex);
		}
	}

}

  這是精簡了很大篇幅程式碼後的部分原始碼(其他部分多數都是被此方法呼叫的方法)。我們不知道它是幹嘛的,但是可以知道是誰呼叫了它,我們來看看呼叫關係:

已經很說明問題了吧?在Spring容器啟動過程中,會通過invokeBeanFactoryPostProcessors()方法執行很多BeanFactoryPostProcessors()方法來完成BeanFactory的初始化。在這個過程中,EnableAutoConfigurationImportSelector類的selectImports()方法也被呼叫了。

接下來,我們來看看selectImports()方法都做了些什麼?EnableAutoConfigurationImportSelector類匯入了一個這樣的包:

import org.springframework.core.io.support.SpringFactoriesLoader;

我們看看它的原始碼(精簡):

public abstract class SpringFactoriesLoader {

	private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);

	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";


	public static <T> List<T> loadFactories(Class<T> factoryClass, ClassLoader classLoader) {
	
	}

	public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
		
	}

}

 這個類我們有利於我們繼續往下找線索的程式碼是這一行:

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

有了這一行程式碼,我們大致就可以猜出來這個SpringFactoriesLoader類大概是幹嘛的了吧?它的兩個核心方法一個是用來尋找spring.factories檔案中的Factory名稱的,一個是用來尋找類的。我們再來看看EnableAutoConfigurationImportSelector類中是怎樣使用SpringFactoriesLoader類的,以一段程式碼如下:

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
			AnnotationAttributes attributes) {
   return SpringFactoriesLoader.loadFactoryNames(
		getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
}

我們現在再來看看selectImports()方法:

public String[] selectImports(AnnotationMetadata metadata) {
	try {
	    AnnotationAttributes attributes = getAttributes(metadata);
            //從spring.factories檔案中找出所有配置好的factory的名稱
        List<String> configurations = getCandidateConfigurations(metadata,
					attributes);
           //去重
	    configurations = removeDuplicates(configurations);
           //分析所有需要排除掉的factory類(EnableAutoConfiguration註解中配置的)
            Set<String> exclusions = getExclusions(metadata, attributes);
           //移除所有需要過濾放入factory類
	    configurations.removeAll(exclusions);
           //排序
	    configurations = sort(configurations);
           //記錄
	    recordWithConditionEvaluationReport(configurations, exclusions);
           //陣列形式返回
	    return configurations.toArray(new String[configurations.size()]);
	}
	catch (IOException ex) {
		throw new IllegalStateException(ex);
	}
}