java - 在运行时绑定动态bean的最佳实践是什么
问题描述
我有一些配置属性定义为
@Data
@ConfigurationProperties(prefix = RedisClientConfigProperties.CONFIG_PROP_NAME)
public class RedisClientConfigProperties {
public static final String CONFIG_PROP_NAME = "dao.redis";
private int database = 0;
private String host = "127.0.0.1";
private int port = 6379;
private boolean enabled = false;
}
和以下java类:
public interface IDao {
Object get(UUID id);
void put(UUID id, Object item);
}
public class NoOpDao implements IDao {
@Override
public Object get(UUID id) {
return new Object();
}
@Override
public void put(UUID id, Object item) {
// no-op
}
}
public class RedisDao implements IDao {
@Autowired
private RedisClient client;
@Override
public Object get(UUID id) {
return client.get(id);
}
@Override
public void put(UUID id, Object item) {
client.put(id, item);
}
}
我想要的是能够将一个 IDao 对象自动装配到另一个 bean 中,并且在运行时根据该RedisClientConfigProperties#enabled
字段决定实现。在 Guice 中,我可以在模块中执行以下操作:
class DaoModule extends PrivateModule {
private final RedisClientConfigProperties configProps;
@Inject
public DaoModule(RedisClientConfigProperties configProps) {
this.configProps = configProps;
}
@Override
protected void configure() {
if (configProps.isEnabled()) {
bind(IDao.class).to(RedisDao.class);
} else {
bind(IDao.class).to(NoOpDao.class);
}
}
}
在 Spring 中,还不清楚如何执行此操作。我找到了几种不同的方法来做到这一点,但我不确定“春天的方式”是什么:
- 使用带有@Bean 方法的@Configuration 类,该方法返回IDao,方法内带有条件逻辑
@Bean
public IDao getDao(RedisClientConfigProperties config) {
if (config.isEnabled()) return new RedisDao();
else return new NoOpDao;
}
- 使用服务定位器模式创建一个工厂,该工厂将返回您想要的 bean
- 使用 @ConditionalOnProperty 注释对实现进行注释
我很好奇推荐使用这些方法中的哪一种,因为我对它们中的每一种都有一些保留意见:
- 我宁愿不必手动实例化 bean,而是让 Springs IOC 根据需要处理实例化 bean
- 这仍然会创建所有接口实现 bean,并要求 bean 的任何消费者知道他们要求什么 bean
- 由于它不使用 Config 属性对象,而是直接在您的配置属性文件中查找,因此可能容易出错。
在这三个解决方案中,我认为我更喜欢#1,但我会喜欢反馈/建议。
解决方案
您的代码似乎是每个应用程序的代码,因此我建议您将所有属性都放在配置路径中,而不是放在RedisClientConfigProperties
.
保留你的类,但像这样修改它:RedisClientConfig.java
@Data
@ConfigurationProperties(prefix = "dao.redis")
public class RedisClientConfigProperties {
private int database;
private String host;
private int port;
private boolean enabled;
// public getters and setters
.
.
}
在你的配置文件中:application.yml
dao:
redis:
enabled: true
database: 0
host: 127.0.0.1
port: 6379
并让您的 dao 仅在基于属性的上下文中注入
如果您只使用前缀来避免传递布尔值,您可以让它检查是否定义了属性
RedisDao.java
@Component
@Primary
@ConditionalOnProperty(prefix = "dao.redis", name = "enabled", havingValue = "true")
public class RedisDao implements IDao {
.
.
.
}
这样,您将NoOpDao
始终被注入,除非dao.redis.enabled
为真,否则 Redis 将覆盖NoOpDao
实现,因为它带有注释@Primary
。
推荐阅读
- php - 使用 Powershell 变量在命令行上将键/值数据传递给 PHP
- c# - System.ArgumentException:'不支持关键字:'数据源'。'
- angular - ionic3 和角度 HttpErrorResponse jsonp
- android - Android:工具栏没有填满所有空间
- java - 使用 Java Spring 4.0 和 Thymeleaf 进行表单验证
- vue.js - 排序表不起作用Vue
- mobile - Android/iOS 上的 Twilio 可编程语音 - 拨打电话时传递自定义信息
- java - 泛型,不适用于 List 的参数
当方法参数是(列表 调用方法maximum时我在eclipse上遇到错误,说方法
maximum (<? extends T>, int, int) in the type generics classname is not applicable for the argument (List<Integer>, int ,int).
- java - java I/O 尝试使用二进制数据参数的资源?
- go - 解析地图的 yaml 错误