Инъекция в класс ресурсов Джерси - PullRequest
5 голосов
/ 02 ноября 2011

Я пытался перейти по следующим ссылкам Как подключить соавтора к ресурсу Джерси? и Доступ к внешним объектам в классе ресурсов Джерси Но все же я не могу найтирабочий пример, который показывает, как внедрить в класс Resource.Я не использую Spring или веб-контейнер.

Мой ресурс

package resource;

import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/something")
public class Resource
{
    @MyResource
    Integer foo = null;
    private static String response = "SampleData from Resource";

    public Resource()
    {
        System.out.println("...constructor called :" + foo);
    }

    @Path("/that")
    @GET
    @Produces("text/plain")
    public String sendResponse()
    {
        return response + "\n";
    }
}

Мой поставщик

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;

@Provider
public class MyResourceProvider implements InjectableProvider<MyResource, Integer>
{
    @Override
    public ComponentScope getScope()
    {
       return ComponentScope.PerRequest;
    }

     @Override
    public Injectable getInjectable(final ComponentContext arg0, final MyResource arg1, final Integer arg2)
    {
       return new Injectable<Object>()
        {
            @Override
            public Object getValue()
            {
              return new Integer(99);
            }
        };
    }
}

Мой EndpointPublisher

import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.core.MediaType;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.container.grizzly.GrizzlyWebContainerFactory;

class EndpointPublisher
{
    public static void main(final String[] args)
    {

        final String address = "http://localhost:8080/";
        final Map<String, String> config = new HashMap<String, String>();
        config.put("com.sun.jersey.config.property.packages", "resource");
        try
        {
            GrizzlyWebContainerFactory.create(address, config);
            System.out.println("server started ....." + address);
            callGet();
        }
        catch (final Exception e)
        {
            e.printStackTrace();
        }
    }

    public static void callGet()
    {
        Client client = null;
        ClientResponse response = null;
        client = Client.create();
        final WebResource resource =
                client.resource("http://localhost:8080/something");
        response = resource.path("that")
                .accept(MediaType.TEXT_XML_TYPE, MediaType.APPLICATION_XML_TYPE)
                .type(MediaType.TEXT_XML)
                .get(ClientResponse.class);
        System.out.println(">>>> " + response.getResponseDate());
    }
}

Моя аннотация

@Retention(RetentionPolicy.RUNTIME)
public @interface MyResource
{}

Но когда я выполняю свой EndpointPublisher, я не могу ввести foo!!

Ответы [ 2 ]

8 голосов
/ 02 ноября 2011

Ваш InjectableProvider не реализован правильно.Параметр второго типа не должен быть типом поля, которое вы пытаетесь вставить - вместо этого должен быть контекст - либо класс java.lang.reflect.Type, либо класс com.sun.jersey.api.model.Parameter.В вашем случае вы бы использовали Type.Итак, ваша реализация InjectableProvider должна выглядеть следующим образом:

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

Существует вспомогательный класс для поставщиков инъекций по запросу (PerRequestTypeInjectableProvider), а также поставщиков инъекций singleton (SingletonTypeInjectableProvider), так что вы можете еще больше упростить егоунаследовано от этого:

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

Обратите внимание, что для этих вспомогательных классов вторым параметром типа является тип поля.

И еще одна вещь - внедрение происходит после конструктор вызывается, поэтому конструктор вашего ресурса будет по-прежнему выводить ...constructor called :null, но если вы измените свой метод ресурса для возврата foo, вы увидите, что ответ, который вы получите, будет 99.

2 голосов
/ 31 августа 2013

Это решение хорошо работает, и я хотел бы поделиться тем, что я нашел, чтобы включить CDI на ресурсах Джерси.

Вот самый простой боб из всех:

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

В вашем классе ресурсов мы просто внедряем этот bean-компонент, как в обычном контексте:

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

А вот и ключ. Мы определяем Джерси «InjectionProvider», который будет отвечать за разрешение бобов:

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 набирается с типом аннотации, которую мы обрабатываем, и типом контекста (здесь, обычный тип Java)

ApplicationBeans - это простой помощник для разрешения бина. Вот его содержание:

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

}

Надеюсь, это поможет тем, кто хочет включить инъекцию CDI в свои ресурсы на Джерси.

Пока!

...