jsfjakarta-eecdimanaged-beaneager-loading

What is the equivalent of @ManagedBean(eager=true) in CDI


As we all know that it is recommended to use annotations from javax.enterprise.context instead of javax.faces.bean as they are getting deprecated.

And we all found ManagedBeans with eager="true" annotated with @ApplicationScoped from javax.faces.bean and having a @PostConstruct method are very useful to do web application initialization e.g: read properties from file system, initialize database connections, etc...

Example :

import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import javax.annotation.PostConstruct;

@ApplicationScoped
@ManagedBean(eager=true)
public class someBean{

    @PostConstruct
    public void init(){
        //Do all needed application initialization.
    }
    ...
}

What I want to know is how can I get the same behavior if I used annotations from javax.enterprise.context.

Note: @Startup annotation from javax.ejb will help to run that code but only at the moment of deployment of the webapp when the application server Starts.


Solution

  • This is not provided by CDI or JSF. You could homegrow your own with a custom CDI qualifier and a ServletContextListener to hook on webapp start.

    @Qualifier
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public @interface Eager {
        //
    }
    

    @WebListener
    public class EagerListener implements ServletContextListener{
    
        private static final AnnotationLiteral<Eager> EAGER_ANNOTATION = new AnnotationLiteral<Eager>() {
            private static final long serialVersionUID = 1L;
        };
    
        @Override
        public void contextInitialized(ServletContextEvent event) {
            CDI.current().select(EAGER_ANNOTATION).forEach(bean -> bean.toString());
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent event) {
            // NOOP.
        }
    
    }
    

    (note: toString() triggers lazy instantiation)

    import com.example.Eager;
    import javax.enterprise.context.ApplicationScoped;
    
    @Eager
    @ApplicationScoped
    public class YourEagerApplicationScopedBean {
    
        @PostConstruct
        public void init() {
            System.out.println("Application scoped init!");
        }
    }
    

    As to existing libraries, only JSF utility library OmniFaces offers @Eager out the box.

    import org.omnifaces.cdi.Eager;
    import javax.enterprise.context.ApplicationScoped;
    
    @Eager
    @ApplicationScoped
    public class YourEagerApplicationScopedBean {
    
        @PostConstruct
        public void init() {
            System.out.println("Application scoped init!");
        }
    }
    

    It's also supported on @SessionScoped, @ViewScoped and @RequestScoped.

    Regardless of the approach, the only disadvantage is that FacesContext isn't available at the moment the bean is constructed. But that shouldn't be a big problem, with CDI you can simply directly @Inject artifacts of interest such as ServletContext or HttpSession.