SpringBoot高级-自动配置原理剖析一
前言:在使用SpringBoot开发时,发现它非常的方便,很多东西就是导入依赖,拿来即用,并不清楚其中的原理是什么,所有心理多少会有些疑惑,了解自动配置原理之后,我们可以更好的使用SpringBoot,并且学习其中的设计思想。
示例1:我们对SpringBoot引导类进行修改,通过IOC容器,获取redisTemplate这个Bean,看能否获取?
@SpringBootApplication
public class SpringbootConditionApplication {
public static void main(String[] args) {
//启动SpringBoot的应用,返回Spring的IOC容器
ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
//获取Bean,redisTemplate
Object redisTemplate = context.getBean("redisTemplate");
System.out.println(redisTemplate);
}
}
控制台结果:可知默认的时候,SpringBoot不会为我们加载redisTemplate这个Bean。
下一步,我们在pom.xml中加入redis的起步依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
再次启动程序:可以成功的获取redisTemplate的Bean
小结:通过上述示例,我们可以了解到,通过添加依赖,IOC容器就可以获取到相关(redisTemplate)的Bean,由此就可以使用具体的方法了。SpringBoot具体是怎么实现的呢?我们继续往下做示例。
重点:Condition是在Spring4.0增加的条件判断功能,通过这个功能,SpringBoot可以实现选择性的创建Bean操作。
示例2:实现在Spring容器中有一个User的Bean。
1、新建一个User类
/**
* @description:User类:属性略
* @date: 2020/10/9 15:25
* @author: winson
*/
public class User {
}
2、新建一个User的配置类,实现Bean注册到Spring容器中
/**
* @description:User配置类
* @date: 2020/10/9 15:25
* @author: winson
*/
@Configuration
public class UserConfig {
@Bean
public User user() {
return new User();
}
}
3、测试user的Bean是否成功注入到IOC容器中
@SpringBootApplication
public class SpringbootConditionApplication {
public static void main(String[] args) {
//启动SpringBoot的应用,返回Spring的IOC容器
ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
/*//获取Bean,redisTemplate
Object redisTemplate = context.getBean("redisTemplate");
System.out.println(redisTemplate);*/
Object user = context.getBean("user");
System.out.println(user);
}
}
4、结果:测试成功。
接着示例2,在此基础上做示例3。
示例3:示例2完成了自动注入user的Bean,即使我们不想要user的Bean,它还是存在于IOC容器中了,现在我们将程序继续改造,实现当导入Jedis坐标后,才加载user的Bean,没导入,IOC容器中不加载user的Bean。这里我们先介绍一个注解@Conditional
All classes that must in order for the component to be registered.:注册组件必须使用的所有类。
使用@Conditional注解的类,必须按照条件泛型要求,即:泛型条件类,必须Condition接口的子类
此时我们再来看Condition接口,该接口有一个方法matches(),返回值是一个boolean类型的
由此可知,使用@Conditional注解时,参数(类)可以为一个数组,此数组中的类必须是Condition接口的子类(实现此接口并重写matches()方法,如果matches()返回的是true,@Conditional注解生效,否则不生效),由此我们就可以继续做测试了。
1、修改UserConfig配置类
/**
* @description:User配置类
* @date: 2020/10/9 15:25
* @author: winson
*/
@Configuration
public class UserConfig {
//添加@Conditional注解
@Conditional(ClassCondition.class)
@Bean
public User user() {
return new User();
}
}
2、新建一个条件类,该条件类必须实现Condition接口,并重写其matches()方法
/**
* @description:条件类
* @date: 2020/10/9 16:24
* @author: winson
*/
public class ClassCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return false;
}
}
3、再次启动程序,测试结果可知,当条件类ClassCondition返回false后,我们再从IOC容器中获取user类的Bean,就会失败。
4、再修改条件类ClassCondition,使其返回true。
public class ClassCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return true;
}
}
5、重启程序可知,又可以获取user的Bean了
6、回到示例3的需求:实现当导入Jedis坐标后,才加载user的Bean,没导入,IOC容器中不加载user的Bean。
6.1、修改ClassCondition类,思路见代码注释
/**
* @description:条件类
* @date: 2020/10/9 16:24
* @author: winson
*/
public class ClassCondition implements Condition {
//1、需求:导入Jedis坐标后创建Bean
//思路:判断redis.clients.jedis.Jedis文件是否存在
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
boolean flag = true;
try {
Class<?> cls = Class.forName("redis.clients.jedis.Jedis");
} catch (ClassNotFoundException e) {
flag = false;
}
return flag;
}
}
7、测试
7.1、pom.xml中先不导入Jedis依赖
7.2、导入Jedis依赖
<dependency>
<groupId>Jedis</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
7.3、测试结果,导入Jedis依赖后,成功从IOC容器中获取到了user的Bean