I have faced with the following issue. I am using Weld
implementation of the CDI
.
I have found that if a service is annotated with @ApplicationScoped
then @PostConstruct
section is not invoked until the first usage of the service. Here is a code to reproduce this behaviour:
import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.spi.CDI;
public class TestCdi {
public static void main(String[] args) {
try (WeldContainer weldContainer = new Weld().containerId("test").initialize()) {
FooService fooService = CDI.current().select(FooService.class).get();
fooService.test();
System.out.println("Done");
}
}
@ApplicationScoped
public static class FooService {
@PostConstruct
public void init() {
System.out.println("Post construct");
}
public void test() {
System.out.println("test");
}
}
}
So, if fooService.test();
is commented, then FooService.init()
is not invoked. But remove @ApplicationScoped
and it is working again!
This seems strange for me and I can't find and description of such behaviour.
Furthermore, the specification of javax.inject.Provider.get()
says that:
Provides a fully-constructed and injected instance of T.
So, what's the issue? Is it designed so or this is a bug? And what is more important for me: how to bypass this issue? I need my service to be @ApplicationScoped
.
What you are seeing is Weld's lazy approach to bean initialization. With all normal scoped beans (anything except @Dependent
from CDI-provided scopes), you in fact inject a proxy which delegates calls to contextual instance. And until you try to invoke any bean method on that proxy, the contextual instance is not created.
CDI specification does not mandate beans to be eager or lazy, this is implementation-based choice (I am not sure whether Weld docs mention this now). In case of Weld this is mainly performance choice as many of those beans would be initialized for nothing (never used, for instance) and it would slow down bootstrap a lot.
Please note that this is not an inconsistent state, it works like this for every scope Weld provides. It is also not a contradiction to javax.inject.Provider.get()
as it does not state that @PostConstruct
has to be invoked before you get the instance back. Furthermore the instance you in fact get is the proxy instance and that one is fully initialized anyway.
So it boils to to general problem of lazy versus eager init and which is better and/or which feels more natural.
As for a "solution":
@javax.ejb.Singleton
and use @Startup
annotation. This will behave pretty much like @ApplicationScoped
would so it might be good enough if you are in EE environment of course.ping()
method on your @ApplicationScoped
bean and invoke it as soon as your application starts. This will force the creation of the bean hence invoking @PostConstruct
- much like you did with test()
method in your code sample above.As a side note - in your example the @Inject
annotation on your constructor is of no use. It is only required for constructors with params.