【问题标题】:What is the equivalent of @ManagedBean(eager=true) in CDICDI 中 @ManagedBean(eager=true) 的等价物是什么
【发布时间】:2016-11-19 14:52:23
【问题描述】:

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

我们都发现带有 eager="true" 的 ManagedBeans 使用来自 javax.faces.bean@ApplicationScoped 注释并具有 @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 注释将有助于运行该代码,但仅在应用程序服务器启动时部署 webapp 时运行。

【问题讨论】:

    标签: jsf jakarta-ee cdi managed-bean eager-loading


    【解决方案1】:

    作为替代方案,您可以使用 EJB 代替 CDI。然后你可以有一个@Singleton@Startup

    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。
    • 那只是桥接。 EagerInstatiator 可以直接是这个 EJB 的名称。虽然如果您需要更复杂的编排,您可以这样做,并使用此 EJB 编排参与初始化的不同 Bean
    【解决方案2】:

    这是我使用的一种方法:

    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,有一个更简单的解决方案,请参阅其他答案之一
    【解决方案3】:

    这不是 CDI 或 JSF 提供的。您可以使用自定义 CDI 限定符和 ServletContextListener 自行开发以连接 webapp 启动。

    @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 上也是supported

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

    【讨论】:

    • 这里有个问题,应该在注入点添加限定符@Eager。根据您的回答,我在下面发布了一个解决问题的答案。谢谢。
    【解决方案4】:

    CDI 1.1 还提供了一种观察范围生命周期事件的标准方法,例如:

    public void processApplicationScopedInit(@Observes @Initialized(ApplicationScoped.class) ServletContext payload) {}
    public void processApplicationScopedDestroyed(@Observes @Destroyed(ApplicationScoped.class) ServletContext payload) {}
    

    欲了解更多信息:http://www.next-presso.com/2014/06/you-think-you-know-everything-about-cdi-events-think-again/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-22
      • 2012-03-29
      • 1970-01-01
      • 2022-01-21
      • 1970-01-01
      • 2015-08-21
      • 1970-01-01
      • 2021-04-03
      相关资源
      最近更新 更多