Spring Boot自动装配原理

Spring Boot一大特点就是自动约定配置,很多情况下可以写很少的配置甚至不写配置就可以运行程序,其中的原理是需要我们理解的。我们可以大致猜测一下,以前我们没有使用Spring Boot的时候,比如以前搭建SSM框架时,一切入口是Spring开始,当需要使用MyBatis时,需要在配置各种MyBatis的Bean,然后Spring启动的时候就能加载这些Bean,运行时,应用就能直接使用这些Bean。现在使用Spring Boot了,他自己帮我们将一些Bean加载进Spring IOC容器中,如果需要配置,也会给出默认配置,以保证正常启动和基本功能。我们需要了解的是,Spring Boot是如何判断哪些Bean需要加载,以及这些Bean是如何加载的。

一、Spring Boot2自动配置原理

Spring Boot关于自动配置的功能在
spring-boot-autoconfigure-X
这个jar中,当然还要结合启动类上的注解,才能将整个过程串起来

@SpringBootApplication
public class App{

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

它主要加载了@SpringBootApplication注解主配置类,这个@SpringBootApplication注解主配置类里边最主要的功能就是Spring Boot开启了一个@EnableAutoConfiguration注解的自动配置功能。

@EnableAutoConfiguration
public @interface SpringBootApplication {
    ...
}

2、@EnableAutoConfiguration作用:

它主要利用了一个

EnableAutoConfigurationImportSelector选择器给Spring容器中来导入一些组件。

@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    ...
}

3、那么导入了哪些组件呢?

我们来看EnableAutoConfigurationImportSelector#selectImports(),selectImports()方法的作用就是用来挑选需要导入Spring Container的组件,

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	}
// 读取 spring-boot-autoconfigration.jar的
// META-INF/spring-autoconfigure-metadata.properties文件到java.util.Properties中
// 先筛选一下要加载哪些配置类,再把符合条件配置类的信息封装到AutoConfigurationMetadata
	AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
			.loadMetadata(this.beanClassLoader);
// 读取 META-INF/spring.factories文件,把其信息封装到
// AutoConfigurationEntry里,再将步骤一获取的配置类与此处获取的配置类
// 执行过滤、去重、排序等操作,保存满足条件的配置类
	AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
			autoConfigurationMetadata, annotationMetadata);
// 返回满足自动配置条件的类的字符串数组,后交给@Import注解,将其注入到Spring容器中,
// 至此,完成SpringBoot的自动配置!
	return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

protected AutoConfigurationEntry getAutoConfigurationEntry(
		AutoConfigurationMetadata autoConfigurationMetadata,
		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 = filter(configurations, autoConfigurationMetadata);
	fireAutoConfigurationImportEvents(configurations, exclusions);
	return new AutoConfigurationEntry(configurations, exclusions);
}

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
		AnnotationAttributes attributes) {
// 这里加载了factory,加载的是spring-boot-autoconfigration.jar的
// META-INF/spring.factories文件下配置的类
	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;
}
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
	String factoryClassName = factoryClass.getName();
	return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

加入到容器中之后的作用就是用它们来做自动配置,这就是Springboot自动配置之源,也就是自动配置的开始,只有这些自动配置类进入到容器中以后,接下来这个自动配置类才开始进行启动。

以一个自动配置类HttpEncodingAutoConfiguration(HTTP的编码自动配置)为例子来解释SpringBoot的自动配置之原理:

1). 这个HttpEncodingAutoConfiguration类上面标注了一大堆的注解:

@Configuration
@EnableConfigurationProperties(HttpProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(CharacterEncodingFilter.class)
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {

	private final HttpProperties.Encoding properties;

	public HttpEncodingAutoConfiguration(HttpProperties properties) {
		this.properties = properties.getEncoding();
	}

	@Bean
	@ConditionalOnMissingBean
	public CharacterEncodingFilter characterEncodingFilter() {
		CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
		filter.setEncoding(this.properties.getCharset().name());
		filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
		filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
		return filter;
	}

	@Bean
	public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
		return new LocaleCharsetMappingsCustomizer(this.properties);
	}

	private static class LocaleCharsetMappingsCustomizer implements
			WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {

		private final HttpProperties.Encoding properties;

		LocaleCharsetMappingsCustomizer(HttpProperties.Encoding properties) {
			this.properties = properties;
		}

		@Override
		public void customize(ConfigurableServletWebServerFactory factory) {
			if (this.properties.getMapping() != null) {
				factory.setLocaleCharsetMappings(this.properties.getMapping());
			}
		}

		@Override
		public int getOrder() {
			return 0;
		}

	}

}

