今天在了解Springboot的自动配置原理的时候,发现绝大多数“xxxAutoConfiguration”类上,都带有@Conditionalxxx的注解。特意对该方面做一些基本的了解。
话不多说,还是从两个老套路进入了解:是什么?怎么玩?
- 什么是 @Conditional注解?
Indicates that a component is only eligible for registration when all {@linkplain #value specified conditions} match
- 这是来自@Conditional注解的Javadoc。表达的意思是:表示组件仅在全部条件都满足的时候,才有资格注册。
2.@Conditional怎么用?
- 举例说明:
不同的宠物,如猫、狗,它们的叫声是不一样的。我们可以这样处理不同宠物的叫声:
//mock codes
if (dog) {
//狗叫声
} else if (cat) {
// 猫叫声
}
但是如果有成百上千中宠物,这样的if else 显然不太理智,也不太合适。以往我一般想到可以使用工厂模式来解决这个问题:每种宠物定义一个service,所有的service放到一个工厂中,然后根据传入的宠物类型获取对应的service:
//将所有的service实现类定义到该map中
private final Map serviceMap = new HashMap<>();
//根据宠物类型,获取与之对应的service
public PatService getService(String patType) {
return serviceMap.get(patType);
}
这样的方法固然可行。但是,如果有100种宠物,内存中就会存在100个Service,而实际上有可能service根本一次都不会执行到(当然也可能会执行到)。针@Conditional就可以很好地解决这个问题。
2.代码实现
(a)前面提到,当满足一定条件的时候,才有资格注册。所谓的条件,就是我们这里Condition这个接口。我们需要去实现它;
(b)当满足条件的时候,将组件注册到IOC容器中。满足条件后,触发注册的触发器,便是这个@Conditional注解。@Conditional的values中的类(都是Condition接口的实现类)的matches方法返回true的时候,@Conditional所注解的类或者方法就会执行。
3.编写Condition实现类
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* @author ZhouSicheng
* @description
* @date 2019/1/14 下午3:39
*/
public class DogCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String petType = context.getEnvironment().getProperty("pet.type");
return petType.equalsIgnoreCase("dog");
}
}
狗叫的条件:当配置文件中pet.type为dog的时候,matches方法会返回true。
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* @author ZhouSicheng
* @description
* @date 2019/1/14 下午3:41
*/
public class CatCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String petType = context.getEnvironment().getProperty("pet.type");
return petType.equalsIgnoreCase("cat");
}
}
猫叫的条件:当配置文件中pet.type为cat的时候,matches方法会返回true。
3.接口PatService以及两个实现类
/**
* @author ZhouSicheng
* @description
* @date 2019/1/14 下午3:43
*/
public class DogService implements PatService {
@Override
public void voice() {
System.out.println("汪汪汪.....");
}
}
/**
* @author ZhouSicheng
* @description
* @date 2019/1/14 下午3:43
*/
public class CatService implements PatService {
@Override
public void voice() {
System.out.println("喵喵喵...");
}
}
4.配置Config类,根据条件组装不同的PatService实现类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* @author ZhouSicheng
* @description
* @date 2019/1/14 下午3:42
*/
@Configuration
public class ConditionConfig {
//满足哪个Condition,就装配哪个service实现类
@Bean
@Conditional(DogCondition.class)
public PatService dogService() {
return new DogService();
}
@Bean
@Conditional(CatCondition.class)
public PatService catService() {
return new CatService();
}
}
5.配置文件
pet:
type: cat
6.测试代码
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.HashMap;
import java.util.Map;
/**
* @author ZhouSicheng
* @description
* @date 2019/1/14 下午3:45
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class ConditionTest {
@Autowired
private ApplicationContext applicationContext;
@Test
public void testCondition() {
PatService patService = applicationContext.getBean(PatService.class);
patService.voice();
}
最终的输出结果:
喵喵喵...
好了,到这里,@Conditional的功能就实现了。当然,强大的Spring已经为我们做了很多的条件扩展,可以拿来直接用:
@ConditionalOnBean(仅仅在当前上下文中存在某个对象时,才会实例化一个Bean)
@ConditionalOnClass(某个class位于类路径上,才会实例化一个Bean)
@ConditionalOnExpression(当表达式为true的时候,才会实例化一个Bean)
@ConditionalOnMissingBean(仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean)
@ConditionalOnMissingClass(某个class类路径上不存在的时候,才会实例化一个Bean)
@ConditionalOnNotWebApplication(不是web应用)