如何从第三方为@Inject设置非CDI bean到CDI bean

时间:2015-02-10 23:39:27

标签: java cdi weld

虽然我在CDI中找到了设置@Produces(有点像工厂)或使用CDI javax.enterprise.inject.spi.Unmanaged概念的例子,但他们似乎都认为CDI将会创建关于自己的术语和生命周期的类的实例(这是有道理的)。

但是,有些情况下CDI根本无法创建实例。

例如第三方库(不是使用CDI本身),它在内部创建对象并将其提供给您。

现在,我如何才能获取这些已经实例化的对象(顺便说一句,它们是没有默认构造函数的最终对象),并使它们可供我的CDI托管bean使用?

这是一个简化的例子。

public class Foo
{
    @Inject
    private ByteBuffer buf;

    public void go()
    {
        // do something, with buffer
    }
}

public void process() {
    ByteBuffer buf = ByteBuffer.allocate(500);
    // TODO: how to add "buf" to current context?
    Foo foo = CDI.current().select(Foo.class,AnyLiteral.INSTANCE).get();
    foo.go();
}

现在,我意识到对于这个特定的示例,我可以只是传入ByteBuffer,或者为ByteBuffer设置@Produces,甚至让Foo自己制作ByteBuffer。 (所有这些都会更容易)。我之所以选择ByteBuffer,是因为它表现出与第三方图书馆相同的问题

  • 实例是有效的bean
  • 我无法控制其来源
  • 实例由库创建
  • 这些实例为final,无法包装,覆盖或代理

该用例还存在嵌套CDI引用也可以使用对此@Inject ByteBuffer buf;的访问权限的情况。

理想情况下,它希望这是一种纯粹的CDI API技术,而不是焊接或特定于实现的东西。

我也参与了自定义范围创建,认为这可能是一个解决方案,有一种@BufferScope标识start()end()实例。但是,对于CDI无法调用newInstance()produce()的对象,这些示例和文档都没有明确说明。对象实例化不在我手中,但是可以将它呈现给CDI Scope,甚至可以管理该实例的最终死亡/破坏。

4 个答案:

答案 0 :(得分:1)

代理与包装不同。一种常见的解决方法是创建一个ByteBufferHolder(在此处插入您的类),使自定义构建器流适应一个理解它在DI上下文中的bean。

答案 1 :(得分:1)

我不确定我同意CDI假设它正在构建实例的原始断言。例如,我在JSF应用程序中使用以下内容来注入当前的FacesContext实例....

import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.faces.context.FacesContext;

public class FacesContextProducer {

    @Produces @RequestScoped FacesContext getFacesContext() {
        return FacesContext.getCurrentInstance();
    }
}

我无处可创建FacesContext实例。所需要的只是可以访问外部框架创建的内容。

那么,如果有一个外部源来获取实例,只要你能得到正确的实例,那么@Produces方法应该仍然有效吗?

答案 2 :(得分:0)

这里存在一些根本性的误解。 CDI不需要构造它可以通过生产者方法提供的bean实例。如果类不可代理,则依赖范围将正常工作。您还可以为该对象创建一个包装类,并将所述类实例放在您需要的任何范围内。

答案 3 :(得分:0)

您使用生产者方法,它可以像这样简单:

@Produces
@ApplicationScoped
private Foo produceFoo() {
  final Foo instance = // acquire one of those instances you're talking about
  return instance;
}

如果由于某种原因对您不起作用,您也可以编写一个可移植的扩展名,并以编程方式添加一个Bean来满足您的需求。因此在CDI 2.0+中(未测试)是这样的:

private void afterBeanDiscovery(@Observes final AfterBeanDiscovery event) {
  final Foo instance = // acquire one of these final instances you're talking about
  event.addBean()
    .scope(ApplicationScoped.class)
    .qualifiers(whateverYouNeed)
    .addTransitiveTypeClosure(instance.getClass())
    .createWith(cc -> instance) // you may also need .destroyWith()
    ;
}

一旦设置了“生产方”,就可以在任何CDI bean中进行以下操作:

@Inject
private Foo foo;