Injecting into a Jersey Resource class

后端 未结 2 1280
情歌与酒
情歌与酒 2020-12-30 16:32

I did try going through the following links How to wire in a collaborator into a Jersey resource? and Access external objects in Jersey Resource class But still i am unable

相关标签:
2条回答
  • 2020-12-30 16:45

    This solution works well and I wanted to share what I found to enable CDI on jersey resources.

    Here is the simplest bean ever :

    package fr.test;
    
    import javax.annotation.PostConstruct;
    import javax.enterprise.context.RequestScoped;
    
    @RequestScoped
    public class Test {
    
        private int i;
    
        @PostConstruct
        public void create() {
            i = 6;
        }
    
        public int getI() {
            return i;
        }
    }
    

    In your resource class, we just inject this bean, as we would do in a any normal context :

    package fr.test;
    
    import javax.inject.Inject;
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    
    @Path("/login")
    public class LoginApi {
    
        @Inject
        private Test test;
    
        @GET 
        @Produces("text/plain")
        public String getIt() {
            return "Hi there!" + test;
        }
    }
    

    And here is the key. We define a Jersey "InjectionProvider" which will be responsible of beans' resolution :

    package fr.test;
    
    import javax.inject.Inject;
    
    import java.lang.reflect.Type;
    import javax.ws.rs.ext.Provider;
    
    import com.sun.jersey.core.spi.component.ComponentContext;
    import com.sun.jersey.core.spi.component.ComponentScope;
    import com.sun.jersey.spi.inject.Injectable;
    import com.sun.jersey.spi.inject.InjectableProvider;
    
    import fr.xxxxxxxxxx.ApplicationBeans;
    
    @Provider
    public class InjectionProvider implements InjectableProvider<Inject, Type> {
    
        public ComponentScope getScope() {
            // CDI will handle scopes for us
            return ComponentScope.Singleton;
        }
    
        @Override
        public Injectable<?> getInjectable(ComponentContext context,
                Inject injectAnno, Type t) {
            if (!(t instanceof Class))
                throw new RuntimeException("not injecting a class type ?");
    
            Class<?> clazz = (Class<?>) t;
    
            final Object instance = ApplicationBeans.get(clazz);
    
            return new Injectable<Object>() {
                public Object getValue() {
                    return instance;
                }
            };
        }
    }
    

    InjectableProvider is typed with the kind of annotation we are handling, and the context type (here, normal java type)

    ApplicationBeans is just a simple helper for bean resolution. Here is its content :

    package fr.xxxxxxxxxx;
    
    import java.lang.annotation.Annotation;
    import java.util.Arrays;
    import java.util.Collection;
    import java.util.Set;
    
    import javax.enterprise.context.ApplicationScoped;
    import javax.enterprise.context.spi.CreationalContext;
    import javax.enterprise.inject.spi.Bean;
    import javax.enterprise.inject.spi.BeanManager;
    import javax.inject.Inject;
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    
    import fr.xxxxxxxxxxxxx.UnexpectedException;
    
    /**
     * Gives direct access to managed beans - Designed to be used from unmanaged code
     * 
     * @author lgrignon
     * 
     */
    @ApplicationScoped
    public class ApplicationBeans
    {
    
      protected static ApplicationBeans instance;
    
      @Inject
      private BeanManager beanManager;
    
      /**
       * Gets instance
       * 
       * @return Instance from managed environment
       */
      public static ApplicationBeans instance()
      {
        if (instance == null)
        {
          BeanManager beanManager;
          InitialContext ctx = null;
          try
          {
            ctx = new InitialContext();
            beanManager = (BeanManager)ctx.lookup("java:comp/BeanManager");
          }catch(NamingException e)
          {
            try
            {
              beanManager = (BeanManager)ctx.lookup("java:app/BeanManager");
            }catch(NamingException ne)
            {
              throw new UnexpectedException("Unable to obtain BeanManager.", ne);
            }
          }
    
          instance = getBeanFromManager(beanManager, ApplicationBeans.class);
        }
    
        return instance;
      }
    
      /**
       * Gets bean instance from context
       * 
       * @param <T>
       *          Bean's type
       * @param beanType
       *          Bean's type
       * @param annotations
       *          Bean's annotations
       * @return Bean instance or null if no
       */
      public static <T> T get(final Class<T> beanType, Annotation... annotations)
      {
        return instance().getBean(beanType, annotations);
      }
    
      /**
       * Gets bean instance from context
       * 
       * @param <T>
       *          Bean's type
       * @param beanType
       *          Bean's type
       * @param annotations
       *          Bean's annotations
       * @return Bean instance or null if no
       */
      public <T> T getBean(final Class<T> beanType, Annotation... annotations)
      {
        return getBeanFromManager(beanManager, beanType, annotations);
      }
    
      @SuppressWarnings("unchecked")
      private static <T> T getBeanFromManager(BeanManager beanManager, final Class<T> beanType, Annotation... annotations)
      {
        Set<Bean<?>> beans = beanManager.getBeans(beanType, annotations);
        if (beans.size() > 1)
        {
          throw new UnexpectedException("Many bean declarations found for type %s (%s)", beanType.getSimpleName(), beansToString(beans));
        }
    
        if (beans.isEmpty())
        {
          throw new UnexpectedException("No bean declaration found for type %s", beanType.getSimpleName());
        }
    
        final Bean<T> bean = (Bean<T>)beans.iterator().next();
        final CreationalContext<T> context = beanManager.createCreationalContext(bean);
        return (T)beanManager.getReference(bean, beanType, context);
      }
    
      private static String beansToString(Collection<Bean<?>> beans)
      {
        String[] beansLabels = new String[beans.size()];
        int i = 0;
        for (final Bean<?> bean : beans)
        {
          beansLabels[i++] = bean.getName();
        }
    
        return Arrays.toString(beansLabels);
      }
    
    }
    

    Hope this will help those who want to enable CDI injection in their Jersey resources.

    Bye !

    0 讨论(0)
  • 2020-12-30 17:12

    Your InjectableProvider is not implemented correctly. The second type parameter should not be the type of the field you are trying to inject - instead it should be the context - either java.lang.reflect.Type class or com.sun.jersey.api.model.Parameter class. In your case, you would use Type. So, your InjectableProvider implementation should look as follows:

    package resource;
    
    import javax.ws.rs.ext.Provider;
    import com.sun.jersey.core.spi.component.ComponentContext;
    import com.sun.jersey.core.spi.component.ComponentScope;
    import com.sun.jersey.spi.inject.Injectable;
    import com.sun.jersey.spi.inject.InjectableProvider;
    import java.lang.reflect.Type;
    
    @Provider
    public class MyResourceProvider implements InjectableProvider<MyResource, Type> {
    
        @Override
        public ComponentScope getScope() {
            return ComponentScope.PerRequest;
        }
    
        @Override
        public Injectable getInjectable(final ComponentContext arg0, final MyResource arg1, final Type arg2) {
            if (Integer.class.equals(arg2)) {
                return new Injectable<Integer>() {
    
                    @Override
                    public Integer getValue() {
                        return new Integer(99);
                    }
                };
            } else {
                return null;
            }
        }
    }
    

    There is a helper class for per-request injectable providers (PerRequestTypeInjectableProvider) as well as singleton injectable providers (SingletonTypeInjectableProvider), so you can further simplify it by inheriting from that:

    package resource;
    
    import javax.ws.rs.ext.Provider;
    import com.sun.jersey.core.spi.component.ComponentContext;
    import com.sun.jersey.spi.inject.Injectable;
    import com.sun.jersey.spi.inject.PerRequestTypeInjectableProvider;
    
    @Provider
    public class MyResourceProvider extends PerRequestTypeInjectableProvider<MyResource, Integer> {
        public MyResourceProvider() {
            super(Integer.class);
        }
    
        @Override
        public Injectable<Integer> getInjectable(ComponentContext ic, MyResource a) {
            return new Injectable<Integer>() {
                @Override
                public Integer getValue() {
                    return new Integer(99);
                }
            };
        }
    }
    

    Note that for these helper classes the second type parameter is the type of the field.

    And one more thing - the injection happens after the constructor is called, so the constructor of your resource will still print out ...constructor called :null, but if you change your resource method to return foo, you'll see the response you'll get will be 99.

    0 讨论(0)
提交回复
热议问题