实际开发中,我们会遇到一些需要动态地控制 Bean 的实例化的情景,比如在 dev 环境我们想把消息队列的消费者默认关闭,如果大家都开启消费者那么我们调试是很不方便的。在 SpringBoot 项目中可以通过 @ConditionalOnExpression 注解来实现,Spring 项目中 @Conditional 注解来实现。
@ConditionalOnExpression
@ConditionalOnExpression 注解用于 SpringBoot 项目,它的 value 取值支持 EL 表达式,该注解可以用于类也可以用在方法上。
拿前面消息队列的例子,我们可以在配置文件中设置 mq_listener_enabled 属性来控制监听器的开启情况,示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Configuration @ConditionalOnExpression("#{'true'.equals(environment['mq_listener_enabled'])}") public class RocketMqConfig {
@Bean("messageListener") public RocketMqMessageListener messageListener() { return new RocketMqMessageListener(); }
@Bean(value = "mqConsumer", initMethod = "start", destroyMethod = "shutdown") public ConsumerBean timerConsumer(RocketMqMessageListener messageListener, LocalProperties localProperties) { } }
|
@Conditional
@Conditional 注解用于纯 Spring 项目,它的 value 是一个类(要求实现 Condition 接口),该注解可以用于类也可以用在方法上。
拿前面消息队列的例子,我们可以创建一个类来实现开关,示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import org.apache.commons.lang3.BooleanUtils; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata;
import java.io.InputStream; import java.util.Properties;
public class RocketMqListenerEnabledCondition implements Condition {
@Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { boolean enabled = true; try (InputStream is = RocketMqListenerEnabledCondition.class.getClassLoader() .getResourceAsStream("application.properties")) { Properties p = new Properties(); p.load(is); String enabledS = p.getProperty("mq_listener_enabled", "true"); enabled = BooleanUtils.toBoolean(enabledS); } catch (Exception ignored) { } return enabled; } }
|
在需要动态控制实例化的 ConsumerBean 方法上使用 @Conditional 注解,示例代码:
1 2 3 4 5
| @Bean(value = "mqConsumer", initMethod = "start", destroyMethod = "shutdown") @Conditional(RocketMqListenerEnabledCondition.class) public ConsumerBean timerConsumer(RocketMqMessageListener messageListener, LocalProperties localProperties) {
}
|
@Conditional 相对 @ConditionalOnExpression 更加灵活,可以用来实现复杂的动态实例化逻辑,比如我们可以在 redis 上存储一个 key,通过这个 key 的取值来控制实例化。