Spring 中动态实例化 Bean

实际开发中,我们会遇到一些需要动态地控制 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 {
/**
* 配置消息监听器
* @return 消息监听器
*/
@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;

/**
* @desc MQ 监听器启用状态条件
* @author HLJ
* @date 2019/7/11 20:34
*/
public class RocketMqListenerEnabledCondition implements Condition {

/**
* @desc 决定 MQ 监听是否启用
* @author HLJ
* @date 2019/7/11 20:33
* @param context 条件上下文
* @param metadata 方法元数据
* @return 是否匹配
*/
@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 的取值来控制实例化。


Spring 中动态实例化 Bean
https://blog.yohlj.cn/posts/bddbf67f/
作者
Enoch
发布于
2019年7月11日
许可协议