从args讲到springboot全套配置体系架构(五)

五、springboot中第三方组件如何实现自动配置

我们知道springboot 只需要引入一个starter就可以完成一个组件的集成,那么这个starter到底是怎么做的呢?

spring-boot-autoconfigure-2.1.9.RELEASE.jar

我们先来看看spring-boot-autoconfigure-2.1.9.RELEASE.jar 这个包。顾名思义就是springboot 自动配置的核心包。
此包下集合了我们常用的一些组件的配置类,如redis,mq,jdbc,cach等等。我们就以redis为例展开讲解
redis的配置类路径为此包下\org\springframework\boot\autoconfigure\data\redis
在这里插入图片描述
我们先来看看
RedisProperties.java 这个类

RedisProperties

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

我们发现这里除了一个@ConfigurationProperties(prefix = “spring.redis”) 注解以外,不存在多余的注解了,并且我们可以通过实验可以知道这个注解的并不会被spring自动实例化。那么为什么我引入redis的依赖后

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

它就会被实例化加载了呢?

@EnableConfigurationProperties注解

我们先来看看RedisAutoConfiguration这个类,redis的自动配置类
以下为此类全部内容

@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@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;
	}

}

先关注这个注解@EnableConfigurationProperties(RedisProperties.class)
我们知道使用 @component系列(@Configuration…) + @ConfigurationProperties(prefix = “spring.redis”)能够将 application + bootstrap 默认配置文件的信息导入配置类。现在这里似乎又出现了一个可以有这种能力的注解。我们先来实验下
新建配置类
RedisConfig.java

@Data
@ConfigurationProperties(prefix = "spring.redis")
public class RedisConfig {
    private String host;
    private String password;
    private int port;
    private int database;
}

application.properties文件添加

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=123456789
spring.redis.database=0

我们在测试类上添加@EnableConfigurationProperties(RedisConfig.class)这个注解

@SpringBootTest(classes = {ServiceUserApplication.class})
@RunWith(SpringRunner.class)
@EnableConfigurationProperties(RedisConfig.class)
public class ServiceUserApplicationTests {
    @Autowired
    RedisConfig redisConfig;

    @Test
    public void contextLoads() {
        System.out.println(redisConfig.getHost());
    }

}

我们很神奇的发现控制台输出了127.0.0.1 说明这个配置被取到了,说明RedisConfig 被实例化了。也就意味
@EnableConfigurationProperties(RedisConfig.class) 这个注解是另一个系列的Component 注解。它同样能实例化一个bean。
假如我们把@ConfigurationProperties(prefix = “spring.redis”) 这个注解移除掉会发生什么事情呢?

一点都不意外报了如下错误信息

Caused by: java.lang.IllegalArgumentException: No ConfigurationProperties annotation found on  'com.yzj.learn.user.config.RedisConfig'.
	at org.springframework.util.Assert.notNull(Assert.java:215)

也就是说@EnableConfigurationProperties(RedisConfig.class)这个注解引入的类名一定需要被@ConfigurationProperties(prefix = “spring.redis”) 这个注解修饰。

我们跟进@EnableConfigurationProperties发现它是被这个注解修饰的@Import(EnableConfigurationPropertiesImportSelector.class) ;我们先不管@import是干什么的,我们先看看他的这个入参
EnableConfigurationPropertiesImportSelector.class
这个类就是去解析了EnableConfigurationProperties 这个注解,并且通过这个注解又拿到了对应的ConfigurationProperties 注解的类的实例。然后将之实例化。

@Import 注解简述

在回过头看看@Import这个注解

@Import(EnableConfigurationPropertiesImportSelector.class) 这个注解实际上相当于实例化EnableConfigurationPropertiesImportSelector

那么有人会说了那既然效果都和@Component系列注解一样,为啥又要分这么多类型呢?
我来解释下:
首先类如果被注解了@Component 系列,那么只要它在spring的扫包路径下,它就一定会被实例化成bean。那么这个时候,我如果实际上并没有使用这个实例。那么这就造成极大的性能和资源上的浪费,而@Import +@Enable**注解的出现给加载提供了一个开关,实现按需加载。
后续还会讲到一套@Condition注解,@import和@condition也就是springboot的整个核心所在。

整个springboot配置核心概念@Import +@Enable** 实现手动按需加载 @import和@condition 实现自动按需加载

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章