Java DeltaSpike自定义配置源与CDI

Java DeltaSpike自定义配置源与CDI,java,cdi,deltaspike,Java,Cdi,Deltaspike,我正在尝试定义自定义DeltaSpike配置源。自定义配置源将具有最高优先级,并检查数据库中的config参数 我有一个ConfigParameter实体,它只有一个键和一个值 @Entity @Cacheable public class ConfigParameter ... { private String key; private String value; } 我有一个查找所有配置参数的@DependentDAO 我现在要做的是定义一个自定义ConfigS

我正在尝试定义自定义DeltaSpike配置源。自定义配置源将具有最高优先级,并检查数据库中的config参数

我有一个ConfigParameter实体,它只有一个键和一个值

@Entity
@Cacheable
public class ConfigParameter ... {

      private String key;
      private String value;

}
我有一个查找所有配置参数的
@Dependent
DAO

我现在要做的是定义一个自定义ConfigSource,它能够从数据库中获取config参数。因此,我想将我的DAO注入ConfigSource。基本上是这样的

@ApplicationScoped
public class DatabaseConfigSource implements ConfigSource {

    @Inject
    private ConfigParameterDao configParameterDao;

    ....
}
但是,当通过META-INF/services/org.apache.deltaspike.core.spi.config.ConfigSource注册ConfigSource时,该类将被实例化,CDI将无法工作

有没有办法让CDI在这种情况下工作


提前感谢,如果您需要任何进一步的信息,请告诉我。

DS正在为此使用java se机制,该机制不是CD“可注入的”。一种解决方案是使用获取数据库ConfigSource并将操作委托给它。

主要问题是,当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中注入配置类时,将解析每个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);
    }

}
@RequestScoped
公共类ConfigParameterProvider{
@注入
私有ConfigParameterDao ConfigParameterDao;
私有映射configParameters=newhashmap();
@施工后
公共void init(){
List configParams=configParameterDao.findAll();
configParameters=configParams.stream()
.collect(toMap(ConfigParameter::getId,ConfigParameter::getValue));
}
公共字符串getProperty(字符串键){
返回configParameters.get(key);
}
}
我肯定可以将请求范围内的ConfigParameterProvider更改为ApplicationScoped。但是,我们有一个多租户设置,每个请求都需要解析参数

正如您所看到的,这有点粗糙,因为我们需要显式地告诉ConfigSource何时允许正确实例化(注入bean)


我更喜欢DeltaSpike的标准化解决方案,以便在配置源中使用CDI。如果您对如何正确实现这一点有任何想法,请让我知道。

即使这篇文章已经得到了回答,我还是想为这个问题提出另一种可能的解决方案

我通过创建一个扩展
org.apache.deltaspike.core.impl.config.BaseConfigSource
@Signleton@Startup
EJB,从我的db服务加载属性,并将我的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;
    }
}
@启动
@独生子女
公共类DatabaseConfigSourceBean扩展了BaseConfigSource{
私有静态最终记录器Logger=LoggerFactory.getLogger(DatabaseConfigSourceBean.class);
private@injectpropertyservice delegateService;
@施工后
启动时公共无效(){
ConfigResolver.addConfigSources(Collections.singletonList(this));
info(“在ConfigSourceProvider中注册了DatabaseConfigSourceBean…”);
}
@凌驾
公共映射getProperties(){
返回delegateService.getProperties();
}
@凌驾
公共字符串getPropertyValue(字符串键){
返回delegateService.getPropertyValue(键);
}
@凌驾
公共字符串getConfigName(){
返回DatabaseConfigSourceBean.class.getSimpleName();
}
@凌驾
公共布尔值可扫描(){
返回true;
}
}

我知道,为此目的创建EJB基本上会产生太大的开销,但是我认为这是一个更干净的解决方案,而不是通过一些带有静态访问器的标记布尔来处理这个问题…

在beanprovider初始化之前,配置源会被初始化。因此,当尝试在ConfigSource中使用Beanprovider时,会引发一个异常,即没有可用的提供程序。好的,但是如果在GetProperty/getProperties方法中访问Beanprovider,不是很好吗?这两个属性是在init上调用的吗?
getProperty
函数是在应用程序/容器启动的不同阶段调用的。DS在尝试解析我的自定义配置属性之前,首先尝试获取许多内部属性。在第一次“运行”期间,BeanProvider不可用。根本不调用
getProperties
函数。当CDIBeans完成初始化时,我需要初始化ConfigSource或任何类似的东西。您仍然可以改进
initifnequired()
-目前,它的工作方式与
initifnequired()
,即字段不会很快被注入,但它们确实会在ev上被注入