在CDI中,@ManagedBean(eager=true)的等效是什么?

14

众所周知,推荐使用来自javax.enterprise.context的注解而不是javax.faces.bean,因为它们已被弃用。

我们发现,使用@ApplicationScoped注释的eager="true" ManagedBeans,同时具有@PostConstruct方法,非常有用于进行Web应用程序初始化,例如从文件系统读取属性,初始化数据库连接等...

示例:

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.
    }
    ...
}
我想知道的是,如果我使用javax.enterprise.context中的注释,如何获得相同的行为。

注意:来自javax.ejb@Startup注释将有助于运行该代码,但仅在Web应用程序部署时,应用程序服务器启动时才会运行它。

4个回答

15

这不是由CDI或JSF提供的。你可以使用自定义的CDI限定符和ServletContextListener来创建自己的,以便在Web应用程序启动时挂接。

@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.
    }

}

(注: toString() 触发了惰性实例化)
import com.example.Eager;
import javax.enterprise.context.ApplicationScoped;

@Eager
@ApplicationScoped
public class YourEagerApplicationScopedBean {

    @PostConstruct
    public void init() {
        System.out.println("Application scoped init!");
    }
}

关于现有的库,只有 JSF 工具库 OmniFaces 提供了开箱即用的 @Eager
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!");
    }
}

它也在@SessionScoped@ViewScoped@RequestScoped上得到了支持

无论采取哪种方法,唯一的缺点是在构建bean时FacesContext不可用。但这不应该是一个大问题,使用CDI,您可以直接@Inject感兴趣的工件,例如ServletContextHttpSession


这里有一个问题,注入点应该添加限定符@Eager。根据您的答案,我在下面发布了一个解决方案。谢谢。 - Eng.Fouad

2
作为一种替代方案,您可以使用EJB而不是CDI。然后您可以使用带有@Startup的@Singleton。
import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;

@Singleton
@Startup
public class SomeBean {

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

为什么不使用您提出的bean并注入需要急切实例化的CDI bean?将类命名为EagerInstatiator。 - LovaBill
那只是简单的桥接。EagerInstatiator可以直接成为这个EJB的名称。虽然如果您需要更复杂的编排,您可以这样做,并使用此EJB来编排参与初始化的不同Bean。 - gmanjon

2

0

这是我使用的一种方法:

import javax.annotation.PostConstruct;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Any;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.util.AnnotationLiteral;
import javax.inject.Inject;

@Startup
@Singleton
public class AppStartup
{
    @Inject
    private BeanManager beanManager;

    @PostConstruct
    public void init()
    {       
        // enforce initializing eager CDI beans
        var beans = beanManager.getBeans(Object.class, new AnnotationLiteral<Any>(){});
        for(var bean : beans)
        {
            if(bean.getBeanClass().getAnnotation(Eager.class) != null && bean.getBeanClass().getAnnotation(ApplicationScoped.class) != null)
            {
                var beanProxyInstance = beanManager.getReference(bean, bean.getBeanClass(), beanManager.createCreationalContext(bean));

                // invoking toString() on the proxy object will invoke the method annotated with @PostConstruct, if has not been invoked yet
                beanProxyInstance.toString();
            }
        }
    }
}

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Retention(RUNTIME)
@Target({TYPE})
public @interface Eager {}

import javax.enterprise.context.ApplicationScoped;

@Eager
@ApplicationScoped
public class SomeCdiBean {}

现在,您可以注入此CDI Bean而无需任何额外的限定符:
@Inject
private SomeCdiBean someCdiBean;

如果一个人开始使用 EJB,那么有更简单的解决方案,请参考其他答案之一。 - Kukeltje

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接