深入解析SpringBoot自动配置原理

 更新时间:2023年11月02日 09:29:50   作者:桐花思雨  
这篇文章主要介绍了深入解析SpringBoot自动配置原理,SpringBoot 的一大好处就是:大大简化了 Spring 和其他框架的整合配置,为了简化配置文件使开发者更专注于业务编码,可以使用 SpringBoot 来进行 Web 开发,需要的朋友可以参考下

前言

SpringBoot 的一大好处就是:大大简化了 Spring 和其他框架的整合配置。传统的 SSM 套装虽然很大程度地简化了 Web 开发,但是其的配置文件却较为繁琐,为了简化配置文件使开发者更专注于业务编码,可以使用 SpringBoot 来进行 Web 开发,其精简的配置和庞大繁茂的生态圈绝对令人惊叹

SpringBoot 之所以可以达到如此精简的配置,主要原因就是 SpringBoot自动配置

自动配置原理

本文的 SpringBoot 的版本为 2.0.6 RELEASE

SpringBoot 应用的启动类

SpringBoot 应用是从启动类的 main 方法中启动,加载 SpringBoot 配置类

@SpringBootApplication
public class Application {

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

@SpringBootApplication 注解

由于 SpringBoot 应用是从启动类的 main 方法中启动的,我们就有必要查看 @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 {
	// 省略 ......
}

可以看到 @SpringBootApplication 这个注解是一个复合注解,它有三个核心注解,下面我们依次查看它们

  • @SpringBootConfiguration
  • @EnableAutoConfiguration
  • @ComponentScan

@SpringBootConfiguration 注解

表明它是一个配置类

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {

}

@ComponentScan 注解

它负责包的扫描,并排除一些特定的类型(不允许注册进 Spring 容器)

@ComponentScan(excludeFilters = {
		@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

@EnableAutoConfiguration 注解

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

可以看到 @EnableAutoConfiguration 注解是个复合注解,有两个元注解,我们依次查看它们

  • @AutoConfigurationPackage
  • @Import(AutoConfigurationImportSelector.class)

@AutoConfigurationPackage

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

}

查看 AutoConfigurationPackages.Registrar

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

它其实是注册了一个 bean 的定义,返回了当前主启动类包的同级以及子级的包组件

@Import(AutoConfigurationImportSelector.class)

在 AutoConfigurationImportSelector 类中查看 selectImports() 方法

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
	if (!isEnabled(annotationMetadata)) {
		return NO_IMPORTS;
	}
	
	AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
				.loadMetadata(this.beanClassLoader);
				
	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 StringUtils.toStringArray(configurations);
}

再查看 getCandidateConfigurations(annotationMetadata, attributes) 方法

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(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()) 方法

public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {

	String factoryClassName = factoryClass.getName();
	return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}

loadSpringFactories 方法调用如下

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()) {
				List<String> factoryClassNames = Arrays.asList(
						StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
				result.addAll((String) entry.getKey(), factoryClassNames);
			}
		}
		cache.put(classLoader, result);
		return result;
	}
	catch (IOException ex) {
		throw new IllegalArgumentException("Unable to load factories from location [" +
				FACTORIES_RESOURCE_LOCATION + "]", ex);
	}
}

该方法的作用:扫描类路径下所有具有 META-INF/spring.factories 的 jar 包,将扫描到的相应类组件注册进 Spring 容器中,不过这些扫描到的类组件注册进 Spring 容器中需要生效也是有条件的,使用条件注解 @Conditional

public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

在这里插入图片描述

META-INF/spring.factories 的部分内容如下

......

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\
org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\
org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\
org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\

......

这些自动配置类都是以 AutoConfiguration 结尾来命名的,它实际上就是一个 JavaConfig 形式的 Spring 容器配置类

在此我们以 RedisAutoConfiguration 类为例,举例说明

@Configuration
@ConditionalOnClass(RedisOperations.class)// 当给定的类名在类路径上存在时,则实例化当前 bean
@EnableConfigurationProperties(RedisProperties.class)// 将 RedisProperties 这个类注册进 spring 容器中
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	@Bean
	@ConditionalOnMissingBean
	public StringRedisTemplate stringRedisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}
}

@Conditional 条件注解

  • @ConditionalOnBean:当给定的在 bean 存在时,则实例化当前 bean
  • @ConditionalOnExpression:当表达式为 true 的时候,才会实例化一个 bean
  • @ConditionalOnMissingBean:当给定的在 bean 不存在时,则实例化当前 bean
  • @ConditionalOnMissingClass:当给定的类名在类路径上不存在时,则实例化当前 bean
  • @ConditionalOnNotWebApplication:不是 web 应用
  • @ConditionalOnClass:当给定的类名在类路径上存在时,则实例化当前 bean

