Search code examples
dependency-injectioncdihelidon

While injecting the ConfigProperty, getting the null value in constructor, with or without @Inject over the constructor?


For Helidon MP..

I am observing some problem, in field injection, when accessed in constructor.

In below scenario, getting the null value in constructor, with or without @Inject over the constructor

The GreetingProvider class is annotate with ApplicationScoped

@Inject
@ConfigProperty(name = "app.greeting")
private String message;

@Inject //Getting the field as null with or without @Inject annotation
public GreetingProvider() {   
  LOG.debug("Message {}, message);
}

Can get the value in the event listener for ApplicationScoped.

It works fine if I used constructor based injection.

@Inject
public GreetingProvider(@ConfigProperty(name = "app.greeting") String message) {   
    this.message.set(message);
}

Shouldn't the constructor get the initialised value from the field injection?

Expecting the initialised field injected with @ConfigProperty to be accessible in constructor.

It is accessible in the method annotated with @PostConstruct or method which observes the event activated(@Observes @Initialized(ApplicationScoped.class) final Object event).


Solution

  • Field values can only be injected once the object is instantiated. Instantiating the object requires a call to the constructor. So, if you think about this, your default constructor is invoked to build the instance of the object first and only post-construction are the fields injected in the order they are declared. This is why all the @Inject fields will be null when you access them through the default constructor.

    The right way to handle this situation is through constructor injection, using @Inject on the constructor like in your second example.

    @Inject
    public GreetingProvider(@ConfigProperty(name = "app.greeting") String message) {   
        this.message.set(message);
    }
    

    Here, you are identifying the dependencies required by your class and CDI manager will ensure that the dependencies are passed into the constructor annotated with @Inject. You can now access the dependencies as formal arguments of the constructor and initialize the fields yourself.

    The method annotated @PostConstruct OTOH is invoked only once per instance, and after the constructor, field and method injections have finished. If you have some logic to be executed once per instance @PostConstruct is the ideal way.

    @PostConstruct
    public void executeOncePerInstance(){
       // You can safely access all the field & constructor-injected properties here 
    }
    

    The behaviour would be similar to constructor injection, but some container implementations of CDI might invoke the constructor more than once during the process of setting up a Java proxy for the bean.