Spring条件化装配Bean

Spring4引入了@Conditional注解,可配合@Bean或者@Component注解一起使用。

用CET4考试来演示@Conditional注解。

新建考试结果Result接口:

1
2
3
public interface Result {
public void getResult();
}

实现类CET4:

1
2
3
4
5
public class CET4 implements Result{
public void getResult() {
System.out.println("恭喜你通过CET4");
}
}

定义学生接口:

1
2
3
public interface Student {
public void exam();
}

实现类XiaoMing:

1
2
3
4
5
6
7
8
9
10
public class XiaoMing implements Student{
//注入result
@Autowired(required=false)
private Result result;

public void exam() {
if(result == null) System.out.println("抱歉,CET4未通过");
else result.getResult();
}
}

配置JavaConfig:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ConditionConfig {
@Bean
@Conditional(ResultCondition.class)
public Result result(){
return new CET4();
}
@Bean(name="xiaoming")
public Student student(){
return new XiaoMing();
}
}

可以看到,@Conditional中给定了一个Class,它指明了条件——在本例中,也就是ResultCondition:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class ResultCondition implements Condition{

public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
//获取环境变量中的result属性
String result = env.getProperty("result");
if("success".equals(result)) return true;
else return false;
}
}

设置给@Conditional的类可以是任意实现了Condition接口的类型。可以看出来,这个接口实现起来很简单直接,只需提供matches()方法的实现即可。如果matches()方法返回 true,那么就会创建带有@Conditional注解的bean。如果matches()方法返回false,将不会创建这些bean。

ConditionContext是一个接口,大致如下所示:

1
2
3
4
5
6
7
public interface conditioncontext{
BeanDefinitionRegistry getRegistry();
ConfigurableListableBeanFactory getBeanFactory();
ResourceLoader getResourceLoader();
Environment getEnvironment();
ClassLoder getClassLoader();
}

通过ConditionContext,我们可以做到如下几点:

1.借助getRegistry()返回的BeanDefinitionRegistry检查bean定义;

2.借助getBeanFactory()返回的ConfigurableListableBeanFactory检查bean是否存在,甚至探查bean的属性;

3.借助getEnvironment()返回的Environment检查环境变量是否存在以及它的值是什么;

4.读取并探查getResourceLoader()返回的ResourceLoader所加载的资源;

5.借助getClassLoader()返回的ClassLoader加载并检查类是否存在。

AnnotatedTypeMetadata则能够让我们检查带有@Bean注解的方法上还有什么其他的注解。像ConditionContext一样,AnnotatedTypeMetadata也是一个接口。它如下所示:

1
2
3
4
5
6
7
8
9
10
public interface AnnotatedTypeMetadata{
boolean isAnnotated(String annotationType);
Map<String,Object> getAnnotationAttributes(String annotationType);
Map<String,Object> getAnnotationAttributes(
String annotationType,boolean classValuesAsString);
MutilValueMap<String,Object> getAllAnnotationAttributes(
String annotationType);
MutilValueMap<String,Object> getAllAnnotationAttributes(
String annotationType,boolean classValuesAsString);
}

借助isAnnotated()方法,我们能够判断带有@Bean注解的方法是不是还有其他特定的注解。借助其他的那些方法,我们能够检查@Bean注解的方法上其他注解的属性。

现在测试@Conditional注解的作用:

1
2
3
4
5
6
7
8
public class TestConditional {
public static void main(String[] args) {
System.setProperty("result", "fail");
ApplicationContext ac = new AnnotationConfigApplicationContext(ConditionConfig.class);
Student xiaoming = (Student) ac.getBean("xiaoming");
xiaoming.exam();
}
}

这里设置result为fail,ResultCondition的matches()方法返回false,所以CET4 Bean并不会被创建,结果应该输出“抱歉,CET4未通过”,测试结果:

1
抱歉,CET4未通过

请作者喝瓶肥宅水🥤

0