如何在
@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
也不起作用。
如何在转换器中注入服务依赖项以便可以访问数据库?
我可以使用
将我的服务注入到@EJB
中吗?@FacesConverter
不,直到 JSF 2.3 发布为止。 JSF/CDI 人员正在为 JSF 2.3 做这方面的工作。另请参阅 JSF 规范问题 1349 以及我的同事 Arjan Tijms 的相关 “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 之前,您有多种选择:
改为将其设为托管 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");
缺点是存在一定的风险,即不完全便携。另请参阅 以编程方式从 JSF 托管 bean 注入 EJB bean。
安装OmniFaces。从 1.6 版本开始,它在
@EJB
中透明地添加了对 @Inject
(和 @FacesConverter
)的支持,无需任何进一步修改。另请参阅展示柜。如果您碰巧需要 <f:selectItem(s)>
的转换器,那么另一种方法是使用其 SelectItemsConverter
,它将根据选择的项目自动执行转换作业,而不需要任何数据库交互。
<h:selectOneMenu ... converter="omnifaces.SelectItemsConverter">
另请参阅 “null Converter”的转换错误设置值。
如果您可以在 Web 应用程序中容纳 Seam Faces 模块,那么答案是肯定的。请查看这篇文章Injection of EntityManager or CDI Bean in FacesConverter。您可以以类似的方式使用@EJB。
您可以通过 FacesContext 间接访问它,这是两个 Converter 方法中的参数。
转换器也可以注释为 CDI,以应用程序范围命名。访问外观时,将使用同一类的两个实例。一是转换器实例本身,愚蠢,不知道 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));
}
...
@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();
}
}
这个周末我遇到了这个@EJB 注入不起作用问题并找到了这个线程,但今天的 Faces 版本没有答案。我想添加此更新只是为了记录。
@RequestScoped
@Named("miniController")
public class MiniController {
...
MiniValue val;
...
}
@FacesConverter(managed = true, forClass = MiniValue.class, value ="miniConverter")
public class MiniConverter implements Serializable, Converter<MiniValue> {
...
@EJB
DemoBeanRemote bean;
...
}
...
<h:inputText value="#{miniController.val}" converter="miniConverter" />
...
@RequestScoped
@Named("miniController")
public class MiniController {
...
MiniValue val;
...
}
@FacesConverter(managed = true, forClass = MiniValue.class)
public class MiniConverter implements Serializable, Converter<MiniValue> {
...
@EJB
DemoBeanRemote bean;
...
}
...
<h:inputText value="#{miniController.val}" />
...
@RequestScoped
@Named("miniController")
public class MiniController {
...
MiniValue val;
...
}
@FacesConverter(managed = true, value="miniConverter")
public class MiniConverter implements Serializable, Converter<MiniValue> {
...
@EJB
DemoBeanRemote bean;
...
}
...
<h:inputText value="#{miniController.val}" converter="miniConverter" />
...
The JavaDoc 说: “...应用程序用户应该理解的一个重要的微妙之处。不可能使用单个 @FacesConverter 注释在按类和按转换器中注册单个转换器实现 - id 数据结构...”
作者:路易斯·查孔,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();
................