4

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).

SidM
  • 43
  • 4

1 Answers1

4

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.

Ashwin Prabhu
  • 9,285
  • 5
  • 49
  • 82
  • 1
    Thanks for detailed clarification. Also, one more thing to add after going through the CDI docs is, by default its lazy init, so for controller class (the ones exposing REST apis, but are never injected) it is better to go with event observation`@Observes @Initialized(ApplicationScoped.class...)`, since `@PostConstruct` may only be called after bean is injected first time. – SidM Apr 04 '23 at 09:42