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

28

我该如何在@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,导致了NPEs。看起来@PersistenceContext@Inject也不起作用。

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

5个回答

53

我能否使用@EJB将我的服务注入到一个@FacesConverter中?

不行,除非JSF 2.3被发布。JSF / CDI团队正在为JSF 2.3而努力。请参见JSF规范问题1349以及我的同事Arjan Tijms的相关文章“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,因此需要在视图中的每个需要的位置手动定义转换器。

    也可以将其注入到普通托管bean中。

  2. @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。

  4. YourService yourService = (YourService) new InitialContext().lookup("java:global/appName/YourService");
    
    这种方法的缺点是存在一定的风险,可能不完全可移植。另请参见从JSF托管的Bean程序化地注入EJB bean。 安装OmniFaces。自版本1.6以来,它在@FacesConverter中透明地添加了对@EJB(和@Inject)的支持,无需任何其他修改。请参见示例。如果您需要用于<f:selectItem(s)>的转换器,则选择使用其SelectItemsConverter,该转换器将根据选择项自动执行转换工作,无需进行任何数据库交互。
    <h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">
    

    另请参见Conversion Error setting value for 'null Converter'

另请参见:


EJB查找应该是可移植的,不是吗? - Kalpesh Soni
@Kalpesh:这取决于您如何打包您的EJB以及应用服务器的制作/版本。 - BalusC
@BalusC 我知道这个问题很老了,但是从UIComponent中检索UISelectItems,然后迭代此选择项列表并查找值是否“不好”?我的意思是你需要任何注入或发送请求到数据库(如果问题不清楚,我可以用示例开始一个新问题) - Ouerghi Yassine
@OuerghiYassine:该转换器已经存在:http://showcase.omnifaces.org/converters/SelectItemsConverter - BalusC
JSF 2.3本周发布了!耶!https://javaserverfaces.java.net/2.3/download.html - Max
尝试使用包含JSF 2.3的Glassfish5.1。但是无法正常工作。在facesConverter中的sessionBean为null,未注入。 - eastwater

2

0

你可以通过FacesContext间接访问它,这是Converter方法中的参数之一。

转换器也可以用应用程序范围的CDI Named进行注释。在访问外观时,使用了相同类的两个实例。一个是转换器实例本身,它是愚蠢的,不知道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));
    }

...

0
@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();
  }

}

-4

作者: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,以获取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();


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

这怎么是对问题的回答呢? - Kukeltje
猜测这可能是问题的答案,但没有解释很难确定... - Kalle Richter

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