springboot自动配置

SpringBoot 自动配置原理

1、SpringBoot自动配置版本号
org.springframework.boot:spring-boot-autoconfigure:2.6.5
2、@EnableAutoConfiguration

在@EnableAutoConfiguration注解中,通过@Import注解先引入AutoConfigurationImportSelector类,在springboot启动时,会执行该类下的getAutoConfigurationEntry方法. 此方法是由AutoConfigurationImportSelector类中process方法所调用。

前置知识

通用配置文件加载类,指定的类型和类加载器,从根目录下中所有的META-INF/spring.factories配置文件中读取信息,并实例化对象(创建一个属性未赋值的对象,既只是一个刚new出来的对象)。

SpringFactoriesLoader.loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) 方法

a、getAutoConfigurationEntry 方法(加载配置信息)
    //   传入的参数为启动类
	protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
	    //  判断启动类是否有添加@EnableAutoConfiguration注解,默认
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		
		//  返回该启动类上的@EnableAutoConfiguration注解中的属性,如果启动类上不存在该注解将抛出IllegalArgumentException异常
		AnnotationAttributes attributes = getAttributes(annotationMetadata);
		
		//  返回应该考虑的自动配置类名
		List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
		
		//  移除重复的类名
		configurations = removeDuplicates(configurations);
		
		//  返回任何限制候选配置的排除类
		Set<String> exclusions = getExclusions(annotationMetadata, attributes);
		
		//  检查无效的排除类,如果存在将抛出异常
		checkExcludedClasses(configurations, exclusions);
		
		//  从所有配置类中移除掉排除类
		configurations.removeAll(exclusions);
		
		//  从所有配置类中移除掉不匹配的配置(AutoConfigurationImportFilter的过滤器类)
		configurations = getConfigurationClassFilter().filter(configurations);
		
		//  初始化监听器
		fireAutoConfigurationImportEvents(configurations, exclusions);
		
		//  返回自动配置类
		return new AutoConfigurationEntry(configurations, exclusions);
	}

b、isEnabled() 判断自动配置是否是开启,默认为开启,可通过配置文件设置spring.boot.enableautoconfiguration=false关闭,关闭后该启动类将返回一个空的自动配置对象(AutoConfigurationEntry)
	protected boolean isEnabled(AnnotationMetadata metadata) {
		if (getClass() == AutoConfigurationImportSelector.class) {
		
			return getEnvironment().getProperty(EnableAutoConfiguration.ENABLED_OVERRIDE_PROPERTY, Boolean.class, true);
		}
		return true;
	}
c、getAttributes() (返回该启动类上的@EnableAutoConfiguration注解中的属性)
	protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
		String name = getAnnotationClass().getName();
		AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
		Assert.notNull(attributes, () -> "No auto-configuration attributes found. Is " + metadata.getClassName()
				+ " annotated with " + ClassUtils.getShortName(name) + "?");
		return attributes;
	}

d、getCandidateConfigurations() (返回应该考虑的自动配置类名)
	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
	    //  加载
		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() 方法能够根据接口获取其实现类类名的集合

	public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		ClassLoader classLoaderToUse = classLoader;
		if (classLoaderToUse == null) {
			classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
		}
		String factoryTypeName = factoryType.getName();
		
		//  获取根目录下所有的META-INF/spring.factories配置文件信息
		return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
	}

loadSpringFactories() 获取根目录下所有的META-INF/spring.factories配置文件信息

private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        //  根据类加载器获取该类加载器中类的信息
		Map<String, List<String>> result = cache.get(classLoader);
		if (result != null) {
			return result;
		}

		result = new HashMap<>();
		try {
		
		    //  通过类加载器,去搜索META-INF/spring.factories下的配置信息
		   //   	public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
		   
			Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
			
			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();
					String[] factoryImplementationNames =
							StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
					for (String factoryImplementationName : factoryImplementationNames) {
						result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
								.add(factoryImplementationName.trim());
					}
				}
			}

			// Replace all lists with unmodifiable lists containing unique elements
			result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
					.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
			cache.put(classLoader, result);
		}
		catch (IOException ex) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
		return result;
	}

e、removeDuplicates(移除重复类型)
	protected final <T> List<T> removeDuplicates(List<T> list) {
		return new ArrayList<>(new LinkedHashSet<>(list));
	}

f、getExclusions (返回任何限制候选配置的排除类)
/**
 * 参数说明
 * metadata: 启动类信息
 * attributes:@EnableAutoConfiguration注解的属性信息(exclude,excludeName)
 **/
	protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		Set<String> excluded = new LinkedHashSet<>();
		
		//  添加exclude属性的排除项
		excluded.addAll(asList(attributes, "exclude"));
		
		//  添加excludeName属性的排除项
		excluded.addAll(Arrays.asList(attributes.getStringArray("excludeName")));
		
		//  添加yml等配置文件中spring.autoconfigure.exclude属性的排除项
		excluded.addAll(getExcludeAutoConfigurationsProperty());
		return excluded;
	}
g、checkExcludedClasses (检查无效的排除类,如果存在将抛出异常)
	private void checkExcludedClasses(List<String> configurations, Set<String> exclusions) {
		List<String> invalidExcludes = new ArrayList<>(exclusions.size());
		
		for (String exclusion : exclusions) {
		    // 如果排除类存在当前类加载器中,并且该排除类不在加载的配置类中,表示此类为无效排除类
			if (ClassUtils.isPresent(exclusion, getClass().getClassLoader()) && !configurations.contains(exclusion)) {
				invalidExcludes.add(exclusion);
			}
		}
		
	    //  如果存在无效类,系统将抛出IllegalStateException异常
		if (!invalidExcludes.isEmpty()) {
			handleInvalidExcludes(invalidExcludes);
		}
	}
