通用 CDI 生产者方法未按预期工作

发布于 2024-10-05 03:59:40 字数 787 浏览 0 评论 0原文

我有一个 CDI 生产者方法 - 根据与此示例无关的某些条件 - 创建不同类型的对象:

public class TestProducer {

  @Produces @TestQualifier
  public Object create(InjectionPoint ip) {
    if(something) {
      return "a String";
    } else {
      return Integer.valueOf(42);
    }
  }

但是在使用此生产者时,我总是在以下情况下收到错误:

@Named("test")
public class TestComponent {
   ...
   @Inject public void setA(@TestQualifier String stringValue) {
   ...
   @Inject public void setB(@TestQualifier Integer integerValue) {

它仅在生产者的 create 方法时有效方法签名中具有预期的类型:

public class TestProducer {

  @Produces @SpringBean
  public String create(InjectionPoint ip) {

现在字符串已正确注入,但我无法从生产者方法中生成整数。但这正是我想要避免的,因为生产者本身应该是完全通用的。

我做错了什么或者没有办法实现我想要的行为吗?

I have a CDI producer method which - depending on some conditions not relevant to this example - creates objects of different types:

public class TestProducer {

  @Produces @TestQualifier
  public Object create(InjectionPoint ip) {
    if(something) {
      return "a String";
    } else {
      return Integer.valueOf(42);
    }
  }

but when using this producer, I always get an error in the followin situation:

@Named("test")
public class TestComponent {
   ...
   @Inject public void setA(@TestQualifier String stringValue) {
   ...
   @Inject public void setB(@TestQualifier Integer integerValue) {

It only works when the create method of the producer has the expected type in the method signature:

public class TestProducer {

  @Produces @SpringBean
  public String create(InjectionPoint ip) {

Now the String get's injected correctly, but I have no way to also generate an integer from the producer method. But this is exactly what I want to avoid, since the producer itself should be completely generic.

Am I doing something wrong or is there no way to achieve the behaviour I want?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

皓月长歌 2024-10-12 03:59:40

所有 CDI 文档都清楚地表明 CDI 执行类型安全依赖注入 - 并且它是 CDI 的一个尊贵属性。恕我直言,您想要做的正是 CDI 试图避免的。您希望容器将 Object 转换为每种类型,而 CDI 无法以这种方式工作。

注入点 stringValueintegerValue 只能接收具有 java.lang.Stringjava.lang.Integer 的 bean code> 在其 bean 类型 分别。 java.lang.Object 不满足此标准。

我有两个建议。首先,由于您有两个或多个不同类型的注入点,因此请为该类型创建两个或多个生成器方法:

public class TestProducer {

  @Produces @TestQualifier
  public String createString(InjectionPoint ip) {
    if(something) {
      return "a String";
    } else {
      // Some other value
    }
  }

  @Produces @TestQualifier
  public int createInt(InjectionPoint ip) {
    if(something) {
      return 42;
    } else {
      // Some other value
    }
  }
// ...

如果 something 条件只是检查注入点的类型(我的意思),那么它就可以工作。我打赌就是这样)。

但是,如果 something 条件确实使用注入点类型之外的其他条件决定类型,我建议您自己做“肮脏的工作”:将返回值注入 对象类型注入点并手动进行转换:

@Named("test")
public class TestComponent {
   ...
   @Inject public void setA(@TestQualifier Object value) {
       String stringValue = (String) value;

   ...
   @Inject public void setB(@TestQualifier Object value) {
       int intValue = (Integer) value;

要点是,与其他一些 DI 框架不同,CDI 不能针对 Java 类型系统工作 - 相反,它大量使用它。不要试图对抗它,而是利用 CDI 的这一方面对你有利:)

All CDI documentation makes it clear that CDI does typesafe dependency injection - and it is an exalted property of CDI. IMHO, what you are trying to do is just what CDI tries to avoid. You want the container to cast Object to each type and CDI does not work that way.

The injections points stringValue and integerValue can only receive a bean which has java.lang.String and java.lang.Integer in its list of bean types respectively. java.lang.Object does not satisfy this criterion.

I have two suggestions. First, since you have two or more injection points of different types, create two or more producer methods for that types:

public class TestProducer {

  @Produces @TestQualifier
  public String createString(InjectionPoint ip) {
    if(something) {
      return "a String";
    } else {
      // Some other value
    }
  }

  @Produces @TestQualifier
  public int createInt(InjectionPoint ip) {
    if(something) {
      return 42;
    } else {
      // Some other value
    }
  }
// ...

It works if the something condition is just to check the type of the injection point (what I am betting is the case).

However, if the something condition does decide the type using other criteria than the type of the injection point, I'd suggestion to do the "dirty job" yourself: inject the returned value in an Object-typed injection point and does the cast manually:

@Named("test")
public class TestComponent {
   ...
   @Inject public void setA(@TestQualifier Object value) {
       String stringValue = (String) value;

   ...
   @Inject public void setB(@TestQualifier Object value) {
       int intValue = (Integer) value;

The main point is that, unlike some other DI frameworks, CDI does not work against the Java type system - on the contrary, it heavily uses it. Do not try to fight against it but use this aspect of CDI in your favor :)

叹沉浮 2024-10-12 03:59:40

无论如何,Object 的生产者很奇怪。我不确定这是否是规范所禁止的,或者这是一个错误,但我认为您可以采取一些巧妙的解决方法:

public class ValueHolder<T> {
    private T value;

    public T getValue() {
        return value;
    }
}

然后注入 ValueHolderValueHolder;

A producer for Object is strange anyway. I'm not sure if this is forbidden by the spec, or it's a bug, but I think you can make some clever workaround:

public class ValueHolder<T> {
    private T value;

    public T getValue() {
        return value;
    }
}

And then inject a ValueHolder<String> and ValueHolder<Integer>

櫻之舞 2024-10-12 03:59:40

它可以使用 CDI 创建通用对象,如下所示:

  // the wrapper class
    public class Wrapper<T> {
      public final T bean;
      public Wrapper(T bean){
        this.bean = bean;
      }
    }

    // the producer inside some class
    @Produces
    public <T> Wrapper<T> create(InjectionPoint p){
      // with parameter 'p', it is possible retrieve the class type of <T>, at runtime
    }


    // the bean example 1
    public class BeanA {
      public void doFoo(){
        // ...
      }
    }
    // the bean example 2
    public class BeanB {
      public void doBar(){
        // ...
      }
    }


    // the class that uses the produced beans
    public class SomeBean{

//// There on producer method, do you can retrieve the Class object of BeanA and BeanB, from type parameters of Wrapper.

      @Inject
      private Wrapper<BeanA> containerA;
      @Inject
      private Wrapper<BeanB> containerB;

      public void doSomeThing(){
         containerA.doFoo();
         containerB.doBar();
      }

    }

适用于焊接 2.2.0。
我认为这也适用于某些以前的版本。

Its possible create generic objects with CDI produces like that:

  // the wrapper class
    public class Wrapper<T> {
      public final T bean;
      public Wrapper(T bean){
        this.bean = bean;
      }
    }

    // the producer inside some class
    @Produces
    public <T> Wrapper<T> create(InjectionPoint p){
      // with parameter 'p', it is possible retrieve the class type of <T>, at runtime
    }


    // the bean example 1
    public class BeanA {
      public void doFoo(){
        // ...
      }
    }
    // the bean example 2
    public class BeanB {
      public void doBar(){
        // ...
      }
    }


    // the class that uses the produced beans
    public class SomeBean{

//// There on producer method, do you can retrieve the Class object of BeanA and BeanB, from type parameters of Wrapper.

      @Inject
      private Wrapper<BeanA> containerA;
      @Inject
      private Wrapper<BeanB> containerB;

      public void doSomeThing(){
         containerA.doFoo();
         containerB.doBar();
      }

    }

Works on weld 2.2.0.
I think that works on some previous versions as well.

茶色山野 2024-10-12 03:59:40

您的初始化方法将查找具有 API 类型 String 和 Integer 的托管 bean,但您的生产者方法 bean 仅具有 API 类型(在生产者方法的情况下,返回类型)对象。

因此,您只能在初始化方法注入字段中使用 Object,然后区分接收者主体的 int 类型,或者简单地将它们和生产者方法包装在可以返回 Strings 或 Int 的实际类型中(但我会避免仿制药)

Your initializer methods will look for a managed bean with API types String and Integer, but your producer method bean only has API type (in case of producer method, return type) Object.

You can therefore only use Object in your initializer method injected fields and then discriminate between the types int the body of the receiver, or simply wrap them and the producer method in an actual type that can return Strings or Int (but I'd avoid the generics)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文