SpringBoot2.2.13自动配置的实现原理

本文详细介绍了SpringBoot2.2.13自动配置的实现原理,包括@SpringBootConfiguration、@EnableAutoConfiguration的用法,以及@AutoConfigurationPackage和@Import在自动配置过程中的作用。通过源码分析,揭示了自动配置如何查找和加载配置类,以及条件注解在过滤配置中的关键角色。
摘要由CSDN通过智能技术生成

SpringBoot2.2.13自动配置的实现原理

​ 上一节中创建的子模块的启动类如下:

/*标注这个注解的类,说明这个类是Springboot项目的主配置类*/
@SpringBootApplication
public class SpringbootAIBeiTestApplication {
	public static void main(String[] args) {
		SpringApplication.run(SpringbootAIBeiTestApplication.class, args);
	}
}

​ 我们运行main方法的时候,Springboot项目会自动帮我们完成项目所依赖的Bean的装配。我们进去@SpringBootApplication 源文件中,如下:

package org.springframework.boot.autoconfigure;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.TypeExcludeFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.annotation.AliasFor;
import org.springframework.data.repository.Repository;
/*下面4个注解是元注解*/
@Target(ElementType.TYPE) /*注解的使用范围,Type表示注解可以描述在:类,接口,注解或者枚举中*/
@Retention(RetentionPolicy.RUNTIME)/*表示注解的生命周期,runtime运行时*/
@Documented/*表示注解可以记录在javadoc中*/
@Inherited/*表示可以被子类继承该注解*/
/*下面3个是Springboot的注解*/
@SpringBootConfiguration/*标明这个类是配置类*/
@EnableAutoConfiguration/*启动自动配置功能*/
/*注解扫描*/
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
	
	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

	@AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};
	
	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

​ 这里分析系@SpringBootApplication注解。

/下面4个注解是元注解/
@Target(ElementType.TYPE) /注解的使用范围,Type表示注解可以描述在:类,接口,注解或者枚举中/
@Retention(RetentionPolicy.RUNTIME)/表示注解的生命周期,runtime运行时/
@Documented/表示注解可以记录在javadoc中/
@Inherited/表示可以被子类继承该注解/

/下面3个是Springboot的注解/
@SpringBootConfiguration/标明这个类是配置类/
@EnableAutoConfiguration/启动自动配置功能/
/注解扫描/
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

1.1 @SpringBootConfiguration

​ 通过上面的分析,我们可以知道,@SpringBootApplication 这个注解等同于: @SpringBootConfiguration@EnableAutoConfiguration@ComponentScan 这三个注解的组合。

​ 通过翻看@SpringBootConfiguration的源码,如下:

import org.springframework.context.annotation.Configuration;
....
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
    
    /*指定别名的注解类型*/
	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

​ 除去三个元注解:@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented,只有一个注解 @Configuration 这个注解是springframework提供的。通过翻阅 @Configuration 源码如下:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";

    boolean proxyBeanMethods() default true;
}

@component我们都比较熟悉,@component是spring中的一个注解,它的作用就是实现bean的注入。分析到这里,我们就已经知道了,@SpringBootConfiguration 这个注解对 @Configuration 进行了包装,主要目的是标明这个类是配置类,在springboot项目中只能有一个@SpringBootConfiguration,所以一旦这个类上里面,标注了这个注解,则说明这个类是系统的主配置类。

1.2 @EnableAutoConfiguration

​ 从字面意思,我们也大概可以猜出这类就是,Springboot中的实现自动配置的核心类。通过查看 @EnableAutoConfiguration类的源码:

import org.springframework.context.annotation.Import;
......

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
/* 自动配置包 */
@AutoConfigurationPackage
/* Spring的底层注解@Import ,给容器中导入一个组件。导入的组件是AutoConfigurationImportSelector*/
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	/**
	 * Environment property that can be used to override when auto-configuration is
	 * enabled.
	 */
	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/*不会被导入spring容器的类*/
	Class<?>[] exclude() default {};

	/*不会被导入spring容器的类名称*/
	String[] excludeName() default {};

}

​ 如果项目中配置了这个类,则告知Springboot开启自动配置功能。@EnableAutoConfiguration 这个注解包含:@AutoConfigurationPackage@Import@Import 这个注解我们比较熟悉,@Import 是Spring中的一个底层注解,实现给容器中导入一个组件。@EnableAutoConfiguration 这个注解就是借助@Import 这个注解来收集符合自动配置条件bean定义,并且加入到Spring中的容器中。Spring中有很多的以Enable开头的注解,其作用和@EnableAutoConfiguration作用类似。

