带 CDI 的 DeltaSpike 自定义配置源

DeltaSpike custom ConfigSource with CDI

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

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

@Entity
@Cacheable
public class ConfigParameter ... {

      private String key;
      private String value;

}

我有一个 @Dependent DAO 可以找到所有配置参数。

我现在要做的是定义一个自定义的 ConfigSource,它能够从数据库中获取配置参数。因此,我想在 ConfigSource 中注入我的 DAO。所以基本上就像

@ApplicationScoped
public class DatabaseConfigSource implements ConfigSource {

    @Inject
    private ConfigParameterDao configParameterDao;

    ....
}

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

在这种情况下有什么方法可以使 CDI 正常工作吗?

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

DS 正在使用 java se spi mechanism for this which is not CD'Injectable'. One solution would be to use the BeanProvider 获取您的 DatabaseConfigSource 并将操作委托给它。

主要问题是,当 BeanManager 尚不可用时,ConfigSource 很早就被实例化了。甚至 JNDI 查找在那个时间点也不起作用。因此,我需要延迟 injection/lookup.

我现在所做的是向我的配置源添加一个静态布尔值,这是我手动设置的。我们有一个 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 中注入配置 class 时,将解析每个 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 的 standarized 解决方案,以便在 ConfigSource 中使用 CDI。如果您对如何正确实现这一点有任何想法,请告诉我。

尽管这个 post 已经得到解答,但我还是想为这个问题提出另一种可能的解决方案。

我设法通过创建 @Signleton @Startup 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 基本上会产生太大的开销,但是我认为这是一个更简洁的解决方案,而不是通过一些标记来处理这个问题具有静态访问器的布尔值 ...