SpringBoot自动装配原理


前言

SpringBoot基本实现了0配置,因为很多东西官网都提供了默认的配置。但是有时候自己写的时候难免还是需要配置一些东西,所以理解她的自动配置原理挺重要的!


一、从问题出发

提出三个问题,以此来明确接下来阅读的目的:
  1. 怎么启动自动装配
  2. 自动装配的内容
  3. 怎么筛选这些配置内容(提供的肯定是一推配置,怎么筛选掉不用的配置内容)

二、阅读源码来解决问题

问题1.怎么启动自动装配

神奇从这个启动类的@SpringBootApplication开始

@SpringBootApplication
public class DemoApplication {

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

}

点开这个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注解和@EnableAutoConfiguraton注解

对于@SpringBootConfiguration注解,往下继续点开,发现@Component注解。也就是说这个类最后是被当做一个组件被加到spring容器。

下面看@EnableAutoConfiguration注解 其实是开启自动配置的意思
看@Import注解,点开对应的AutoConfigurationImportSelector.class 其中有一个selectImports方法。
首先这个方法是被下面图片的类加载,我就把这个图片中的类当做一个具体加载配置的类来看,不再往上研究,而是往下看加载的内容。

@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
	// ...
}
@Override
	public String[] selectImports(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return NO_IMPORTS;
		}
		AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
		return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
	}

在这里插入图片描述
返回给上层的内容就是AutoConfigurationEntry中的getConfigurations()。
接着往下看,去看configurations对应的是什么?

// return 应该导入的自动配置
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
		if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}
		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);
		configurations = getConfigurationClassFilter().filter(configurations);
		fireAutoConfigurationImportEvents(configurations, exclusions);
		return new AutoConfigurationEntry(configurations, exclusions);
	}
if (!isEnabled(annotationMetadata)) {
			return EMPTY_ENTRY;
		}

其中的isEnabled方法判断的是自动导入配置是否生效,是由于加了@EnableAutoCongruation注解,所以这个自动配置就生效,具体怎么实现的,我没继续往下看。
现在是否启动自动配置问题告一段落,接着看自动配置的内容。

结论:因为用了有@EnableAutoCongruation注解,所以isEnabled()方法判断自动配置生效。

2.配置内容?

从上图继续,这个获取候选配置方法
List configurations = getCandidateConfigurations(annotationMetadata, attributes);
这句话字面意思获得预选配置。所以现在关键是configurations。
直接说结论configurations是spring.factories中配置的组件全类名形成的list

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()
看到这里基本没啥耐心了。。。。
接下来这段的意思就是加载"META-INF/spring.factories"这个下面的所有配置的组件的全限定类名,然后形成一个list返回。

也就是说现在我们自动配置的内容,在META-INF/spring.factories下面。
但是这只是配置了具体的组件类名

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
		String factoryTypeName = factoryType.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, 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 ?
					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 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) {
			throw new IllegalArgumentException("Unable to load factories from location [" +
					FACTORIES_RESOURCE_LOCATION + "]", ex);
		}
	}

那么具体自动配置的内容在哪呢?
我们不妨以springMvc的配置举例。
首先打开spring.properties文件,其中有一个mvc自动配置组件的全限定类名。点开
在这里插入图片描述
在这里插入图片描述
其中可以看到下面还有@Bean注解,@ConditionalOnProperty。
其实这个组件对应着一个pom.xml中的一个启动器,这个启动器需要的环境由一个个bean组成,把这个bean配置好之后放入到ioc容器中就是实现了环境的自动配置。
而bean真是的内容来自于@ConditionalOnProperty对应的一个XXXProperties文件,其中提供了官网提供的约定配置。

**总结一下前俩个问题:

  1. 带有@EnableAutoConfigruation注解的就开启自动配置
  2. 装配的内容来自哪里? 在spring-boot-autoConfiguration报下的META-INF/spring.factories文件**
  3. spring.factories中配置的组件中对应着各种bean,这些bean是一个个具体的类,其中的属性就是需要配置的东西。配置内容在一个个XXXProperties文件中,配置内容和属性对应就通过一个@ConditionalOnProperty来进行关联。

3.剔除不需要的配置

我们打开spring.factories文件下的一个类
发现爆红!
剔除不需要的配置关键在于@ConditionalXXX这个注解
可以看到RabbitTEmplate这个类,所以这个条件没有满足,所以这个配置不生效。
在这里插入图片描述
那么怎么让他生效呢?我们看一个生效的类
在这里插入图片描述

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-**starter**-web</artifactId>
		</dependency>

生效是因为我们在pom.xml文件中导入了web的启动器,那么一些类就被导入,所以这个条件注解生效。

该处使用的url网络请求的数据。


自己配置

上面三个问题基本回答完毕,那么我们该如何自己配置呢? 我们可以写一个application.yaml文件,如果这个对应的启动器被加载,那么我们要配置的内容必定对应一个已经配置好的xxx.properties文件,并且已经有@ConditionalOnProperty对应到具体的实体类中来覆盖固定的值。

举个例子:
这个是web启动器下的一个文件下的。他加了ConditionalOnProperty注解,而且对应着server.forward-headers-strateg,那么一定有一个实体类配置对应。
可以看到自己写的时候有提示!

@Bean
	@ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
	@ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
	public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
		ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
		FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
		registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
		registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
		return registration;
	}

在这里插入图片描述
对应的实体类:
在这里插入图片描述

完结收工!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值