1.2.1@AutoConfigurationPackage

@AutoConfigurationPackage 源码如下:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
/* Spring的底层注解@Import ,给容器中导入一个组件。导入的组件是 AutoConfigurationPackages.Registrar.class*/
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

当我点进入 AutoConfigurationPackages.Registrar.class 中,会发现这是一个静态方法:

	static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {

		@Override
		public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
			/* 将注解标注的元信息传入,获取到相应的包名*/
			register(registry, new PackageImport(metadata).getPackageName());
		}

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

	}

这个方法中,真正起作用的是register这个方法,这个方法的将注解上标注的元信息作为参数传入,并且获取相应的包。这个方法中的第二个参数:new PackageImport(metadata).getPackageName() ,它会先找到标注有@SpringBootApplication 的类,然后获取这个类的包名:
在这里插入图片描述

我们进一步,进入这个注册方法中如下:

public static void register(BeanDefinitionRegistry registry, String... packageNames) {
		// 这里的参数 packageNames 缺省的情况下,就是一个字符串,是使用了注解 @SpringBootApplication 的Spring boot 应用程序入口类所在的包。
		if (registry.containsBeanDefinition(BEAN)) {
			/* 如果该bean已经注册,则将要注册包名称添加到注册中心 */
			BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
			ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues();
			constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames));
		}
		else {
			/*如果bean没有注册,则注册这个bean,参数中提供的包名称会被设置到bean定义中去*/
			GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
			beanDefinition.setBeanClass(BasePackages.class);
			beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
			beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
            // 完成这个bean对象的注册
			registry.registerBeanDefinition(BEAN, beanDefinition);
		}
	}


// BasePackages.class 
static final class BasePackages {

		private final List<String> packages;

		private boolean loggedBasePackageInfo;

		BasePackages(String... names) {
			List<String> packages = new ArrayList<>();
			for (String name : names) {
				if (StringUtils.hasText(name)) {
					packages.add(name);
				}
			}
			this.packages = packages;
		}

		List<String> get() {
			if (!this.loggedBasePackageInfo) {
				if (this.packages.isEmpty()) {
					if (logger.isWarnEnabled()) {
						logger.warn("@EnableAutoConfiguration was declared on a class "
								+ "in the default package. Automatic @Repository and "
								+ "@Entity scanning is not enabled.");
					}
				}
				else {
					if (logger.isDebugEnabled()) {
						String packageNames = StringUtils.collectionToCommaDelimitedString(this.packages);
						logger.debug("@EnableAutoConfiguration was declared on a class in the package '" + packageNames
								+ "'. Automatic @Repository and @Entity scanning is enabled.");
					}
				}
				this.loggedBasePackageInfo = true;
			}
			return this.packages;
		}

	}
  • 源码分析到这里,我们可以知道:AutoConfigurationPackages.Registrar.class 这个类帮我们完成了,将 org.springframework.boot.autoconfigure.AutoConfigurationPackages.BasePackages 这个类注册到注册中心。这个类有一个参数packageNames,这个参数是 @AutoConfigurationPackages 这个注解的类所在的包路径,保存自动配置类以供之后使用。

    // packageNames 
    beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames);
    

1.2.2 @Import

​ 通过分析底层源码可以看到:

/* Spring的底层注解@Import ,给容器中导入一个组件。导入的组件是 AutoConfigurationImportSelector*/
@Import(AutoConfigurationImportSelector.class)

@Import 这个注解上面有一个 参数: AutoConfigurationImportSelector.class。通过这个注解可以实现给容器中导入这个AutoConfigurationImportSelector组件,而这个组件帮Springboot实现将符合条件的@configuration配置全部加载到当前Springboot核心容器中:ApplicationContext。

下面是 AutoConfigurationImportSelector 的类图

在这里插入图片描述

我们分析这个接口,

public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
		ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {.....}

AutoConfigurationImportSelector这个接口实现了:DeferredImportSelector,BeanClassLoaderAware,ResourceLoaderAware,BeanFactoryAware,EnvironmentAware,Ordered 这个6个接口。

当我们进一步往下翻阅源码时,会发现:在AutoConfigurationImportSelector 中有下面这四个变量:

	private ConfigurableListableBeanFactory beanFactory;

	private Environment environment;

	private ClassLoader beanClassLoader;

	private ResourceLoader resourceLoader;

