英文:
ApplicationScoped beans in Java EE with CDI
问题
ApplicationScoped
beans 会在注入点创建还是在应用部署时创建?
英文:
When the ApplicationScoped beans will be created ?
At injection points or when application will be deployed ?
答案1
得分: 1
应用范围(ApplicationScoped)的Bean是懒加载的,就像所有的CDI(Contexts and Dependency Injection)Beans一样。它们在使用时被物理创建。然而,注入点的验证是在启动时进行的,通过构建一个图并确保该图不违反CDI规范中的任何规则。
请记住,所有的注入点实际上都是通过代理Bean注入的。代理Bean只是实际类的子类,在JVM启动时动态定义。当你在代理上调用函数时,它会找到或创建Bean的实际实例,然后在后台将你的调用路由到Bean上。
整个过程非常迷人,值得用调试器进行探索。你可以在你的ApplicationScoped Bean的构造函数中设置断点,并沿着调用堆栈走到你的调用点,看看发生了什么。
你可以使用以下代码强制初始化一个应用范围的Bean:
@ApplicationScoped
public class MyService {
void init(@Observes @Initialized(ApplicationScoped.class) Object event) {
// 无操作
}
}
编辑
**:虽然该说法是正确的,但在范围变得活跃的几种令人惊讶的情况下,我的答案并不完全准确...但嘿,我们都在这里学习。@ApplicationScoped
Beans确实是懒加载的,但在实际情况中,它们可能会表现得好像它们被立即初始化了。
事实上,容器在几种令人惊讶的情况下激活了ApplicationScoped
上下文。我去读了CDI 2.0规范中的第20.3.3节“Java EE中的应用上下文生命周期”:
应用范围处于活动状态:
* 在Web应用程序中的任何Servlet的service()方法中,
* 在任何Servlet过滤器的doFilter()方法中,当容器调用任何ServletContextListener、HttpSessionListener、AsyncListener或ServletRequestListener时,
* 在任何Java EE Web服务调用期间,
* 在任何EJB的远程方法调用期间,
* 在任何EJB的异步方法调用期间,
* 在调用任何EJB的超时方法和将消息传递给任何EJB消息驱动Bean时,
* 当调用具有任何正常范围(而不是@ApplicationScoped)的Bean的清理方法或@PreDestroy回调时,
* 在任何Bean的@PostConstruct回调期间。
此外,你可能会看到你的构造函数被调用了两次。据我所读,在CDI 2.0规范中,代理的创建并没有明确禁止调用类的构造函数。
我的原始来源在这里:https://www.cdi-spec.org/faq/
英文:
ApplicationScoped beans are lazily created, like all CDI beans. They are physically created when used. **see edit below
However, validation of injection points happens at boot time by construction a graph and ensuring said graph does not violate any of the rules in the CDI specification.
Remember that all injection points are actually injected with a proxy bean. A proxy bean is nothing but a subclass of the actual class, that was dynamically defined in the JVM when it booted. When you invoke a function on the proxy, it then finds or creates the actual instance of the bean and then routes your invocation to the bean in the background.
The whole process is pretty fascinating, and it's worth exploring with your debugger. You can set a breakpoint in the constructor of your ApplicationScoped bean and walk up the call stack to your invocation point to see what's happening.
You can force Eager init of an ApplicationScoped bean with the following:
@ApplicationScoped
public class MyService {
void init(@Observes @Initialized(ApplicationScoped.class) Object event) {
// noop
}
}
EDIT
**: While the statement is true, there are several surprising cases in which the scope becomes active and my answer was not fully complete... but hey we're all here to learn. @ApplicationScoped
beans are indeed lazy, but in practical terms they'll probably behave as if they're initialized immediately.
It turns out the container activates the ApplicationScoped
context in several surprising cases. I went and read Section 20.3.3. Application context lifecycle in Java EE
of the CDI 2.0 Spec:
The application scope is active:
* during the service() method of any servlet in the web application,
* during the doFilter() method of any servlet filter and when the container calls any ServletContextListener, HttpSessionListener, AsyncListener or ServletRequestListener,
* during any Java EE web service invocation,
* during any remote method invocation of any EJB,
* during any asynchronous method invocation of any EJB,
* during any call to an EJB timeout method and during message delivery to any EJB message-driven bean,
* when the disposer method or @PreDestroy callback of any bean with any normal scope other than @ApplicationScoped is called, and
* during @PostConstruct callback of any bean.
In addition, you may actually see your constructor invoked twice. The creation of a proxy is not explicitly forbidden to invoke the constructor of a class as far as I read in the CDI 2.0 spec.
My original source was here: https://www.cdi-spec.org/faq/
A CDI implementation may choose to lazily instantiate a normal scoped bean.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论