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。

猜你喜欢

转载自blog.csdn.net/seasonLai/article/details/83113229