显而易见,上面四个变量与AutoConfigurationImportSelector这个接口实现了的四个 *Aware 接口有关。

当我们查看 ``DeferredImportSelector时会发现,它继承了ImportSelector` ,实现了导入外部依赖能力。

public interface DeferredImportSelector extends ImportSelector {....}

当我们查看其中的一个类 BeanClassLoaderAware ,实现 Aware 接口,表示在某个时机会回调这个类。

public interface BeanClassLoaderAware extends Aware {
	void setBeanClassLoader(ClassLoader classLoader);
}
  • 自动配置逻辑相关的代码在 DeferredImportSelectorGrouping 类中的 getImports 中。在Spring的程序中如何通过DeferredImportSelectorGrouping 这个类调到 AutoConfigurationImportSelector 的呢?

    当我继续翻阅DeferredImportSelectorGrouping 的源码:

    public Iterable<Group.Entry> getImports() {
    			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
    				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
    						deferredImport.getImportSelector());
    			}
    			return this.group.selectImports();
    		}
    
    • 分析源码可以知道,for (DeferredImportSelectorHolder deferredImport : this.deferredImports) 这里对 deferredImports 进行了循环遍历,

    • deferredImports 是一个ArrayList集合,这里面存储了各种 ImportSelector,这个 deferredImports 集合记录就包含 AutoConfigurationImportSelector

    • this.group.process 这个方法处理自动配置的相关逻辑,决定导入哪些配置类,这个方法是核心,自动配置的逻辑都在这部分。groupDeferredImportSelector 的一个内部接口。

    • this.group.selectImports(); 通过上一部的操作处理后,调用这个方法,进行选择需要导入的配置类。

  • 我们在IDEA中通过断点的方式,进一步剖析这部分的源码:

    在这里插入图片描述

    在这里插入图片描述

    进入 AutoConfigurationGroup 接口中,这里贴上process方法的源码

    /**
    		 * 处理自动配置类的过程,过滤掉不符合匹配条件的自动配置类。
    		 * 该方法会调用{@link AutoConfigurationImportSelector}的{@code getAutoConfigurationEntry}方法,
    		 * 以获取自动配置类的信息,并将其存储起来。
    		 *
    		 * @param annotationMetadata 注解元数据,包含当前环境的元数据信息。
    		 * @param deferredImportSelector 延迟导入选择器,用于获取自动配置类的信息。
    		 *                                必须是{@link AutoConfigurationImportSelector}的实现类。
    		 */
    		@Override
    		public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
    			// 断言校验,确保传入的deferredImportSelector是AutoConfigurationImportSelector的实例
    			Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
    					() -> String.format("Only %s implementations are supported, got %s",
    							AutoConfigurationImportSelector.class.getSimpleName(),
    							deferredImportSelector.getClass().getName()));
    			// 调用AutoConfigurationImportSelector的getAutoConfigurationEntry方法得到自动配置类,放入autoConfigurationEntry中
    			AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
    					.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
    			this.autoConfigurationEntries.add(autoConfigurationEntry);
    			// 将自动配置类的名称作为键,注解元数据作为值,存储到entries中
    			for (String importClassName : autoConfigurationEntry.getConfigurations()) {
    				this.entries.putIfAbsent(importClassName, annotationMetadata);
    			}
    		}
    

    继续往下走,进入getAutoConfigurationEntry方法

    /**
    	 * 根据导入的{@link Configuration @Configuration}类的{@link AnnotationMetadata},返回{@link AutoConfigurationEntry}。
    	 * @param autoConfigurationMetadata 自动配置的元数据
    	 * @param annotationMetadata 配置类的注解元数据
    	 * @return 应当被导入的自动配置项
    	 */
    	protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
    															   AnnotationMetadata annotationMetadata) {
    		// 检查是否启用了自动配置,是否有spring.boot.enableautoconfiguration属性,默认返回true
    		if (!isEnabled(annotationMetadata)) {
    			return EMPTY_ENTRY;
    		}
    		// 获取注解属性
    		AnnotationAttributes attributes = getAttributes(annotationMetadata);
    
    		// 获取候选配置,这个是Springboot自动配置的核心,得到spring.factories文件中的所有配置
    		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    		// 移除重复的配置(利用LinkedHashSet移除)
    		configurations = removeDuplicates(configurations);
    
    		// 获取排除的配置,例如:@SpringBootApplication注解的exclude属性
    		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    
    		// 检查排除的类是否在配置中,有些不是配置类,所以会抛出异常
    		checkExcludedClasses(configurations, exclusions);
    
    		// 从配置中移除被排除的配置
    		configurations.removeAll(exclusions);
    		// 过滤配置
    		configurations = filter(configurations, autoConfigurationMetadata);
    		// 触发自动配置导入事件
    		fireAutoConfigurationImportEvents(configurations, exclusions);
    		// 返回自动配置项
    		return new AutoConfigurationEntry(configurations, exclusions);
    	}
    

在这里插入图片描述

继续往下走,进入getCandidateConfigurations方法

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		// 加载自动配置类的候选名单
		// 这个方法需要传入两个参数,第一个参数是加载的类,第二个参数是类加载器
		// 第一个参数返回的是EnableAutoConfiguration注解的类名
		// 第二个参数返回的是当前类的类加载器
		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;
	}

继续往下走,进入loadFactoryNames方法

/**
     * 从指定资源位置加载给定类型的工厂实现的完全限定类名。
     * 使用提供的类加载器来加载资源。如果未提供类加载器,则使用默认的。
     * 
     * @param factoryType 表示工厂的接口或抽象类
     * @param classLoader 用于加载资源的类加载器;可以为null,以使用默认类加载器
     * @return 工厂实现的完全限定类名列表;如果不存在相应工厂类型,则返回空列表
     * @throws IllegalArgumentException 如果加载工厂名称时发生错误
     * @see #loadFactories
     */
    public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName(); // 获取工厂类型的名称
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
        // 加载工厂实现的类名,并返回对应工厂类型的类名列表;如果不存在,则返回空列表
    }

在这里插入图片描述

继续往下走,进入loadSpringFactories方法

/**
 * 加载Spring工厂配置。
 * 该方法会从指定的类加载器中加载名为{@value #FACTORIES_RESOURCE_LOCATION}的资源文件,
 * 并将其解析为工厂类型到实现的映射。
 * 
 * @param classLoader 要从中加载资源的类加载器。如果为null,则使用系统类加载器。
 * @return 一个映射,其中包含工厂类型名称到其实现类名称的列表。
 * @throws IllegalArgumentException 如果无法加载工厂配置资源。
 */
	private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
		// 尝试从缓存中获取结果,如果存在,直接返回缓存中的结果。
		MultiValueMap<String, String> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		try {
			// 获取所有资源URL,无论类加载器是否提供,都会尝试加载。
			Enumeration<URL> urls = (classLoader != null ?
					classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
					ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
			result = new LinkedMultiValueMap<>();
			// 遍历所有资源URL,加载并解析属性文件。
			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				UrlResource resource = new UrlResource(url);
				Properties properties = PropertiesLoaderUtils.loadProperties(resource);
				// 解析属性文件,将工厂类型到实现的映射添加到结果中。
				for (Map.Entry<?, ?> entry : properties.entrySet()) {
					String factoryTypeName = ((String) entry.getKey()).trim();
					for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
						result.add(factoryTypeName, factoryImplementationName.trim());
					}
				}
			}
			// 将结果缓存起来,以供后续使用。
			cache.put(classLoader, result);
			return result;
		}
		catch (IOException ex) {
			// 如果在加载资源时发生IO异常,则抛出非法参数异常。
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

  • 这个方法会查询系统中所有在META-INF路径下的spring.factories文件,然后通过
    Properties properties = PropertiesLoaderUtils.loadProperties(resource);

    这个方法将spring.factories中的数据,解析到内存,通过键值对的方式存储。

继续往往下走,springboot 不会将 spring.factories 解析得到的数据都加载到容器中,会经过一系列的过滤操作。核心分一个过滤方法如下:

// 过滤配置
configurations = filter(configurations, autoConfigurationMetadata);
private List<String> filter(List<String> configurations, AutoConfigurationMetadata autoConfigurationMetadata) {
		long startTime = System.nanoTime();
		// 将获取的spring.factories中配置转换成字符串数组。
		String[] candidates = StringUtils.toStringArray(configurations);
		// 定义一个 skip 数组,是否需要跳过,这个skip的数组与candidates的数组长度保持一致。
		boolean[] skip = new boolean[candidates.length];
		// getAutoConfigurationImportFilters : 这个方法会拿到 OnBeanCondition , OnClassCondition, OnWebApplicationCondition
		// 然后遍历这三个条件类去过滤从spring.factories 中加载的大量配置类。
		boolean skipped = false;
		for (AutoConfigurationImportFilter filter : getAutoConfigurationImportFilters()) {
			// 调用各种aware的方法,将beanClassLoader,beanFactory 等注入到filter中
			// 这里的filter的对象就是:OnBeanCondition || OnClassCondition || OnWebApplicationCondition
			invokeAwareMethods(filter);
			// 通过各种filter来判断candidates
			// 这里主要是想通过candidates的自动配置类,拿到标注的 @ConditionalOnClass ,@ConditionalOnBean, @ConditionalOnWebApplication 上面的注解值是否匹配。
			// 这里需要注意 candidates 数组 与 skip 数组是[一一对应]
			boolean[] match = filter.match(candidates, autoConfigurationMetadata);
			//
			for (int i = 0; i < match.length; i++) {
				if (!match[i]) {
					// 不匹配的将记录在skip数组中,
					skip[i] = true;
					// 将自动配置类置NULL
					candidates[i] = null;
					// 标注skipped为true
					skipped = true;
				}
			}
		}
		// 这里表示,当前经过匹配后,全部匹配,则原样返回。
		if (!skipped) {
			return configurations;
		}
		
		// 构建结果集合
		List<String> result = new ArrayList<>(candidates.length);
		for (int i = 0; i < candidates.length; i++) {
			if (!skip[i]) {
				result.add(candidates[i]);
			}
		}
		if (logger.isTraceEnabled()) {
			int numberFiltered = configurations.size() - result.size();
			logger.trace("Filtered " + numberFiltered + " auto configuration class in "
					+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
		}
		// 返回符合条件的自动配置类
		return new ArrayList<>(result);
	}

Conditional是spring框架中spring-context模块提供的注解,是spring容器创建Bean的条件,如果条件满足,则创建Bean,否则不创建。

  • ConditionalOnBean,只匹配特定的bean在beanfactory时。常用于相关联的bean之间的依赖注册
  • ConditionalOnClass,只匹配特定的bean在类路径上。常用于某个某块被引用时,注册bean,如spring boot自动配置
  • ConditionalOnMissingBean,只匹配特定的bean不在beanfactory时。常用于防止重复注册
  • ConditionalOnMissingClass,只匹配特定的bean不在类路上。常用于自动配置,某个bean不存在,然后才注册另外的bean
  • ConditionalOnWebApplication,只匹配是web applicationContext时。常用于如果是web环境,注册某些和web环境相关联的bean
  • ConditionalOnResource,只匹配某些特定资源存在时
  • ConditionalOnProperty,只匹配某个特定属性存在environment时,默认情况需要存在且为true时。常用于开关注册某些bean,或者策略模式
  • ConditionalOnNotWebApplication,只匹配非web applicationContext时
  • 上面提供部分Conditional相关的类。

导致这里我们基本将Springboot自动配置个核心处理方法 process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector)
讲完了,当我继续翻阅DeferredImportSelectorGrouping 的源码:

public Iterable<Group.Entry> getImports() {
			for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
				this.group.process(deferredImport.getConfigurationClass().getMetadata(),
						deferredImport.getImportSelector());
			}
			return this.group.selectImports();
		}

getImports这个方法最后,还会执行 selectImports()

@Override
		public Iterable<Entry> selectImports() {
			if (this.autoConfigurationEntries.isEmpty()) {
				return Collections.emptyList();
			}
			// 这里得到所有要排除的自动配置类的set集合
			Set<String> allExclusions = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
			// 这里得到经过过滤后所有符合条件的自动配置类的set集合
			Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
					.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
					.collect(Collectors.toCollection(LinkedHashSet::new));
			// 移除需要排除的
			processedConfigurations.removeAll(allExclusions);

			// 对标志有@Order注解的自动配置类进行排序
			return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
					.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
					.collect(Collectors.toList());
		}

1.3 总结

Springboot中的自动配置的原理
1、@AutoConfigurationPackage 指定了自动配置包名称。
2、@Import(AutoConfigurationImportSelector.class) ,Spring的底层注解@Import ,给容器中导入一个组件。导入的组件是 AutoConfigurationImportSelector。
3、从spring.factories配置文件中加载自动配置类。
4、加载的自动配置类中排除掉配置的 excloud的类。
5、AutoConfigurationImportFilter接口根据 Conditional相关的接口过滤。
6、fireAutoConfigurationImportEvents,触发自动配置导入事件。
7、spring将筛选后的类导入IOC容器中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深漂的小小小刘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值