有没有办法重新注入/更新注入的bean字段?

时间:2016-06-23 13:43:12

标签: java java-ee dependency-injection cdi weld

我有一些bean(多种类型,CDI,@Stateless@Singleton bean)。他们的一些字段将从数据库值注入。

public class MyBean {

    @Inject
    @DbConfigValue(MyConfig.HOST)
    String host;
}

所以我添加了制作人使用的自定义@QualifierDbConfigValue)。生产者从数据库中读取和缓存配置值,并将它们注入到bean中。

@Singleton
@Lock(LockType.READ)
public class Configuration {

    @Produces
    @Dependent
    @DbConfigValue
    public String getDbConfigValue(InjectionPoint point) {
        // get key for the config-value from qualifier-annotation of the injected field
        String key = point.getAnnotated().getAnnotation(DbConfigValue.class).value();
        // i have read+cached database config values in @PostConstruct before
        return cachedConfigValues.get(key);
    }
}

这适用于初始注入/ bean构建。一些网络教程提出了这种方法。

现在,我认为假设配置值(如果存储在DB中)可能在运行时更改是合理的。因此,每当管理员更改数据库配置值时,我当前都会触发CDI事件。

问题:有没有办法将值重新注入已初始化的bean实例的字段中?或者注入始终只与实例创建有关吗?

E.g。我有s.th.与此类似:

public class MyEventListener {

    @Inject
    BeanManager beanManager;

    @Asynchronous
    public void onDbConfigValueChangedEvent (@Observes(during = TransactionPhase.AFTER_SUCCESS) DbConfigValueChangedEvent event) {
        try {
            // could be filtered by custom qualifier:
            Set<Bean<?>> beans = beanManager.getBeans(Object.class,new AnnotationLiteral<Any>() {});

            for (Bean<?> bean : beans) {
                Set<InjectionPoint> points = bean.getInjectionPoints();
                // What now? javax.enterprise.inject.spi.Bean is the 
                // bean-representation only. 
                // Can I somehow resolve the actual bean-instances here?
                // Then update Field via Reflection?
            }
        }
        catch(Exception e){
            // ...
        }
    }
}

我还考虑了DeltaSpike,它有一些注射控制方法。但是,我只找到了注入新bean实例的方法,甚至只使用new或null-CreationalContexts(之后没有CDI管理的bean)

请注意:我知道我可以通过注入配置并在每个请求中显式获取当前值来解决此特定用例:

public class MyBean {

    @Inject
    Configuration config;

    public void someMethod(){
        String host = config.getConfig(MyConfig.HOST);
        // ...
    }
}

但是,我对这个问题感到疑惑:是否有重新注射的支持?或者如果没有,那么规范(CDI或Java EE)是否禁止它?

2 个答案:

答案 0 :(得分:1)

根据数据库的速度/速度有多快,这可能会很昂贵。您可以在producer方法中利用一些缓存机制。 利用private void LogFileNames() { //There may be more than one node at root level foreach (TreeNode rootNode in FileTreeView.Nodes) { //Print only filenames, not directories foreach (TreeNode leafNode in LeafNodes(rootNode)) { Logger.Info(leafNode.Text); } } } 注入机制,懒惰地加载实际注入的bean。

你的制作人(可能利用一些缓存来避免db调用所有的大部分内容)

Instance

注射点:

@Singleton
@Lock(LockType.READ)
public class Configuration {

    @Produces
    @RequestScoped //May fail if not in a request-context, but for ejb-calls, it is guaranteed to work as CDI has EJB Request Context
    @DbConfigValue
    public String getDbConfigValue(InjectionPoint point) {
        // get key for the config-value from qualifier-annotation of the injected field
        String key = point.getAnnotated().getAnnotation(DbConfigValue.class).value();
        // i have read+cached database config values in @PostConstruct before
        return cachedConfigValues.get(key);
    }
}

答案 1 :(得分:0)

不支持重新注射。 CDI具有静态特性,这意味着一切都是在启动时确定的,并且之后保持不变。

它是这样设计的,并且不能引入再注入(或任何其他动态的东西),因为它会破坏整个结构。例如,bean验证在引导时执行,这是一项耗时的工作。重新注入会引入重复验证的需要。

建议解决您的问题:

您可能需要查看自定义范围,您可以在其中控制范围的生命周期。要拥有一个范围,您还需要一个Context来实现这个范围(这是您实现的地方)。为什么这一切?好吧,如果你可以在事件观察结束范围的生命周期并创建一个新的生命周期,那也意味着它再次调用生产者并创建一个新的引用,最终用新的更新对象奖励你。