点进去HttpProperties这个类,发现这个HttpProperties类上面标注了@ConfigurationProperties注解

@ConfigurationProperties(prefix = "spring.http")
public class HttpProperties {

	/**
	 * Whether logging of (potentially sensitive) request details at DEBUG and TRACE level
	 * is allowed.
	 */
	private boolean logRequestDetails;

	/**
	 * HTTP encoding properties.
	 */
	private final Encoding encoding = new Encoding();

	public boolean isLogRequestDetails() {
		return this.logRequestDetails;
	}

	public void setLogRequestDetails(boolean logRequestDetails) {
		this.logRequestDetails = logRequestDetails;
	}

	public Encoding getEncoding() {
		return this.encoding;
	}

	/**
	 * Configuration properties for http encoding.
	 */
	public static class Encoding {

		public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

		/**
		 * Charset of HTTP requests and responses. Added to the "Content-Type" header if
		 * not set explicitly.
		 */
		private Charset charset = DEFAULT_CHARSET;

		/**
		 * Whether to force the encoding to the configured charset on HTTP requests and
		 * responses.
		 */
		private Boolean force;

		/**
		 * Whether to force the encoding to the configured charset on HTTP requests.
		 * Defaults to true when "force" has not been specified.
		 */
		private Boolean forceRequest;

		/**
		 * Whether to force the encoding to the configured charset on HTTP responses.
		 */
		private Boolean forceResponse;

		/**
		 * Locale in which to encode mapping.
		 */
		private Map<Locale, Charset> mapping;

		public Charset getCharset() {
			return this.charset;
		}

		public void setCharset(Charset charset) {
			this.charset = charset;
		}

		public boolean isForce() {
			return Boolean.TRUE.equals(this.force);
		}

		public void setForce(boolean force) {
			this.force = force;
		}

		public boolean isForceRequest() {
			return Boolean.TRUE.equals(this.forceRequest);
		}

		public void setForceRequest(boolean forceRequest) {
			this.forceRequest = forceRequest;
		}

		public boolean isForceResponse() {
			return Boolean.TRUE.equals(this.forceResponse);
		}

		public void setForceResponse(boolean forceResponse) {
			this.forceResponse = forceResponse;
		}

		public Map<Locale, Charset> getMapping() {
			return this.mapping;
		}

		public void setMapping(Map<Locale, Charset> mapping) {
			this.mapping = mapping;
		}

		public boolean shouldForce(Type type) {
			Boolean force = (type != Type.REQUEST) ? this.forceResponse
					: this.forceRequest;
			if (force == null) {
				force = this.force;
			}
			if (force == null) {
				force = (type == Type.REQUEST);
			}
			return force;
		}

		public enum Type {

			REQUEST, RESPONSE

		}

	}

}

所以说配置文件中该配置什么,我们就按照这个类的Field来,它要配spring.http.encoding这个属性,这个属性里边能配置什么值,就对应HttpEncodingProperties这个类来配置,所有的配置文件中能配置的属性都是在xxx.Properties类中封装着。

所以我们能够配置哪些属性,都是来源于这个功能的properties类

用好SpringBoot只要把握这几点:

  1. SpringBoot启动会加载大量的自动配置类

  2. 所要做的就是我们需要的功能SpringBoot有没有帮我们写好的自动配置类:

  3. 如果有就再来看这个自动配置类中到底配置了哪些组件,Springboot自动配置类里边只要我们要用的组件有,我们就不需要再来配置了,但是如果说没有我们所需要的组件,那么我们就需要自己来写一个配置类来把我们相应的组件配置起来。

  4. 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性,而这些属性我们就可以在配置文件指定这些属性的值

  5. xxxxAutoConfigurartion 自动配置类的作用就是给容器中添加组件
    xxxxProperties 的作用就是封装配置文件中相关属性

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值