-
Available since OmniFaces 1.8
The CDI annotation @
Eager
specifies that a scoped bean is to be eagerly instantiated.
Faces native managed beans have been deprecated in favor of CDI managed beans. One feature that those Faces native managed beans had that's not directly available for CDI managed beans is the ability to eagerly instantiate application scoped beans.
OmniFaces fills this void and even goes one step further by introducing the @Eager
annotation that can be applied to @RequestScoped
, @ViewScoped
, @SessionScoped
and @ApplicationScoped
beans. This causes these beans to be instantiated automatically at the start of each such scope instead of on demand when a bean is first referenced.
In case of @RequestScoped
and @ViewScoped
beans instantiation happens per request URI / view and an extra attribute is required for specifying this.
Supported scopes
Currently supported scopes:
- CDI
RequestScoped
- CDI
ViewScoped
- OmniFaces
ViewScoped
- CDI
SessionScoped
- CDI
ApplicationScoped
Usage
E.g. The following bean will be instantiated during application's startup:
@Eager
@ApplicationScoped
public class MyEagerApplicationScopedBean {
@PostConstruct
public void init() {
System.out.println("Application scoped init!");
}
}
Note: you can also use the stereotype @
Startup
for this instead.
The following bean will be instantiated whenever a session is created:
@Eager
@SessionScoped
public class MyEagerSessionScopedBean implements Serializable {
private static final long serialVersionUID = 1L;
@PostConstruct
public void init() {
System.out.println("Session scoped init!");
}
}
The following bean will be instantiated whenever the URI /components/cache
(relatively to the application root) is requested, i.e. when an app is deployed to /myapp
at localhost this will correspond to a URL like https://example.com/myapp/components/cache
:
@Eager(requestURI = "/components/cache")
@RequestScoped
public class MyEagerRequestScopedBean {
@PostConstruct
public void init() {
System.out.println("/components/cache requested");
}
}
FacesContext
in @PostConstruct
When using @Eager
or @Eager(requestURI)
, be aware that the FacesContext
is not available in the @PostConstruct
. Reason is, the FacesServlet
isn't invoked yet at the moment @Eager
bean is constructed. Only in @Eager(viewId)
, the FacesContext
is available in the @PostConstruct
.
In case you need information from HttpServletRequest
, HttpSession
and/or ServletContext
, then you can just @Inject
it right away. Also, all other CDI managed beans are available the usual way via @Inject
, as long as they do also not depend on FacesContext
in their @PostConstruct
.
CDI issues in EAR
Note that CDI has known issues when the same web fragment library is bundled in multiple WARs in a single EAR and the CDI feature is based on an Extension
.
It's important to understand that those issues are not related to OmniFaces, but to the CDI spec.
For an overview of those issues, please refer Known issues of OmniFaces CDI features in combination with specific application servers.
Needless to say is that EAR is a dead end since introduction of the cloud and has no value in the microservices world.
In other words, if you want to use CDI (or microservices), then you shouldn't be using an EAR in first place.
Bean instantiated at beginning of request
A bean annotated with @Model
(a stereotype for @RequestScoped and @Named)
has been annotated with @Eager
setting requestURI
to the URI of this page relative to the
application root.
For the demo the @PostConstruct
method will grab the current nano seconds time. When the bean is referenced
on this page it will show the elapsed time in nanoseconds.
One usecase for this feature in a real application could be calling an @Asynchronous
annotated method that
loads data from a database and returns a Future
. That way loading of this data and the processing of the
request pipeline plus the building and rendering of a view up till the moment the Future#get()
is referenced
can overlap. A more concrete example can be found in the chapter
Eagerly instantiate a CDI managed bean.
Elapsed time in nanoseconds since@Eager @RequestScoped
bean initiated at the start of this request: 6277761 ns.
As comparison, elapsed time in nanoseconds since non-@Eager @RequestScoped
bean initiated in this request: 23866 ns.
So, you have 6253895 ns time space to perform some@Asynchronous
initialization.
Bean instantiated when application starts up
A bean has been annotated with @Startup
(a stereotype for @Eager and @ApplicationScoped) and
sets its startupDate property in the @PostConstruct
method, which will thus represent the time this
application started.
Application started at: 24 Nov 2024 16:11.43.964 UTC
As comparison, the Faces#{startup}
is at: 24 Nov 2024 16:11.43.963 UTC
<h3>Bean instantiated at beginning of request</h3>
<p>
A bean annotated with <code>@Model</code> <small>(a stereotype for @RequestScoped and @Named)</small>
has been annotated with <code>@Eager</code> setting <code>requestURI</code> to the URI of this page relative to the
application root.
For the demo the <code>@PostConstruct</code> method will grab the current nano seconds time. When the bean is referenced
on this page it will show the elapsed time in nanoseconds.
</p>
<p>
One usecase for this feature in a real application could be calling an <code>@Asynchronous</code> annotated method that
loads data from a database and returns a <code>Future</code>. That way loading of this data and the processing of the
request pipeline plus the building and rendering of a view up till the moment the <code>Future#get()</code> is referenced
can overlap. A more concrete example can be found in the chapter
<a href="https://balusc.omnifaces.org/2014/06/omnifaces-18-released.html" target="_blank">Eagerly instantiate a CDI managed bean</a>.
</p>
<blockquote>
Elapsed time in nanoseconds since <code>@Eager @RequestScoped</code> bean initiated at the start of this request: <strong>#{myEagerRequestBean.elapsedTime} ns</strong>.
<br/>
As comparison, elapsed time in nanoseconds since non-<code>@Eager @RequestScoped</code> bean initiated in this request: <strong>#{myRequestBean.elapsedTime} ns</strong>.
<br/>
So, you have <strong>#{myEagerRequestBean.elapsedTime - myRequestBean.elapsedTime} ns</strong> time space to perform some <code>@Asynchronous</code> initialization.
</blockquote>
<h3>Bean instantiated when application starts up</h3>
<p>
A bean has been annotated with <code>@Startup</code> <small>(a stereotype for @Eager and @ApplicationScoped)</small> and
sets its startupDate property in the <code>@PostConstruct</code> method, which will thus represent the time this
application started.
</p>
<blockquote>
Application started at: <strong>#{of:formatDateWithTimezone(myStartupBean.startupDate, 'd MMM yyyy HH:mm.ss.SSS', 'UTC')} UTC</strong>
<br/>
As comparison, the Faces <code>\#{startup}</code> is at: <strong>#{of:formatDateWithTimezone(startup, 'd MMM yyyy HH:mm.ss.SSS', 'UTC')} UTC</strong>
</blockquote>
package org.omnifaces.showcase.cdi;
import static java.lang.System.nanoTime;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.inject.Model;
import org.omnifaces.cdi.Eager;
@Model
@Eager(requestURI = "/cdi/Eager")
public class MyEagerRequestBean {
private long initTime;
private Long elapsedTime;
@PostConstruct
public void init() {
initTime = nanoTime();
}
public long getElapsedTime() {
if (elapsedTime == null) {
elapsedTime = nanoTime() - initTime;
}
return elapsedTime;
}
}
package org.omnifaces.showcase.cdi;
import static java.lang.System.nanoTime;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.inject.Model;
@Model
public class MyRequestBean {
private long initTime;
private Long elapsedTime;
@PostConstruct
public void init() {
initTime = nanoTime();
}
public long getElapsedTime() {
if (elapsedTime == null) {
elapsedTime = nanoTime() - initTime;
}
return elapsedTime;
}
}
package org.omnifaces.showcase.cdi;
import java.util.Date;
import jakarta.annotation.PostConstruct;
import jakarta.inject.Named;
import org.omnifaces.cdi.Startup;
@Named
@Startup
public class MyStartupBean {
private Date startupDate;
@PostConstruct
public void init() {
startupDate = new Date();
}
public Date getStartupDate() {
return startupDate;
}
}