Condition
Spring4.0 增加的条件判断功能,通过这个功能可以实现选择性的创建Bean。
第一节、小例子–获取redisTemplate
- 新建一个项目
- 在springboot启动后,获取IOC容器,从容器中获取redisTemplate
- 运行,发现找不到redisTemplate这个Bean
- 此时我们导入redis的依赖(注意刷新一下maven)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 再运行,发现有了这个redisTemplate
根据上面这个例子,我们发现,在导入依赖后,springboot自动帮我们创建了redisTemplate这个Bean。为什么springboot自动创建这个Bean?
第二节、入门Condition
小例子
需求:如果导入了jedis,则初始化User这个Bean到IOC容器,否则不加载
- 新建User
package com.it2.springbootcondition01.pojo;
public class User {
}
- 新建Config
package com.it2.springbootcondition01.config;
import com.it2.springbootcondition01.condition.MyClassCondition;
import com.it2.springbootcondition01.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class UserConfig {
@Bean
@Conditional({MyClassCondition.class})
public User user(){
return new User();
}
}
- 导入jedis,导入或者移除都一定要刷新依赖,避免这种小错误。
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
- 新建Condition
package com.it2.springbootcondition01.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class MyClassCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
try {
Class.forName("redis.clients.jedis.Jedis");
} catch (ClassNotFoundException e) {
return false;
}
return true;
}
}
说明:redis.clients.jedis.Jedis是jedis包的里面的类,可以先导入jedis包,去找到里面的jedis。 5. 从容器里面取User这个Bean
package com.it2.springbootcondition01;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class SpringbootCondition01Application {
public static void main(String[] args) {
ConfigurableApplicationContext context=SpringApplication.run(SpringbootCondition01Application.class, args);
Object user=context.getBean("user");
System.out.println(user);
}
}
- 运行,发现从容器里面可以拿到User
- 把jedis的依赖注释掉,再次运行。发现user这个bean不可用。
源码解析
关键点@Conditional
我们进入到@Conditional的源码,发现它的参数是Condition数组 再进入Condition,发现它是一个接口,有一个matchers方法(匹配) 按照这个要求,我们自己定义一个condition,返回true/false。也就是说,我们可以根据自定义的condition返回的boolean值来决定是否加载user,剩下的问题就回归到如何确定jedis是否存在了(java反射可以知道是否包含某个类Class.forName)。
第三节、自定义Conditional(动态传值)
接着,我们对前面的例子进行升级,动态的传入名称。
- 自定义Conditional
package com.it2.springbootcondition01.conditional;
import com.it2.springbootcondition01.condition.MyClassCondition;
import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(MyClassCondition.class)
public @interface MyConditionalOnClass {
String[] value();
}
- 重新给user设置自定义的注解
@MyConditionalOnClass("redis.clients.jedis.Jedis")
3. 修改MyClassCondition
package com.it2.springbootcondition01.condition;
import com.it2.springbootcondition01.conditional.MyConditionalOnClass;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Map;
public class MyClassCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> map = metadata.getAnnotationAttributes(MyConditionalOnClass.class.getName());
String[] value= (String[]) map.get("value");
try {
for (String className : value) {
Class.forName(className);
}
} catch (ClassNotFoundException e) {
return false;
}
return true;
}
}
- 把前面jedis的依赖加回来
dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
- 运行。发现依然可以获取user
- 注释掉jedis的依赖,再运行。容器内找不到user这个bean。
第四节、springboot提供的Conditional
我们定义的MyConditionalOnClass实际上就是系统自带的ConditionalOnClass,除此之外,系统还提供了各种各样的Conditional。
试用系统自带的ConditionalOnProperty 添加属性 启动测试 注释属性再测试
第五节 常用的条件注解
- ConditionalOnProperty:配置文件有对应属性才初始化Bean
- ConditionalOnMissingClass:环境中不包含对应的字节码才初始化Bean
- ConditionalOnClass:环境中有包含对应的字节码才初始化Bean
|