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
.
The PostConstruct annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization. This method MUST be invoked before the class is put into service. This annotation MUST be supported on all classes that support dependency injection.
When we annotate a method in Spring Bean with @PostConstruct annotation, it gets executed after the spring bean is initialized. We can have only one method annotated with @PostConstruct annotation. This annotation is part of Common Annotations API and it's part of JDK module javax.
Methods marked with the @PostConstruct will be invoked after the bean has been created, dependencies have been injected, all managed properties are set, and before the bean is actually set into scope.
2. @PostConstruct. Spring calls the methods annotated with @PostConstruct only once, just after the initialization of bean properties.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With