初始化时不为@ApplicationScoped 调用@PostConstruct?
@PostConstruct is not invoked for @ApplicationScoped on initialisation?
我遇到了以下问题。我正在使用 Weld
实现 CDI
.
我发现如果服务用 @ApplicationScoped
注释,那么 @PostConstruct
部分在第一次使用该服务之前不会被调用。这是重现此行为的代码:
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");
}
}
}
因此,如果 fooService.test();
被注释,则 FooService.init()
不会被调用。但是删除 @ApplicationScoped
它又可以工作了!
这对我来说似乎很奇怪,我找不到此类行为的描述。
此外,javax.inject.Provider.get()
的规范说:
Provides a fully-constructed and injected instance of T.
所以,问题是什么?它是这样设计的还是这是一个错误?对我来说更重要的是:如何绕过这个问题?我需要我的服务 @ApplicationScoped
.
您看到的是 Weld 对 bean 初始化的惰性方法。对于所有正常作用域的 bean(除了 CDI-provided 作用域中的 @Dependent
之外的任何东西),您实际上注入了一个代理,该代理将调用委托给上下文实例。在您尝试调用该代理上的任何 bean 方法之前,不会创建上下文实例。
CDI 规范不要求 bean 急切或懒惰,这是 implementation-based 选择(我不确定 Weld 文档现在是否提到了这一点)。在 Weld 的情况下,这主要是性能选择,因为其中许多 bean 将被初始化为空(例如,从未使用过)并且它会减慢 bootstrap 很多。
请注意,这不是不一致的状态,对于 Weld 提供的每个范围,它都是这样工作的。它也不与 javax.inject.Provider.get()
矛盾,因为它没有说明 @PostConstruct
必须在您取回实例之前调用。此外,您实际上获得的实例是代理实例,并且该实例已完全初始化。
所以它归结为惰性和急切初始化的一般问题,哪个更好and/or感觉更自然。
至于一个"solution":
- 您可以使用 EJB 的
@javax.ejb.Singleton
并使用 @Startup
注释。这将表现得非常像 @ApplicationScoped
所以它可能足够好 如果你在 EE 环境中 当然。
- 或者您可以在
@ApplicationScoped
bean 上创建一个虚拟 ping()
方法,并在您的应用程序启动时立即调用它。这将强制创建 bean,从而调用 @PostConstruct
- 就像您在上面的代码示例中使用 test()
方法所做的那样。
作为旁注 - 在您的示例中,构造函数上的 @Inject
注释没有用。只有带参数的构造函数才需要它。
用户@PostConstruct注解+@Observes ContainerInitialized事件
- 使用
@PostConstruct
确保实例正确初始化
@PostConstruct
public void setup() {
// will be executed before it's going to be injected somewhere
}
- 使用
ContainerInitialized
CDI 事件侦听以确保初始化过程最终将在容器启动期间完成:
private void on(@Observes ContainerInitialized event) {
// will be executed during container bootstrap
}
您可以使用其中之一或两者,具体取决于您的需要...
PostConstruct(如果需要)早于 ContainerInitialized 事件发生,但是! PostConstruct 是所谓的 Lazy,而 ContainerInitialized 事件最终将在应用 bootstrap 期间产生,并有 100% 的保证。您可以控制在使用哪个 bean 之前对其进行初始化,或者您可以确保启动过程将触发一些业务逻辑,具体取决于您的需要。
在你的例子中,使用的是二合一:
public class TestCdi {
public static void main(String[] args) {
try (WeldContainer weldContainer = new Weld().containerId("test").initialize()) {
System.out.println("Done");
}
}
@ApplicationScoped
public static class FooService {
// with @PostConstruct you will make sure
// your bean is going to be properly configure on its creation...
@PostConstruct
public void init() {
System.out.println("Post construct");
}
// with @Observes ContainerInitialized event you will make sure
// needed business logic will be executed on container startup...
private void test(@Observes ContainerInitialized event) {
System.out.println("test");
}
}
}
此致
CDI 的可扩展性很强。在另一个 Whosebug Q/A: CDI Eager Application scoped bean 中给出了一个例子,他们在其中创建了一个要使用的 @eager
注释。虽然这个其他问题在 JSF 的范围内,但答案是普遍适用的
我遇到了以下问题。我正在使用 Weld
实现 CDI
.
我发现如果服务用 @ApplicationScoped
注释,那么 @PostConstruct
部分在第一次使用该服务之前不会被调用。这是重现此行为的代码:
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");
}
}
}
因此,如果 fooService.test();
被注释,则 FooService.init()
不会被调用。但是删除 @ApplicationScoped
它又可以工作了!
这对我来说似乎很奇怪,我找不到此类行为的描述。
此外,javax.inject.Provider.get()
的规范说:
Provides a fully-constructed and injected instance of T.
所以,问题是什么?它是这样设计的还是这是一个错误?对我来说更重要的是:如何绕过这个问题?我需要我的服务 @ApplicationScoped
.
您看到的是 Weld 对 bean 初始化的惰性方法。对于所有正常作用域的 bean(除了 CDI-provided 作用域中的 @Dependent
之外的任何东西),您实际上注入了一个代理,该代理将调用委托给上下文实例。在您尝试调用该代理上的任何 bean 方法之前,不会创建上下文实例。
CDI 规范不要求 bean 急切或懒惰,这是 implementation-based 选择(我不确定 Weld 文档现在是否提到了这一点)。在 Weld 的情况下,这主要是性能选择,因为其中许多 bean 将被初始化为空(例如,从未使用过)并且它会减慢 bootstrap 很多。
请注意,这不是不一致的状态,对于 Weld 提供的每个范围,它都是这样工作的。它也不与 javax.inject.Provider.get()
矛盾,因为它没有说明 @PostConstruct
必须在您取回实例之前调用。此外,您实际上获得的实例是代理实例,并且该实例已完全初始化。
所以它归结为惰性和急切初始化的一般问题,哪个更好and/or感觉更自然。
至于一个"solution":
- 您可以使用 EJB 的
@javax.ejb.Singleton
并使用@Startup
注释。这将表现得非常像@ApplicationScoped
所以它可能足够好 如果你在 EE 环境中 当然。 - 或者您可以在
@ApplicationScoped
bean 上创建一个虚拟ping()
方法,并在您的应用程序启动时立即调用它。这将强制创建 bean,从而调用@PostConstruct
- 就像您在上面的代码示例中使用test()
方法所做的那样。
作为旁注 - 在您的示例中,构造函数上的 @Inject
注释没有用。只有带参数的构造函数才需要它。
用户@PostConstruct注解+@Observes ContainerInitialized事件
- 使用
@PostConstruct
确保实例正确初始化
@PostConstruct
public void setup() {
// will be executed before it's going to be injected somewhere
}
- 使用
ContainerInitialized
CDI 事件侦听以确保初始化过程最终将在容器启动期间完成:
private void on(@Observes ContainerInitialized event) {
// will be executed during container bootstrap
}
您可以使用其中之一或两者,具体取决于您的需要...
PostConstruct(如果需要)早于 ContainerInitialized 事件发生,但是! PostConstruct 是所谓的 Lazy,而 ContainerInitialized 事件最终将在应用 bootstrap 期间产生,并有 100% 的保证。您可以控制在使用哪个 bean 之前对其进行初始化,或者您可以确保启动过程将触发一些业务逻辑,具体取决于您的需要。
在你的例子中,使用的是二合一:
public class TestCdi {
public static void main(String[] args) {
try (WeldContainer weldContainer = new Weld().containerId("test").initialize()) {
System.out.println("Done");
}
}
@ApplicationScoped
public static class FooService {
// with @PostConstruct you will make sure
// your bean is going to be properly configure on its creation...
@PostConstruct
public void init() {
System.out.println("Post construct");
}
// with @Observes ContainerInitialized event you will make sure
// needed business logic will be executed on container startup...
private void test(@Observes ContainerInitialized event) {
System.out.println("test");
}
}
}
此致
CDI 的可扩展性很强。在另一个 Whosebug Q/A: CDI Eager Application scoped bean 中给出了一个例子,他们在其中创建了一个要使用的 @eager
注释。虽然这个其他问题在 JSF 的范围内,但答案是普遍适用的