我正在尝试定义自定义DeltaSpike ConfigSource。自定义配置源将具有最高优先级,并检查数据库中的config参数。
我有一个ConfigParameter实体,只有一个键和一个值。
@Entity
@Cacheable
public class ConfigParameter ... {
private String key;
private String value;
}
我有一个@Dependent
DAO,可以找到所有配置参数。
我现在要做的是定义一个自定义ConfigSource,它能够从数据库中获取config参数。因此,我想在ConfigSource中注入我的DAO。所以基本上像
@ApplicationScoped
public class DatabaseConfigSource implements ConfigSource {
@Inject
private ConfigParameterDao configParameterDao;
....
}
但是,当通过 META-INF / services / org.apache.deltaspike.core.spi.config.ConfigSource 注册ConfigSource时,该类将被实例化,CDI将无法工作。
在这种情况下,有没有办法让CDI工作?
如果您需要任何进一步的信息,请提前致谢,请告知我们。
答案 0 :(得分:0)
DS正在使用java se spi机制,而不是CD' Injectable'。一种解决方案是使用BeanProvider来获取DatabaseConfigSource并将操作委托给它。
答案 1 :(得分:0)
主要问题是,当BeanManager不可用时,ConfigSource很早就会被实例化。甚至JNDI查找在那个时间点也不起作用。因此,我需要延迟注入/查找。
我现在做的是在我的配置源中添加一个静态布尔值,我手动设置。我们有一个InitializerService,可以确保系统设置正确。在初始化过程结束时,我调用allowInitialization()
以告诉配置源,该bean现在是可注入的。下次询问ConfigSource时,它将能够使用BeanProvider.injectFields
注入bean。
public class DatabaseConfigSource implements ConfigSource {
private static boolean allowInit;
@Inject
private ConfigParameterProvider configParameterProvider;
@Override
public int getOrdinal() {
return 500;
}
@Override
public String getPropertyValue(String key) {
initIfNecessary();
if (configParameterProvider == null) {
return null;
}
return configParameterProvider.getProperty(key);
}
public static void allowInitialization() {
allowInit = true;
}
private void initIfNecessary() {
if (allowInit) {
BeanProvider.injectFields(this);
}
}
}
我有一个请求范围的bean,它包含我所有的配置变量以进行类型安全访问。
@RequestScoped
public class Configuration {
@Inject
@ConfigProperty(name = "myProperty")
private String myProperty;
@Inject
@ConfigProperty(name = "myProperty2")
private String myProperty2;
....
}
在不同的bean中注入Configuration类时,将解析每个ConfigProperty。由于我的自定义DatabaseConfigSource具有最高序数(500),因此它将首先用于属性解析。如果找不到该属性,它会将解析权委托给下一个ConfigSource。
对于每个ConfigProperty,调用DatabaseConfigSource中的getPropertyValue
函数。由于我不想从每个配置属性的数据库中检索参数,因此我将配置属性解析移动到请求范围的bean。
@RequestScoped
public class ConfigParameterProvider {
@Inject
private ConfigParameterDao configParameterDao;
private Map<String, String> configParameters = new HashMap<>();
@PostConstruct
public void init() {
List<ConfigParameter> configParams = configParameterDao.findAll();
configParameters = configParams.stream()
.collect(toMap(ConfigParameter::getId, ConfigParameter::getValue));
}
public String getProperty(String key) {
return configParameters.get(key);
}
}
我可以肯定将请求范围的ConfigParameterProvider更改为ApplicationScoped。但是,我们有多租户设置,需要根据请求解决参数。
正如你所看到的,这有点hacky,因为我们需要明确告诉ConfigSource,何时允许它被正确实例化(注入bean)。
我更喜欢DeltaSpike的标准化解决方案,用于在ConfigSource中使用CDI。如果您对如何正确实现这一点有任何想法,请告诉我。
答案 2 :(得分:0)
即使这篇文章已经被回答,我还是想为这个问题提出另一种可能的解决方案。
我设法通过创建一个@Signleton @Startup
EJB来从我的数据库服务中加载属性,该EJB扩展了org.apache.deltaspike.core.impl.config.BaseConfigSource
并将DAO作为委托注入,然后我将其注册到org.apache.deltaspike.core.api.config.ConfigResolver
中。
@Startup
@Singleton
public class DatabaseConfigSourceBean extends BaseConfigSource {
private static final Logger logger = LoggerFactory.getLogger(DatabaseConfigSourceBean.class);
private @Inject PropertyService delegateService;
@PostConstruct
public void onStartup() {
ConfigResolver.addConfigSources(Collections.singletonList(this));
logger.info("Registered the DatabaseConfigSourceBean in the ConfigSourceProvider ...");
}
@Override
public Map<String, String> getProperties() {
return delegateService.getProperties();
}
@Override
public String getPropertyValue(String key) {
return delegateService.getPropertyValue(key);
}
@Override
public String getConfigName() {
return DatabaseConfigSourceBean.class.getSimpleName();
}
@Override
public boolean isScannable() {
return true;
}
}
我知道为此目的创建EJB基本上会产生太大的开销,但是我认为这是一种更干净的解决方案,而不是由带有静态访问器的某些标记布尔来处理此问题。 ..