h、getConfigurationClassFilter(获取过滤器的类)

从spring.factories配置文件中加载到三个过滤器类:

  • org.springframework.boot.autoconfigure.condition.OnBeanCondition
    加载判断“关于Bean有关”的类

  • org.springframework.boot.autoconfigure.condition.OnClassCondition
    加载判断“关于Class对象有关”的类

  • org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
    加载判断“关于Web应用有关”的类

三个过滤器的加载顺序是: OnClassCondition -> OnWebApplicationCondition -> OnBeanCondition
	private ConfigurationClassFilter getConfigurationClassFilter() {
		if (this.configurationClassFilter == null) {
		    
		    // 获取spring.factories配置文件中AutoConfigurationImportFilter类对应的类信息
			List<AutoConfigurationImportFilter> filters = getAutoConfigurationImportFilters();
			
			for (AutoConfigurationImportFilter filter : filters) {
			    //   初始化(给filter对象中的属性赋值)
				invokeAwareMethods(filter);
			}
			this.configurationClassFilter = new ConfigurationClassFilter(this.beanClassLoader, filters);
		}
		return this.configurationClassFilter;
	}

getAutoConfigurationImportFilters() 获取spring.factories配置文件中AutoConfigurationImportFilter类对应的类信息。

	protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
	
	    //  通用工厂加载机制类。指定需获取的类和类加载器,从META-INF/spring.factories获取对应的类信息。
	    (实例化对象,对象中的属性只有默认值)
		return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
	}
i、 filter() 根据过滤器匹配满足条件的类(会从META-INF/spring-autoconfigure-metadata.properties中获取信息)
		List<String> filter(List<String> configurations) {
			long startTime = System.nanoTime();
			String[] candidates = StringUtils.toStringArray(configurations);
			boolean skipped = false;
			for (AutoConfigurationImportFilter filter : this.filters) {
				
				//  判断加载类是否满足条件(是否满足@Conditional的衍生类)
				boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
				
				for (int i = 0; i < match.length; i++) {
					if (!match[i]) {
						candidates[i] = null;
						skipped = true;
					}
				}
			}
			if (!skipped) {
				return configurations;
			}
			List<String> result = new ArrayList<>(candidates.length);
			for (String candidate : candidates) {
				if (candidate != null) {
					result.add(candidate);
				}
			}
			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 result;
		}

	}

i、fireAutoConfigurationImportEvents(初始化监听器)
	private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
	    //  从META-INF/spring.factories配置文件中加载监听器(实例化,只创建了监听器对象,未对属性赋值)
		List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
		
		if (!listeners.isEmpty()) {
		    //  初始化监听器(为监听器的属性赋值)
			AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
			for (AutoConfigurationImportListener listener : listeners) {
				invokeAwareMethods(listener);
				listener.onAutoConfigurationImportEvent(event);
			}
		}
	}


3、自动配置的生效和修改

spring.factories文件中所有自动配置类,都必须在一定的条件下才会作为组件添加到容器中,配置的内容才会生效,这些限制条件在SpringBoot中以@Conditional派生注解形式体现。如下表

注解生效条件
@ConditionalOnJava应用使用指定的Java版本时生效
@ConditionalOnBean容器中存在指定的Bean时生效
@ConditionOnMissingBean容器中不存在指定的Bean时生效
@ConditionalOnExpression满足指定的SpEL表达式时生效
@ConditionalOnClass存在指定的Class类时生效
@ConditionalOnMissingClass不存在指定的Class类时生效
@ConditionalOnSingleCandidate容器中只存在一个指定的 Bean 或这个 Bean 为首选 Bean 时生效
@ConditionalOnProperty系统指定属性存在指定的值时生效
@ConditionalOnResource类路径下存在指定的资源文件时生效
@ConditionalOnWebApplication当前应用是 web 应用时生效
@ConditionalOnNotWebApplication当前应用不是 web 应用生效

总结

自动配置总共分为以下几步骤

  • 调用SpringBootApplication.run方法后,先在根目录下读取spring.factories配置文件,并将这些文件的信息添加到cache缓存中,以key为类加载器,value为自动配置信息存储。(方便之后使用)

  • 然后执行AutoConfigurationImportSelector类中的process方法,读取自动配置信息。

  • 先判断自动配置是否启动,默认是启动的,可通过配置文件配置spring.boot.enableautoconfiguration的启动状态,值为true/false

  • 自动配置为启动状态,既:spring.boot.enableautoconfiguration=true,将获取@EnableAutoConfiguration注解中的属性,分别为exclude和excludeName,表示需要排除的类

  • 加载spring.factories配置文件的信息,如果cache缓存中存在,就从缓存中获取,否则重新加载自动配置类

  • 去除重复的自动配置类

  • 获取所有排除类(有三个地方可配置排除类,@EnableAutoConfiguration注解中的属性的两个属性+配置yml属性spring.autoconfigure.exclude【数组】)

  • 在所有自动配置类中去除排除类

  • 获取自动配置过滤器,执行过滤器将不满足@Conditional衍生注解的类排除掉(先排除OnClassCondition类 -> OnWebApplicationCondition类 -> OnBeanCondition类)

  • 加载自动配置监听器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值