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

Posted

技术标签:

【中文标题】如何在@FacesConverter 中注入@EJB、@PersistenceContext、@Inject、@Autowired 等?【英文标题】:How to inject @EJB, @PersistenceContext, @Inject, @Autowired, etc in @FacesConverter? 【发布时间】: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 也不起作用。

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

【问题讨论】:

【参考方案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 之前,您有多种选择:

    改为将其设为托管 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,因此需要在必要时在视图中的任何位置手动定义转换器。

    改为将其注入常规托管 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

    从 JNDI 手动获取 EJB。

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

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

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

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

    另见Conversion Error setting value for 'null Converter'。

另见:

How to inject in @FacesValidator with @EJB, @PersistenceContext, @Inject, @Autowired CDI Injection into a FacesConverter Getting an @EJB in a @FacesValidator and @FacesConverter

【讨论】:

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();
  


https://docs.oracle.com/javaee/7/api/javax/enterprise/inject/spi/CDI.html

https://***.com/a/33017416/5626568

【讨论】:

【参考方案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();


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

【讨论】:

如何回答这个问题? 猜猜这可能是问题的答案,但没有解释很难说...

以上是关于如何在@FacesConverter 中注入@EJB、@PersistenceContext、@Inject、@Autowired 等?的主要内容,如果未能解决你的问题,请参考以下文章

在较新版本的 JSF 中,@FacesValidator 和 @FacesConverter 中的 EJB 和 CDI 注入点无法通过 OmniFaces 工作

升级到 JSF 2.3 后,@Inject 在 @FacesConverter 中不起作用

使用 Express、Mongodb 和 EJ 显示下拉动态数据

php 在帖子中添加元变量(自定义字段,ej template-slug)#wordpress

如何在 c# (CA1307) 中使用 StringComparison?

java.lang.IllegalArgumentException:WFLYWELD0037:将持久性单元注入 CDI 托管 bean 时出错