如何在@FacesConverter中注入@ EJB,@ PersistenceContext,@ Inject,@ Autowired等?

时间:2011-10-05 18:08:55

标签: jsf dependency-injection ejb converter

如何在@EJB中注入@PersistenceContext@Inject@AutoWired@FacesConverter等依赖项?在我的具体情况下,我需要通过@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也不起作用。

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

5 个答案:

答案 0 :(得分:53)

  

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

不,直到JSF 2.3发布。 JSF / CDI的人正在研究JSF 2.3。另请参阅JSF spec issue 1349和我的同事Arjan Tijms的相关"What's new in JSF 2.3?"文章。只有当您向注释明确添加@EJB属性时,@PersistenceContext@Inject@FacesConverter等依赖注入才会在managed=true中生效。

@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开始,它在@EJB中透明地添加了对@Inject(和@FacesConverter)的支持,而无需进一步修改。另见the showcase。如果您恰好需要<f:selectItem(s)>的转换器,那么替代方法是使用其SelectItemsConverter,它将根据选择项自动执行转换作业,而无需任何数据库交互。

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

    另见Conversion Error setting value for 'null Converter'

  5. 另见:

答案 1 :(得分:2)

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

答案 2 :(得分:0)

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

转换器也可以带有应用范围的注释CDI。访问外观时,使用同一类的两个实例。一个是转换器实例本身,哑,不知道EJB注释。另一个实例保留在应用程序范围中,可以通过FacesContext访问。该实例是一个Named对象,因此它知道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));
    }

...

答案 3 :(得分:0)

  

@Inject 仅适用于CDI托管实例

这仅适用于 Java EE 7 CDI 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();
  }

}

答案 4 :(得分:-3)

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;
    }
}

使用Aplication 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();


                ................