springboot自動注入源碼解析

基於springboot 2.1.3版本
首先看看springboot啓動類代碼:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

整個流程的入口就是@SpringBootApplication註解:

@Target(ElementType.TYPE)//用在類上
@Retention(RetentionPolicy.RUNTIME)//保留到運行時
@Documented //生成文檔
@Inherited //如果有子類繼承該類,則會繼承類的所有註解
@SpringBootConfiguration //申明是配置,裏面就是@Configuration註解
@EnableAutoConfiguration //開啓自動注入
@ComponentScan(excludeFilters = { //包掃描
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

下面我們主要看看@ComponentScan和@EnableAutoConfiguration註解的源碼

先看@ComponentScan,這個註解的作用就是指定當前應用所要掃描的包

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)//表示可以重複用這個註解,在一個類上可以加兩個@ComponentScan註解,但是屬性值肯定需要不同
public @interface ComponentScan {

下面就是重點,@EnableAutoConfiguration註解
看源碼:

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

裏面的重點註解就兩個
@AutoConfigurationPackage:用於導入並裝配用戶自定義類,即自動掃描包中的類
@Import(AutoConfigurationImportSelector.class):用於導入並裝配框架本身的類

下面我們看@AutoConfigurationPackage:

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

}

static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata,
				BeanDefinitionRegistry registry) {
				//掃描包中加了註解(@Controller,@Service等)的類,加入到IOC容器中
			register(registry, new PackageImport(metadata).getPackageName());
		}

		@Override
		public Set<Object> determineImports(AnnotationMetadata metadata) {
			return Collections.singleton(new PackageImport(metadata));
		}

	}

下面我們看@Import註解,這個註解用於導入指定的類,導入的類一般爲配置類,常見的導入方式有三種:

1. 直接引入配置類
@Import 中指定的類一般爲 Configuration 結尾,且該類上會註解@Configuration,表示當前類爲 配置類。例如用於開啓定時任務的@EnableScheduling 註解的@Import。

2. 根據條件選擇配置類
@Import 中指定的類一般以 ConfigurationSelector 結尾,且該類實現了 ImportSelector接口,表示當前類會根據條件選擇不同的配置類導入。例如,用於開啓緩存功能的@EnableCaching 的@Import。
3. 動態註冊 Bean
@Import 中指定的類一般以 Registrar 結尾,且該類實現了 ImportBeanDefinitionRegistrar
接口,用於表示在代碼運行時若使用了到該配置類,則系統會自動將其導入。例如,用於開啓 AspectJ 自動代理功能的@EnableAspectJAutoProxy 註解的@Import。

接着看最上面:
@SpringBootApplication 的 @EnableAutoConfiguration 的 @Import(AutoConfigurationImportSelector.class),很明顯就是採用的第二種,根據條件選擇配置類

下面看AutoConfigurationImportSelector類的selectImports方法源碼:

	@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
				//下面看這個方法getAutoConfigurationEntry
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
				autoConfigurationMetadata, annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

		protected AutoConfigurationEntry getAutoConfigurationEntry(
			AutoConfigurationMetadata autoConfigurationMetadata,
			AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		//下面看這個方法getCandidateConfigurations
		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) {
			//下面看loadFactoryNames
		List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
				getSpringFactoriesLoaderFactoryClass(), 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;
	}

	public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		//下面看loadSpringFactories
		return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
	}
	
	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 ?
				//重點就在這
				// public static final String 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);
		}
	}

看到最後就知道了,最終加載的是"META-INF/spring.factories"裏面制定的類

在這裏插入圖片描述
spring.factories裏面指定了許多類,這些類在應用啓動的時候就會被加載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章