SpringBoot(4)—自动配置原理

SpringBoot版本:2.1.1.RELEASE

在SpringBoot应用启动时,会加载大量的默认配置,从而简化了我们的开发成本。那么,SpringBoot是如何实现自动配置的呢?

在SpringBoot应用的启动类上,我们可以看到 @SpringBootApplication注解,这个注解是一个组合注解,它不仅能标记这个应用是一个SpringBoot应用,更重要的是它能开启自动配置的功能。

@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 {

}

下面将会详细的分析这个组合注解以及它实现自动配置的过程。

第一个注解:@Inherited

这是Java自带的注解,它的作用是:使用此注解申明出来的自定义注解,在使用此自定义的注解时,如果作用在类上,子类会自动继承此注解。

第二个注解:@SpringBootConfiguration

这是SpringBoot自动的注解,作用就是声明这是SpringBoot的配置类,该注解继承自 @Configuration,所以两者功能也一致,可以将当前类内声明的一个或多个以 @Bean 注解标记的方法的实例纳入到 Spring 容器中,并且实例名就是方法名。

第三个注解:@EnableAutoConfiguration

顾名思义,该注解就是用来启动自动配置功能的注解,也是SpringBoot应用最核心的注解之一。下面将会详细分析该注解


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.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.support.SpringFactoriesLoader;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

	/**
	 * Exclude specific auto-configuration classes such that they will never be applied.
	 * @return the classes to exclude
	 */
	Class<?>[] exclude() default {};

	/**
	 * Exclude specific auto-configuration class names such that they will never be
	 * applied.
	 * @return the class names to exclude
	 * @since 1.3.0
	 */
	String[] excludeName() default {};

}

这上面有两个重要的注解,我们先分析 @Import(AutoConfigurationImportSelector.class)

其中 @Import 注解会把注解中的类注入到当前的IOC容器中,一般用于资源的导入。
AutoConfigurationImportSelector类是导入自动配置相关的资源,而资源的导入我们可以进入该类中详细查看 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;
	}

我们可以看下 **getBeanClassLoader()getSpringFactoriesLoaderFactoryClass()**方法的返回值

protected Class<?> getSpringFactoriesLoaderFactoryClass() {
		return EnableAutoConfiguration.class;
	}
protected ClassLoader getBeanClassLoader() {
		return this.beanClassLoader;
	}

我们在进入 loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()) 方法一探究竟。

在这里插入图片描述

从上图中我们可以看出传递的参数就是 注解EnableAutoConfiguration 和当前的类加载器。然后我们再看 **loadFactoryNames()**方法

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
		String factoryClassName = factoryClass.getName();
		return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
	}

该方法首先获取 EnableAutoConfiguration的Name属性,即String factoryClassName=org.springframework.boot.autoconfigure.EnableAutoConfiguration,然后调用loadSpringFactories()方法加载指定类型的EnableAutoConfiguration 对应的在 META-INF/spring.factories 里的类名的数组,如下图

在这里插入图片描述
这样子,就会把每一个这样的 xxxAutoConfiguration类作为容器中的一个组件,都加入到容器中,用他们来做自动配置,每一个自动配置类里面都内置了大量的默认配置。我们以 HttpEncodingAutoConfiguration自动配置类为例来说明。

@Configuration   //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
@EnableConfigurationProperties(HttpProperties.class)  //启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpProperties绑定起来;并把HttpProperties加入到ioc容器中

@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) //Spring底层@Conditional注解,根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效;    判断当前应用是否是web应用,如果是,当前配置类生效

@ConditionalOnClass(CharacterEncodingFilter.class)  //判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;

@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)  //判断配置文件中是否存在某个配置  spring.http.encoding.enabled;如果不存在,判断也是成立的
//即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
public class HttpEncodingAutoConfiguration {
  
  	//他已经和SpringBoot的配置文件映射了
  	private final HttpEncodingProperties properties;
  
   //只有一个有参构造器的情况下,参数的值就会从容器中拿
  	public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
		this.properties = properties;
	}
  
    @Bean   //给容器中添加一个组件,这个组件的某些值需要从properties中获取
	@ConditionalOnMissingBean(CharacterEncodingFilter.class) //判断容器没有这个组件?
	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;
	}

所有能在配置文件中配置的属性都会封装在XXXproperties 中,配置文件中具体能配置哪些参数,可以参考对应的 XXXproperties属性类

@ConfigurationProperties(prefix = "spring.http")
//从配置文件中获取指定的值和bean的属性进行绑定
public class HttpProperties {

	private boolean logRequestDetails;

	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;
	}

	public static class Encoding {

		public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;


		private Charset charset = DEFAULT_CHARSET;
		}
	}

XXXAutoConfiguration : 自动配置类,给容器中添加组件
XXXProperties:封装配置文件中的相关属性

@EnableAutoConfiguration 注解上还有一个需要值得注意的注解,那就是 @AutoConfigurationPackage

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

此时,需要把AutoConfigurationPackages中的内部类Registrar注册到IOC容器中。


/**
 * Class for storing auto-configuration packages for reference later (e.g. by JPA entity
 * scanner).
 */
public abstract class AutoConfigurationPackages {
		......
}

上面的注释简单来说,就是将使用 @AutoConfigurationPackage 注解的类所在的包(package),注册成一个 Spring IoC 容器中的 Bean ,如果有其它模块需要使用,就可以通过获得该 Bean ,从而获得所在的包。例如说,JPA 模块,需要使用到。

好了 ,@SpringBootApplication 注解上最重要的组合注解 @EnableAutoConfiguration 基本已分析完毕,还有一个 @ComponentScan,这个是Spring自定义的注解,用于扫描指定路径下的Component(如@Component,@Controller,@Service,@Configuration等等)

下一篇文章将会对SpringBoot中的配置文件作详细的介绍和说明!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值