【问题标题】:How to inject @EJB, @PersistenceContext, @Inject, @Autowired, etc in @FacesConverter?如何在@FacesConverter 中注入@EJB、@PersistenceContext、@Inject、@Autowired 等?
【发布时间】:2011-12-01 17:09:00
【问题描述】:

如何在@FacesConverter 中注入@EJB@PersistenceContext@Inject@AutoWired 等依赖项?在我的具体情况下,我需要通过@EJB 注入一个 EJB:

@FacesConverter
public class MyConverter implements Converter {

  @EJB
  protected MyService myService;    

  @Override
  public Object getAsObject(FacesContext context, UIComponent component, String value) {
    // myService.doSomething
  }

}

但是,它没有被注入,它仍然是null,导致 NPE。看来@PersistenceContext@Inject 也不起作用。

如何在转换器中注入服务依赖项以便访问数据库?

【问题讨论】:

    标签: jsf dependency-injection ejb converter


    【解决方案1】:

    我可以使用@EJB 将我的服务注入@FacesConverter 吗?

    不,直到 JSF 2.3 发布。 JSF/CDI 人员正在为 JSF 2.3 做这方面的工作。另请参阅 JSF spec issue 1349 和我的同事 Arjan Tijms 的这篇相关的 "What's new in JSF 2.3?" 文章。只有当您将managed=true 属性显式添加到注释时,@EJB@PersistenceContext@Inject 等依赖注入才能在@FacesConverter 中工作。

    @FacesConverter(value="yourConverter", managed=true)
    public class YourConverter implements Converter {
    
        @Inject
        private YourService service;
        // ...
    }
    

    如果不是,那么“正确”的方法是什么?

    在 JSF 2.3 之前,您有多种选择:

    1. 改为将其设为托管 bean。您可以通过@ManagedBean@Named@Component 使其成为JSF、CDI 或Spring 托管bean。下面的示例使其成为 JSF 托管 bean。

      @ManagedBean
      @RequestScoped
      public class YourConverter implements Converter {
      
          @EJB
          private YourService service;
          // ...
      }
      

      下面的例子使它成为一个 CDI 托管 bean。

      @Named
      @RequestScoped
      public class YourConverter implements Converter {
      
          @Inject
          private YourService service;
          // ...
      }
      

      将其引用为<h:inputXxx converter="#{yourConverter}"> 而不是<h:inputXxx converter="yourConverter">,或引用为<f:converter binding="#{yourConverter}"> 而不是<f:converter converterId="yourConverter">。不要忘记删除@FacesConverter 注释!

      缺点是您无法指定forClass,因此需要在必要时在视图中的任何位置手动定义转换器。

    2. 改为将其注入常规托管 bean。

      @ManagedBean
      @RequestScoped
      public class YourBean {
      
          @EJB
          private YourService service;
          // ...
      }
      

      然后在您的转换器中,通过 EL 抓取或调用它。

      YourBean yourBean = context.getApplication().evaluateExpressionGet(context, "#{yourBean}", YourBean.class);
      
      // Then e.g. either
      YourEntity yourEntity = yourBean.getService().findByStringId(value);
      // Or
      YourEntity yourEntity = yourBean.findEntityByStringId(value);
      

      这样你就可以继续使用@FacesConverter

    3. 从 JNDI 手动获取 EJB。

      YourService yourService = (YourService) new InitialContext().lookup("java:global/appName/YourService");
      

      缺点是存在一定的风险,即不完全便携。另见Inject EJB bean from JSF managed bean programmatically

    4. 安装OmniFaces。从 1.6 版开始,它在@FacesConverter 中透明地添加了对@EJB(和@Inject)的支持,无需进一步修改。另见the showcase。如果您碰巧需要<f:selectItem(s)> 的转换器,那么另一种选择是使用它的SelectItemsConverter,它将根据所选项目自动执行转换工作,而无需任何数据库交互。

      <h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">
      

      另见Conversion Error setting value for 'null Converter'

    另见:

    【讨论】:

    • ejb 查找应该是可移植的吧?
    • @Kalpesh:取决于您如何打包 EJB 以及应用服务器的品牌/版本。
    • @BalusC 我知道这个问题已经过时了,但是从UIComponent 中检索UISelectItems 是否“不好”,然后遍历这个选择项列表并找到值?我的意思是你需要任何注入或向数据库发送请求(如果问题不清楚,我可以用一个例子开始一个新问题)
    • @OuerghiYassine:该转换器已经存在:showcase.omnifaces.org/converters/SelectItemsConverter
    • JSF 2.3 本周发布!耶! javaserverfaces.java.net/2.3/download.html
    【解决方案2】:

    如果您可以在 Web 应用程序中容纳 Seam Faces 模块,答案是肯定的。请查看此帖子Injection of EntityManager or CDI Bean in FacesConverter。您可以以类似的方式使用@EJB。

    【讨论】:

      【解决方案3】:

      您可以通过 FacesContext 间接访问它,这是两个 Converter 方法中的参数。

      转换器也可以注释 CDI Named with Application scope。当访问门面时,使用了同一个类的两个实例。一种是转换器实例本身,很笨,不知道 EJB 注释。另一个实例保留在应用程序范围内,可以通过 FacesContext 访问。该实例是一个命名对象,因此它知道 EJB 注释。由于一切都在一个类中完成,因此可以保护访问。

      看下面的例子:

      @FacesConverter(forClass=Product.class)
      @Named
      @ApplicationScoped
      public class ProductConverter implements Converter{
          @EJB protected ProductFacade facade;
      
          protected ProductFacade getFacadeFromConverter(FacesContext ctx){
              if(facade==null){
                  facade = ((ProductConverter) ctx.getApplication()
                      .evaluateExpressionGet(ctx,"#{productConverter}",ProductConverter.class))
                      .facade;
              }
              return facade;
          }
      
          @Override
          public Object getAsObject(FacesContext context, UIComponent component, String value) {
              return getFacadeFromConverter(context).find(Long.parseLong(value));
          }
      
      ...
      

      【讨论】:

        【解决方案4】:

        @Inject 仅适用于 CDI 托管实例

        这仅适用于至少 Java EE 7CDI 1.1 服务器:

        @FacesConverter
        public class MyConverter implements Converter {
        
          protected MyService myService;    
        
          @Override
          public Object getAsObject(FacesContext context, UIComponent component, String value) {
              myService = CDI.current().select(MyService .class).get();
              myService.doSomething();
          }
        
        }
        

        【讨论】:

          【解决方案5】:

          Luis Chacon, Sv

          工作正常,经过测试

          定义 EJB:

          @Stateless
          @LocalBean
          public class RubroEJB {
          
              @PersistenceContext(unitName = "xxxxx")
              private EntityManager em;
          
              public List<CfgRubroPres> getAllCfgRubroPres(){
                  List<CfgRubroPres> rubros = null;
                  Query q = em.createNamedQuery("xxxxxxx");
                  rubros = q.getResultList();
                  return rubros;
              }
          }
          

          定义具有应用程序bean范围的bean,用于获取EJB对象

          @ManagedBean(name="cuentaPresService", eager = true)
          @ApplicationScoped
          public class CuentaPresService {
          
              @EJB
              private RubroEJB cfgCuentaEJB;
          
              public RubroEJB getCfgCuentaEJB() {
                  return cfgCuentaEJB;
              }
          
              public void setCfgCuentaEJB(RubroEJB cfgCuentaEJB) {
                  this.cfgCuentaEJB = cfgCuentaEJB;
              }
          }
          

          从转换器最终访问 Ejb 对象:

          @FacesConverter("cuentaPresConverter")
          public class CuentaPresConverter implements Converter {
          
              @EJB
              RubroEJB rubroEJB;
          
              public Object getAsObject(FacesContext fc, UIComponent uic, String value) {
                  if(value != null && value.trim().length() > 0) {
                      try {
                          CuentaPresService service = (CuentaPresService) fc.getExternalContext().getApplicationMap().get("cuentaPresService");
          
          
                          List<CfgCuentaPres> listCuentas=service.getCfgCuentaEJB().getAllCfgCuentaPres();
          
          
                          ................
          

          【讨论】:

          • 如何回答这个问题?
          • 猜猜这可能是问题的答案,但没有解释很难说...
          猜你喜欢
          • 2011-11-26
          • 2016-01-30
          • 2015-12-12
          • 1970-01-01
          • 1970-01-01
          • 2012-10-20
          • 2015-07-13
          • 1970-01-01
          相关资源
          最近更新 更多