Pass Parameter to Instance of @Inject Bean

Я использую CDI в качестве рамки для инъекций, но я нашел некоторые ограничения в ее использовании, и это один из них. Я пытаюсь инициализировать создание экземпляра bean со значениями runtime. Пример:

@RequestScoped
public class MyNumber {
   int number;

   public MyNumber(int number) {
      this.number = number;
   }

   public String toString() {
      return "Your number is: " + number;
   }
}

public class UseNumber {
   @Inject
   Instance<MyNumber> number;

   public void doStuff() {
      int a = 8;
      MyNumber mN = number.select(a).get(); // ?? - Obviously this does not work.

      System.out.print(mN); // Should print: "Your number is: 8"
   }
}

Обратите внимание, что "a" является константой в примере, но на практике это переменная; Я уточняю это, поэтому вы не отправляете ответ с помощью @Producer, чтобы ввести значение, а затем в конструктор MyNumber.

Теперь у кого-нибудь есть представление о том, как я могу это сделать?

Ответ 1

Я не уверен, что вы пытаетесь сделать, но из того, что я понимаю, вы хотите инициализировать свой bean данными в аннотации точки вставки или во время выполнения через программный поиск. Вы можете сделать это, используя метаданные InjectionPoint в вашем bean (единственным ограничением будет размещение вашего bean в зависимой области)

Вы можете сделать что-то вроде этого.

Сначала создайте квалификатор с необязательным значением.

@Qualifier
@Target({TYPE, METHOD, PARAMETER, FIELD})
@Retention(RUNTIME)
@Documented
public @interface Initialized {

    @Nonbinding int value() default 0; // int value will be store here 
}

Вы должны добавить этот классификатор на свой bean и проанализировать InjectionPoint во время создания.

@Initialized
public class MyNumber {
   int number;

   private int extractValue(InjectionPoint ip) {
    for (Annotation annotation : ip.getQualifiers()) {
        if (annotation.annotationType().equals(Initialized.class))
            return ((Initialized) annotation).value();
    }
    throw new IllegalStateException("No @Initialized on InjectionPoint");
  }

   @Inject
   public MyNumber(InjectionPoint ip) {
      this.number = extractValue(ip);
   }

   public String toString() {
      return "Your number is: " + number;
   }
}

Теперь вы можете ввести инициализированный номер следующим образом:

@Inject
@Initialized(8)
MyNumber number;

Если вы хотите определить значение инициализации во время выполнения, вам придется использовать программный поиск:

Сначала создайте литерал аннотации для `@Initialized``

public class InitializedLiteral extends AnnotationLiteral<Initialized> implements Initialized {

    private int value;

    public InitializedLiteral(int value) {
        this.value = value;
    }

    @Override
    public int value() {
        return value;
    }
}

Затем вы можете использовать Instance для создания bean.

public class ConsumingBean {

    @Inject
    @Any
    Instance<MyNumber> myNumberInstance;

    public MyNumber getMyNumberBeanFor(int value) {
     return myNumberInstance.select(new InitializedLiteral(value)).get();
    }
    ...
}

Помните, что это работает, только если MyNumber находится в зависимой области видимости, что имеет смысл, поскольку это единственный способ изменить значение инициализации при каждой инъекции.

Ответ 2

В соответствии со спецификацией нет способа иметь bean с не простым конструктором "без инъецирования" (3.1. Управляемый beans, 3.9. bean конструкторы).

Следовательно, способы установки параметров состоят в том, чтобы иметь для них setMethod(), сделать их как поля @Inject в bean или аннотировать конструктор с помощью аннотации @Inject и сделать параметры конструктора (5.5.1 Инъекция с использованием конструктора bean)

Надеюсь, я ответил на вопрос.