3.2条件化的bean
如果你希望一个或者多个bean只有在应用的类路径夏包含特定的库的时候才创建。
或者希望某个bean只有当另外某个特定的bean也申明了之后才创建。
某个环境变量设置之后,才创建等等。。。
@Conditional 注解
Spring4引入了一个新的注解,可以用在@Bean注解的方法上。如果给定的条件计算结果为true,就会创建这个bean,否则的话,这个bean就会被忽略。
书上的例子不太会配,验证,我找了一个别的例子。
@Bean
@Conditional(MagicExistsCondition.class)
public MagicBean magicBean() {
return new MagicBean();
}
这个Bean的配置,@Conditional注解上,配置了一个MagicExistsCondition,这个类继承了Condition接口。
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
这个condition接口,通过判断,来返回true或者false,来决定这个这个Bean是不是被创建!
public class MagicExistsCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
System.out.println(environment.getProperty("os.name"));
return environment.getProperty("os.name").contains("Linux");
}
}
这边我选择了一个比较简单的判断法,通过env对象,去获取操做系统的名字,如果是Linux就创建,windows就不创建。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DataSourceConfig.class)
//@ActiveProfiles("dev")
public class TestAdv {
@Autowired
public MagicBean magicBean;
@Test
public void testFun(){
System.out.println(magicBean.getMagic());
}
}
这边有一个简单的Testcase,可以随便试试,换一下Linux或者windows看看还会不会创建。
这里的例子是一个非常简单的例子,还有更加复杂的例子。
ConditionContext接口
public interface ConditionContext {
BeanDefinitionRegistry getRegistry(); //检查Bean的定义
@Nullable
ConfigurableListableBeanFactory getBeanFactory(); //检查bean是否存在,甚至探查bean的属性
Environment getEnvironment(); //检查环境变量,及其值
ResourceLoader getResourceLoader(); //resourceLoader所加载的资源
@Nullable
ClassLoader getClassLoader(); //classLoader来检查类是否存在
}
AnnotatedTypeMetaData接口
public interface AnnotatedTypeMetadata {
boolean isAnnotated(String var1);
@Nullable
Map<String, Object> getAnnotationAttributes(String var1);
@Nullable
Map<String, Object> getAnnotationAttributes(String var1, boolean var2);
@Nullable
MultiValueMap<String, Object> getAllAnnotationAttributes(String var1);
@Nullable
MultiValueMap<String, Object> getAllAnnotationAttributes(String var1, boolean var2);
}
这个接口能够让我们检查带有@Bean注解的方法上还有什么其他的注解。借助其他的那些方法,我们能够检查@Bean注解的方法上其他注解的属性。
@Profile的实现
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
/**
* The set of profiles for which the annotated component should be registered.
*/
String[] value();
}
profile也是使用@Conditional注解来判断这个Bean是不是要加载。
class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles((String[]) value)) {
return true;
}
}
return false;
}
return true;
}
}
可以看到这个地方的match方法的实现,
1. 通过metaData来获取这个Bean上面Profile相关的注解,例如
@Profile("dev"),这边的attrs就会是一个value->dev
2. 只有配置了这个@Profile注解的Bean才会进入那个if,没有配置的话就直接返回true,就是需要配置!
3. 进入for loop,看profile里面value是不是和环境相一致,比如我激活的profile是dev,那我的环境和dev相同,就会配置那个bean,相反如果是prod,那么就会是false。