RedisProperties 类

该类负责读取配置文件中具体配置,如 spring.redis.host 和 spring.redis.port

@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {

	private int database = 0;

	private String url;

	private String host = "localhost";

	private String password;

	private int port = 6379;

	private boolean ssl;

	private Duration timeout;

	private Sentinel sentinel;

	private Cluster cluster;

	private final Jedis jedis = new Jedis();

	private final Lettuce lettuce = new Lettuce();

	......
}	

当我们在使用 maven 引入了相关的 jar 包的时候,在类路径下也就具有了 META-INF/spring.factories 这样的文件,同时也就满足了上面的某些条件注解。这时,maven 引入了相关的 jar 包的类组件也就成功的注册进 Spring 容器中了

自动配置总结

@SpringBootApplication 它有三个核心注解:@SpringBootConfiguration,@EnableAutoConfiguration(重点关注),@ComponentScan

@EnableAutoConfiguration:核心作用是扫描类路径下所有具有 META-INF/spring.factories 的类组件(配置类),这些类组件都是以 AutoConfiguration 结尾来命名的,他能读取在配置文件中配置的属性信息,如 server.port,redis.port 等

然后将上述扫描到的相应类组件注册进 Spring 容器中;不过这些扫描到的类组件注册进 Spring 容器中需要生效也是有条件的,此时使用了条件注解 @ConditionOnxxx

当我们在使用 maven 或 gradle 引入了相关的 jar 包的时候,在类路径下也就具有了 META-INF/spring.factories 这样的文件;同时也就满足了某些 @ConditionOnxxx 的条件注解。这时,maven 或 gradle 引入了相关的 jar 包的类组件也就成功的注册进 Spring 容器中了

到此这篇关于深入解析SpringBoot自动配置原理的文章就介绍到这了,更多相关SpringBoot自动配置原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • IDEA 设置 SpringBoot logback 彩色日志的解决方法 附配置文件

    IDEA 设置 SpringBoot logback 彩色日志的解决方法 附配置文件

    这篇文章主要介绍了IDEA 设置 SpringBoot logback 彩色日志(附配置文件)的操作方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-12-12
  • Java简易抽奖系统小项目

    Java简易抽奖系统小项目

    这篇文章主要为大家详细介绍了Java简易抽奖系统小项目,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-01-01
  • 快速了解Spring Boot

    快速了解Spring Boot

    这篇文章主要介绍了快速了解Spring Boot,介绍了其环境准备,URL中的变量以及模板渲染等内容,具有一定参考价值,需要的朋友可以了解下。
    2017-11-11
  • 详解Java中IO字节流基本操作(复制文件)并测试性能

    详解Java中IO字节流基本操作(复制文件)并测试性能

    这篇文章主要介绍了Java中IO字节流基本操作(复制文件)并测试性能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • Java Map集合详解与演示

    Java Map集合详解与演示

    Map用于保存具有映射关系的数据,Map集合里保存着两组值,一组用于保存Map的ley,另一组保存着Map的value,可以理解为Map中的元素是两个对象,一个对象作为键,一个对象作为值。键不可以重复,但是值可以重复
    2021-11-11
  • java  设计模式之单例模式

    java 设计模式之单例模式

    这篇文章主要介绍了java 设计模式之单例模式的相关资料,需要的朋友可以参考下
    2017-02-02
  • java报错:“错误:编码GBK 的不可映射字符”解决办法

    java报错:“错误:编码GBK 的不可映射字符”解决办法

    当Java源代码中包含中文字符时,我们在用javac编译时会出现“错误:编码GBK的不可映射字符”,这篇文章主要给大家介绍了关于java报错:“错误:编码GBK 的不可映射字符”的解决办法,需要的朋友可以参考下
    2024-08-08
  • springBoo3.0集成knife4j4.1.0的详细教程(swagger3)

    springBoo3.0集成knife4j4.1.0的详细教程(swagger3)

    这篇文章主要介绍了springBoo3.0集成knife4j4.1.0的详细教程(swagger3),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-07-07
  • java发送url请求获取返回值的二种方法

    java发送url请求获取返回值的二种方法

    这篇文章主要介绍了java发送url请求获取返回值的二种方法,需要的朋友可以参考下
    2014-03-03
  • Spring question问题小结

    Spring question问题小结

    在AppConfig配置类中,通过@Bean注解创建了Service和Controller的实例,Spring会自动将这些实例纳入容器的管理,并处理它们之间的依赖关系,本文给大家介绍Spring question问题小结,感兴趣的朋友跟随小编一起看看吧
    2023-10-10

最新评论