SpringBoot自動配置(二)-- 原理分析

SpringBoot自定義starter可參考:SpringBoot自動配置(一)-- 自定義starter
SpringBoot啓動過程可參考:SpringBoot 啓動流程源碼筆記

SpringBoot啓動過程會創建應用上下文ApplicationContext,類型一般是AnnotationConfigServletWebServerApplicationContext(servlet的web應用),

	public AnnotationConfigServletWebServerApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
	
	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
        this(registry, getOrCreateEnvironment(registry));
    }

	public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
        ...
		AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

構造方法中創建了一個AnnotatedBeanDefinitionReader,而這個類的構造方法最終會調用AnnotationConfigUtils.registerAnnotationConfigProcessors,這就向容器注入了註解處理器(可參考:Spring component-scan源碼分析(二) – @Configuration註解處理)。

接着會把啓動類封裝成AnnotatedGenericBeanDefinition註冊到容器中,因爲啓動類上會用@SpringBootApplication註解裝飾

	@Target({ElementType.TYPE})
	@Retention(RetentionPolicy.RUNTIME)
	@Documented
	@Inherited
	@SpringBootConfiguration
	@EnableAutoConfiguration
	@ComponentScan(
	    excludeFilters = {@Filter(
	    type = FilterType.CUSTOM,
	    classes = {TypeExcludeFilter.class}
	), @Filter(
	    type = FilterType.CUSTOM,
	    classes = {AutoConfigurationExcludeFilter.class}
	)}
	)
	public @interface SpringBootApplication {...}

其中@SpringBootConfiguration註解帶@Configuration註解的,所以註解處理器會把啓動類作爲配置類進行處理

	@Target({ElementType.TYPE})
	@Retention(RetentionPolicy.RUNTIME)
	@Documented
	@Configuration
	public @interface SpringBootConfiguration {
	}

既然當做配置類來處理,那就要處理類上面的註解,可以看到註解有@EnableAutoConfiguration、@ComponentScan。@EnableAutoConfiguration是起着自動配置作用的註解,而@ComponentScan註解指定掃描規則,如果不設置basePackage屬性,那就默認掃描類的所在包以及子包的類。

接下來分析@EnableAutoConfiguration註解如何實現自動配置功能

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

這裏的@Import註解是引入其他配置類,來看看AutoConfigurationImportSelector類的結構
AutoConfigurationImportSelector
可以看到它繼承了DeferredImportSelector接口,而DeferredImportSelector接口又是繼承ImportSelector接口的。Spring處理引入配置的時候,遇到實現了ImportSelector接口的類,會調用接口的selectImports方法來拿到需要引入的類名數組進行解析引入。

	public interface ImportSelector {
    	String[] selectImports(AnnotationMetadata var1);
	}

而如果對於實現了DeferredImportSelector接口類,Spring是會在處理完其他所有配置類都解析完成後,再解析這個類(這便於處理條件註解@ConditionalOnBean、ConditionalOnMissingBean等)。

	public interface DeferredImportSelector extends ImportSelector {
	    @Nullable
	    default Class<? extends DeferredImportSelector.Group> getImportGroup() {
	        return null;
	    }

	    public interface Group {
	        void process(AnnotationMetadata var1, DeferredImportSelector var2);
	
	        Iterable<DeferredImportSelector.Group.Entry> selectImports();

			//內部靜態類,裝有元數據和類名
	        public static class Entry {
	            private final AnnotationMetadata metadata;
	            private final String importClassName;
				...
			}
		}
	}

DeferredImportSelector這個接口在Spring5增加了內部接口Group,Spring5處理DeferredImportSelector的時候會先調用getImportGroup拿到Group類型的類,然後實例化這個類,接着調用process方法,再調用selectImports拿到要引入的配置集合(Entry類型的集合),最後遍歷這個集合逐個解析配置類。

---------------分割線,自動配置分析------------------

看看AutoConfigurationImportSelector類如何實現DeferredImportSelector接口的getImportGroup方法

	public Class<? extends Group> getImportGroup() {
		return AutoConfigurationGroup.class;
	}

直接返回AutoConfigurationGroup類

上面說過,會先調用Group類型的process方法,再調用其selectImports方法,來看AutoConfigurationGroup類對這兩個方法的實現

	public void process(AnnotationMetadata annotationMetadata,
				DeferredImportSelector deferredImportSelector) {
			...省略assert,限制deferredImportSelector的實際類型是AutoConfigurationImportSelector
			//拿到META-INF/spring.factories中的EnableAutoConfiguration,並做排除、過濾處理
			//AutoConfigurationEntry裏有需要引入配置類和排除掉的配置類,最終只要返回需要配置的配置類
			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
					.getAutoConfigurationEntry(getAutoConfigurationMetadata(),
							annotationMetadata);
			//加入緩存,List<AutoConfigurationEntry>類型
			this.autoConfigurationEntries.add(autoConfigurationEntry);
			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
				//加入緩存,Map<String, AnnotationMetadata>類型
				this.entries.putIfAbsent(importClassName, annotationMetadata);
			}
	}

該方法拿到配置文件META-INF/spring.factories中的EnableAutoConfiguration並做排除、過濾處理,然後緩存到成員變量中。

	public Iterable<Entry> selectImports() {
			//根據緩存的成員變量判斷是不是空
			if (this.autoConfigurationEntries.isEmpty()) {
				return Collections.emptyList();
			}
			//拿到所有排除類
			Set<String> allExclusions = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getExclusions)
					.flatMap(Collection::stream).collect(Collectors.toSet());
			//拿到需要配置的類
			Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getConfigurations)
					.flatMap(Collection::stream)
					.collect(Collectors.toCollection(LinkedHashSet::new));
			//這裏移除排除的類(這裏感覺多此一舉啊)
			processedConfigurations.removeAll(allExclusions);
			//對配置類排序(根據註解AutoConfigureOrder、AutoConfigureBefore、AutoConfigureAfter),
			//最後封裝成Entry裝入集合返回
			return sortAutoConfigurations(processedConfigurations,
					getAutoConfigurationMetadata())
							.stream()
							.map((importClassName) -> new Entry(
									this.entries.get(importClassName), importClassName))
							.collect(Collectors.toList());
		}

這個方法會最終會把需要配置的類封裝成Entry,裝入集合最後返回出去,交由Spring解析處理。

總結:

SpringBoot自動配置實現是通過實現Spring提供的DeferredImportSelector接口來引入配置文件中配置的EnableAutoConfiguration,而配置文件是META-INF/spring.factories。

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