在springboot的自动化配置中,这个注解起到了相当重要的作用。
其一般与@Bean注解一起使用,作用就是满足一定条件才将@Bean修饰的bean注入容器。
下面看一个例子,仅仅当classpath下有某一个类才注入bean。
定义一个Condition接口的实现:
public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
try {
Class<?> clazz = context.getClassLoader().loadClass("com.liyao.A");
if (clazz != null) {
return true;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return false;
}
}
会在classpath里查找“com.liyao.A”这个类,如果有就注入。
下面是配置类:
class MyBean{
}
@Configuration
public class JavaConfig {
public static void main(String args[]) {
ApplicationContext app = new AnnotationConfigApplicationContext(JavaConfig.class);
MyBean myBean = (MyBean) app.getBean("myBean");
System.out.println(myBean == null);
}
@Bean(name = "myBean")
@Conditional(MyCondition.class)
public MyBean getBean() {
return new MyBean();
}
}
在工程里写一个com.liyao.A的java文件,一起编译,最终会输出false。
如果没有类会抛出noBean的异常。
那么@Condition注解是如何被Spring框架处理的呢?
因为该注解与@Configuration一起使用,其处理过程是在处理@Configuration的BeanFactoryPostProcessor里,即ConfigurationClassPostProcessor。
在这个processor里,最终会处理@Bean注解,将bean注册,注册前会看是否有@Condition注解,会调用其match方法看是否需要注入:
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
TrackedConditionEvaluator trackedConditionEvaluator) {
if (trackedConditionEvaluator.shouldSkip(configClass)) {
removeBeanDefinition(configClass);
return;
}
if (configClass.isImported()) {
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
loadBeanDefinitionsForBeanMethod(beanMethod);
}
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
match的调用在conditionEvaluator里:
public boolean shouldSkip(ConfigurationClass configClass) {
Boolean skip = this.skipped.get(configClass);
if (skip == null) {
if (configClass.isImported()) {
if (shouldSkip(configClass.getImportedBy())) {
// The config that imported this one was skipped, therefore we are skipped
skip = true;
}
}
if (skip == null) {
skip = conditionEvaluator.shouldSkip(configClass.getMetadata(),
ConfigurationPhase.REGISTER_BEAN);
}
this.skipped.put(configClass, skip);
}
return skip;
}
public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationPhase phase) {
if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
return false;
}
if (phase == null) {
if (metadata instanceof AnnotationMetadata &&
ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
}
return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
}
for (String[] conditionClasses : getConditionClasses(metadata)) {
for (String conditionClass : conditionClasses) {
Condition condition = getCondition(conditionClass, context.getClassLoader());
ConfigurationPhase requiredPhase = null;
if (condition instanceof ConfigurationCondition) {
requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
}
if (requiredPhase == null || requiredPhase == phase) {
if (!condition.matches(context, metadata)) {
return true;
}
}
}
}
return false;
}