How to inject @EJB, @PersistenceContext, @Inject, @Autowired, etc in @FacesConverter?

前端 未结 5 1249
故里飘歌
故里飘歌 2020-11-22 13:04

How can I inject a dependency like @EJB, @PersistenceContext, @Inject, @AutoWired, etc in a @FacesConverter?

相关标签:
5条回答
  • 2020-11-22 13:50

    You could access it indirectly through FacesContext, which is a parameter in both Converter methods.

    The converter could be also annotated CDI Named with Application scope. When accessing the facade, two instances of the same class are used. One is the converter instance itself, dumb, without knowing EJB annotation. Another instance retains in application scope and could be accessed via the FacesContext. That instance is a Named object, thus it knows the EJB annotation. As everything is done in a single class, access could be kept protected.

    See the following example:

    @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 讨论(0)
  • 2020-11-22 13:51

    By Luis Chacon, Sv

    Works fine, tested

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

    define bean with the Aplication bean scope, for get the EJB Object

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

    final Access to Ejb Object from Converter:

    @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();
    
    
                    ................
    
    0 讨论(0)
  • 2020-11-22 13:58

    The answer is Yes if you can accommodate Seam Faces module in your web application. Please check this post Injection of EntityManager or CDI Bean in FacesConverter. You can use @EJB in similar fashion.

    0 讨论(0)
  • 2020-11-22 14:01

    @Inject will only works in CDI managed instances

    This only works at least Java EE 7 and CDI 1.1 server:

    @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://stackoverflow.com/a/33017416/5626568

    0 讨论(0)
  • 2020-11-22 14:04

    Can I use @EJB to inject my service into a @FacesConverter?

    No, not until JSF 2.3 is released. The JSF/CDI guys are working on that for JSF 2.3. See also JSF spec issue 1349 and this related "What's new in JSF 2.3?" article of my fellow Arjan Tijms. Only then dependency injection like @EJB, @PersistenceContext, @Inject, etc will work in a @FacesConverter when you explicitly add managed=true attribute to the annotation.

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

    If not, what's the "correct" way to do this?

    Before JSF 2.3, you have several options:

    1. Make it a managed bean instead. You can make it a JSF, CDI or Spring managed bean via @ManagedBean, @Named or @Component. The below example makes it a JSF managed bean.

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

      And the below example makes it a CDI managed bean.

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

      Reference it as <h:inputXxx converter="#{yourConverter}"> instead of <h:inputXxx converter="yourConverter">, or as <f:converter binding="#{yourConverter}"> instead of <f:converter converterId="yourConverter">. Don't forget to remove the @FacesConverter annotation!

      The disadvantage is that you cannot specify forClass and thus need to manually define the converter everywhere in the view where necessary.

    2. Inject it in a regular managed bean instead.

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

      And in your converter, grab or call it via 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);
      

      This way you can keep using @FacesConverter.

    3. Manually grab the EJB from JNDI.

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

      The disadvantage is that there is a certain risk that this is not entirely portable. See also Inject EJB bean from JSF managed bean programmatically.

    4. Install OmniFaces. Since version 1.6, it transparently adds support for @EJB (and @Inject) in a @FacesConverter without any further modification. See also the showcase. If you happen to need the converter for <f:selectItem(s)>, then the alternative is to use its SelectItemsConverter which will automatically perform the conversion job based on select items without the need for any database interaction.

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

      See also Conversion Error setting value for 'null Converter'.

    See also:

    • How to inject in @FacesValidator with @EJB, @PersistenceContext, @Inject, @Autowired
    • CDI Injection into a FacesConverter
    • Getting an @EJB in a @FacesValidator and @FacesConverter
    0 讨论(0)
提交回复
热议问题