简体   繁体   中英

Pass Parameter to Instance of @Inject Bean

I am using CDI as injection framework, but I have found some limitations in its usage, and this is one of them. I am trying to initialize the creation of a bean instance with runtime values. Example:

@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"
   }
}

Please, notice that "a" is a constant in the example, but in practice it is a variable; I clarify this so you don't post an answer with a @Producer to inject the value then in the constructor of MyNumber .

Now, anybody has an idea about how can I do that?

I'm not sure what you're trying to do, but from what I understand you want to initialize your bean with data in injection point annotation or at runtime thru programmatic lookup. You can do this by using InjectionPoint meta data in your bean (the only constraint will be to put your bean in dependent scope)

You can do something like this.

First create a qualifier with a non binding value.

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

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

You have to add this qualifier on your bean and analyze InjectionPoint at creation time.

@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;
   }
}

You can now inject an initialized number like this:

@Inject
@Initialized(8)
MyNumber number;

If you want to decide the initialization value at runtime, you'll have to use programmatic lookup:

First create the annotation literal for `@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;
    }
}

Then you can use Instance to create your bean.

public class ConsumingBean {

    @Inject
    @Any
    Instance<MyNumber> myNumberInstance;

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

Remember this works only if MyNumber is in dependent scope which makes sense because it's the only way to change initialization value at each injection.

According to the specification, there is no way to have a bean with a not simple, "not injecting" constructor ( 3.1. Managed beans , 3.9. Bean constructors ).

Therefore, the ways to set the parameters are to have the setMethod() for them, make them as @Inject fields in the bean or annotate the constructor with the annotation @Inject and make the parameters of the constructor to be injectable ( 5.5.1. Injection using the bean constructor )

I hope, I answered the question.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM