目录
五、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 实现自动按需加载