1.Condition
Condition是在spring4.0增加的条件判断功能,通过这个功能可以实现选择性的创建bean的操作
常用到的注解:
@Configuration
:标明该类是一个配置类,并注入容器@ConditionalOnClass
:当需求类均存在时,满足启用要求
1-1.创建一个springboot工程,编写启动类
原理:我们通过springboot提供的run方法查看,发现其返回ConfigurableApplicationContext这个对象,它是spring的Ioc容器,因此我们通过它获取IOC容器,再通过IOC容器获取相应的bean
@SpringBootApplication
public class SpringbootConditionApplication {
public static void main(String[] args) {
//启动springbootd时,返回spring的IOC容器
ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
//获取redisTemplate 的 bean
Object redisTemplate = context.getBean("redisTemplate");
System.out.println(redisTemplate);
}
}
1-2.启动这个启动类
这时我们并没有导入redis的依赖,所以根本无法获取redisTemplate的bean,会报这样的一个错:意思就是没有这个叫redisTemplate的bean 可以用
1-3.导入redisTemplate的所需依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
我们再次启动,发现并没有报错,那么问题来了,springboot是如何知道我们要创建的哪个bean,接下通过一个案例来理解:
需求:在spring的IOC容器中有一个User的bean,要求导入jedis依赖后才能加载该bean,没导入则不加载
1-4.创建User类
package cn.ck.pojo;
public class User {
private String name;
private int age;
//get与set方法。。。
}
1-5.创建一个条件类ClassCondition,继承Condition接口
该接口org.springframework.context.annotation这个包下
实现这个接口的matches方法,其返回值为布尔值,true代表创建指定的bean,false代表不创建指定的bean
public class ClassCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//需求:要求导入jedis依赖后才能加载该bean,没导入则不加载
//思路:判断jedis依赖是否导入
Boolean flag = true;
try {
Class<?> aClass = Class.forName("redis.clients.jedis.Jedis");
} catch (ClassNotFoundException e) {
//出现异常说明没有该类
e.printStackTrace();
flag = false;
}
return flag;
}
}
1-6 创建配置类UserConfig用于加载创建User这个实列:
使用@Conditional(条件类.class)来引用我们创建的条件类,来判断是否符合我们指定的条件来生成User的bean
@Configuration
public class UserConfig {
@Bean(name = "user")
@Conditional(ClassCondition.class)//引用条件是否要创建该bean
public User getUser(){
return new User();
}
}
1-7.编写启动类
@SpringBootApplication
public class SpringbootConditionApplication {
public static void main(String[] args) {
//启动springbootd时,返回spring的IOC容器
ConfigurableApplicationContext context = SpringApplication.run(SpringbootConditionApplication.class, args);
//获取user的bean
User user = (User) context.getBean("user");
user.setName("张三");
user.setAge(20);
System.out.println(user.getName()+" "+user.getAge());
}
}
1-8.启动
这时我们并没有导入jedid的相关依赖,所以不应该获取到user的bean
接着导入jedis的相关依赖:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
再次运行:
能够获取到bean以及数据,说明Condition是在spring4.0增加的条件判断功能,通过这个功能可以实现选择性的创建bean的操作
需求2:
- 在spring的IOC容器中有一个User的bean
- 要求导入指定依赖后才能加载该bean,没导入则不加载
- 将类的判断定义为动态的,判断哪个字节码文件存在可以通过动态指定
1-1.定义一个注解类 ConditionOnClass
import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;
//定义当前注解的使用范围
@Target({ElementType.TYPE, ElementType.METHOD})
//注解的生效时机
@Retention(RetentionPolicy.RUNTIME)
//可以生成javadoc的文档
@Documented
//将我们定义好的条件类通过@Conditional指定,这样该注解就完成了ClassCondition的功能
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {
//给当前这个注解添加属性数组,用于可以指定很多的类
String[] value();
}
1-2.改写我们自定义的条件类
思路:根据提供的metadata参数,来动态获取上方自定义注解的value值
public class ClassCondition implements Condition {
/**
* @param context 上下文对象,用于获取环境,IOC容器,以及classloader对象
* @param metadata 注解源对象,用于获取注解定义时输入的属性值value
* @return boolean
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//需求:导入指定依赖后才能加载该bean,没导入则不加
//通过我们自定义的注解类的名称,来获取指定的依赖,也就是自定义注解属性的值 value
Map<String, Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
assert map != null;
String[] value = (String[]) map.get("value");
boolean flag = true;
try {
for (String s : value) {
Class<?> aClass = Class.forName(s);
}
} catch (ClassNotFoundException e) {
//出现异常说明没有该类
e.printStackTrace();
flag = false;
}
return flag;
}
}
1-3.在UserConfig中使用我们自定义的注解,并传入指定要判断的依赖
@Configuration
public class UserConfig {
@Bean(name = "user")
//自定义的注解,判断指定的依赖
@ConditionOnClass(value = "redis.clients.jedis.Jedis")
public User getUser(){
return new User();
}
}
1-4.再次启动启动类
说明我们自定的注解是可以实现的,这时我们就可以动态的去使用该注解实现自动配置的机制
更多注解:
回到 SpringBoot,除了一开始介绍的 @ConditionalOnClass 外,SpringBoot 还封装了这些注解,供我们使用:
-
@ConditionalOnSingleCandidate 类似于@ConditionalOnBean
-
但只有在确定了给定bean类的单个候选项时才会加载bean。
-
@ConditionalOnMissingBean 当给定的 Bean 不存在时返回 true
-
各类型间是 or 的关系
-
@ConditionalOnBean 与上面相反,要求bean存在
-
@ConditionalOnMissingClass 当给定的类名在类路径上不存在时返回true
-
各类型间是 and 的关系
-
@ConditionalOnClass 与上面相反,要求类存在
-
@ConditionalOnExpression Spel 表达式成立,返回 true
-
@ConditionalOnJava 运行时的Java版本号包含给定的版本号,返回 true
-
@ConditionalOnProperty 属性匹配条件满足,返回 true
-
@ConditionalOnWebApplication web环境存在时,返回 true
-
@ConditionalOnNotWebApplication web环境不存在时,返回 true
-
@ConditionalOnResource 指定的资源存在时,返回 true