Общий способ создания CDI не работает должным образом - PullRequest
4 голосов
/ 29 ноября 2010

У меня есть метод производителя 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) {

Теперь String get введен правильно, но у меня нет способа также сгенерировать целое число из метода продюсера. Но это именно то, чего я хочу избежать, так как сам производитель должен быть полностью универсальным.

Я что-то делаю не так или нет способа добиться желаемого поведения?

Ответы [ 4 ]

4 голосов
/ 07 июля 2011

Вся документация CDI разъясняет, что CDI делает типобезопасным внедрение зависимостей - и это возвышенное свойство CDI.ИМХО, то, что вы пытаетесь сделать, это то, что CDI пытается избежать.Вы хотите, чтобы контейнер передавал Object каждому типу, а CDI не работает таким образом.

Точки инъекции stringValue и integerValue могут получать только бин, имеющий java.lang.String и java.lang.Integerв его списке типы бинов соответственно.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 определяет тип с использованием других критериев, а не типа точки ввода, я бы предложил выполнить «грязную работу» самостоятельно: ввести возвращаемое значение в Object вводит точку ввода и выполняет приведение вручную:

@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 в свою пользу:)

4 голосов
/ 29 ноября 2010

Производитель для Object все равно странный.Я не уверен, если это запрещено спецификацией, или это ошибка, но я думаю, что вы можете сделать какой-то умный обходной путь:

public class ValueHolder<T> {
    private T value;

    public T getValue() {
        return value;
    }
}

А затем ввести ValueHolder<String> и ValueHolder<Integer>

2 голосов
/ 12 декабря 2014

Возможно создание общих объектов с помощью 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. Я думаю, что это работает и в некоторых предыдущих версиях.

1 голос
/ 08 января 2011

Ваши методы инициализатора будут искать управляемый bean-компонент с типами API String и Integer, но у вашего bean-компонента метода-производителя есть только API-тип (в случае метода-производителя, тип возвращаемого значения) Object.

Таким образом, вы можете использовать Object только в полях, введенных методом инициализатора, а затем различать типы в теле получателя или просто обернуть их и метод источника в фактический тип, который может возвращать Strings или Int (но я буду избегать дженериков)

...