【问题标题】:Guice Jersey ClientConfig MessageBodyWriters and InjectionGuice Jersey ClientConfig MessageBodyWriters 和注入
【发布时间】:2012-10-23 19:21:31
【问题描述】:

我正在尝试从客户端的角度让 Guice 注入与 Jersey MessageBodyReader/MessageBodyWriter 一起工作。我已经使用 Guice 正确启动了服务器。我的问题与客户有关。

我整理了以下证明错误的内容:SEVERE: Missing dependency for constructor public FooExample$FooReader(FooExample$FooUnmarshaller) at parameter index 0

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.inject.Inject;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.ext.MessageBodyReader;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Provides;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;

class FooExample {
  public static void main(String[] args) {
    Injector i = Guice.createInjector(new FooExample().new FooModule());
    WebResource service = i.getInstance(WebResource.class);

    service.path("bar")
        .accept(MediaType.APPLICATION_XML)
        .put(String.class, "test123");
  }

  public class FooModule extends AbstractModule{
    @Override
    protected void configure() {
      bind(FooUnmarshaller.class).to(SimpleFooUnmarshaller.class);
    }

    @Provides
    public WebResource configuredClient() {
      ClientConfig config = new DefaultClientConfig();
      config.getClasses().add(FooReader.class);
      return Client.create(config).resource(
          UriBuilder.fromUri("http://localhost:8080/foo").build());
    }
  }

  public static class Foo {}

  public static interface FooUnmarshaller {
    public Foo unmarshall(InputStream is);
  }

  public static class SimpleFooUnmarshaller implements FooUnmarshaller {
    @Override
    public Foo unmarshall(InputStream is) {
      return new Foo();
    }
  }

  public static class FooReader implements MessageBodyReader<Foo> {
    private final FooUnmarshaller marshaller;

    @Inject
    public FooReader(FooUnmarshaller marshaller) {
      this.marshaller = marshaller;
    }

    @Override
    public boolean isReadable(
        Class<?> type,
        Type genericType,
        Annotation[] annotations,
        MediaType mediaType) {
      return true;
    }

    @Override
    public Foo readFrom(
        Class<Foo> type,
        Type genericType,
        Annotation[] annotations,
        MediaType mediaType,
        MultivaluedMap<String, String> httpHeaders,
        InputStream entityStream) throws IOException, WebApplicationException {
      return marshaller.unmarshall(entityStream);
    }
  }
}

我在哪里获得控制台输出:

Oct 23, 2012 3:17:22 PM com.sun.jersey.spi.inject.Errors processErrorMessages
SEVERE: The following errors and warnings have been detected with resource and/or provider classes:
  SEVERE: Missing dependency for constructor public FooExample$FooReader(FooExample$FooUnmarshaller) at parameter index 0
Exception in thread "main" com.google.inject.ProvisionException: Guice provision errors:

1) Error in custom provider, com.sun.jersey.spi.inject.Errors$ErrorMessagesException
  at FooExample$FooModule.configuredClient(FooExample.java:40)
  while locating com.sun.jersey.api.client.WebResource

1 error
  at com.google.inject.internal.InjectorImpl$4.get(InjectorImpl.java:987)
  at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1013)
  at FooExample.main(FooExample.java:25)
Caused by: com.sun.jersey.spi.inject.Errors$ErrorMessagesException
  at com.sun.jersey.spi.inject.Errors.processErrorMessages(Errors.java:170)
  at com.sun.jersey.spi.inject.Errors.postProcess(Errors.java:136)
  at com.sun.jersey.spi.inject.Errors.processWithErrors(Errors.java:199)
  at com.sun.jersey.api.client.Client.<init>(Client.java:187)
  at com.sun.jersey.api.client.Client.<init>(Client.java:170)
  at com.sun.jersey.api.client.Client.create(Client.java:679)
  at FooExample$FooModule.configuredClient(FooExample.java:42)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:616)
  at com.google.inject.internal.ProviderMethod.get(ProviderMethod.java:104)
  at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:40)
  at com.google.inject.internal.InjectorImpl$4$1.call(InjectorImpl.java:978)
  at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:1024)
  at com.google.inject.internal.InjectorImpl$4.get(InjectorImpl.java:974)
  ... 2 more

我觉得我需要使用GuiceComponentProviderFactory,但我似乎找不到任何关于它的文档,也找不到IoCComponentProviderFactoryClientConfig。任何帮助将非常感激。谢谢!

【问题讨论】:

    标签: jersey jax-rs guice


    【解决方案1】:

    所以我通过猜测和检查解决了我自己的问题。我无法确认这是预期的做事方式,但这是可行的。

    Client 类有一个方法:public static Client create(ClientConfig cc, IoCComponentProviderFactory provider),我将 GuiceComponentProviderFactory 传递给它,事情就解决了。上述代码的工作版本是:

    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.annotation.Annotation;
    import java.lang.reflect.Type;
    
    import javax.inject.Inject;
    import javax.ws.rs.WebApplicationException;
    import javax.ws.rs.core.MediaType;
    import javax.ws.rs.core.MultivaluedMap;
    import javax.ws.rs.core.UriBuilder;
    import javax.ws.rs.ext.MessageBodyReader;
    
    import com.google.inject.AbstractModule;
    import com.google.inject.Guice;
    import com.sun.jersey.api.client.Client;
    import com.sun.jersey.api.client.WebResource;
    import com.sun.jersey.api.client.config.DefaultClientConfig;
    import com.sun.jersey.api.core.DefaultResourceConfig;
    import com.sun.jersey.core.spi.component.ioc.IoCComponentProviderFactory;
    import com.sun.jersey.guice.spi.container.GuiceComponentProviderFactory;
    
    class FooExample {
      public static void main(String[] args) {
        WebResource service = configuredClient();
    
        service.path("bar")
            .accept(MediaType.APPLICATION_XML)
            .put(String.class, "test123");
      }
    
      private static WebResource configuredClient() {
        DefaultClientConfig config = new DefaultClientConfig();
        config.getClasses().add(FooReader.class);
        return Client.create(config, provider()).resource(
            UriBuilder.fromUri("http://localhost:8080/foo").build());
      }
    
      private static IoCComponentProviderFactory provider() {
        return new GuiceComponentProviderFactory(
            new DefaultResourceConfig(),
            Guice.createInjector(new FooExample().new FooModule()));
      }
    
      public class FooModule extends AbstractModule {
        @Override
        protected void configure() {
          bind(FooUnmarshaller.class).to(SimpleFooUnmarshaller.class);
        }
      }
    
      public static class Foo {}
    
      public static interface FooUnmarshaller {
        public Foo unmarshall(InputStream is);
      }
    
      public static class SimpleFooUnmarshaller implements FooUnmarshaller {
        @Override
        public Foo unmarshall(InputStream is) {
          return new Foo();
        }
      }
    
      public static class FooReader implements MessageBodyReader<Foo> {
        private final FooUnmarshaller marshaller;
    
        @Inject
        public FooReader(FooUnmarshaller marshaller) {
          this.marshaller = marshaller;
        }
    
        @Override
        public boolean isReadable(
            Class<?> type,
            Type genericType,
            Annotation[] annotations,
            MediaType mediaType) {
          return true;
        }
    
        @Override
        public Foo readFrom(
            Class<Foo> type,
            Type genericType,
            Annotation[] annotations,
            MediaType mediaType,
            MultivaluedMap<String, String> httpHeaders,
            InputStream entityStream) throws IOException, WebApplicationException {
          return marshaller.unmarshall(entityStream);
        }
      }
    }
    

    和输出

    Oct 23, 2012 5:17:11 PM com.sun.jersey.guice.spi.container.GuiceComponentProviderFactory getComponentProvider
    INFO: Binding FooExample$FooReader to GuiceInstantiatedComponentProvider
    Exception in thread "main" com.sun.jersey.api.client.UniformInterfaceException: PUT http://localhost:8080/foo/bar returned a response status of 404 Not Found
      at com.sun.jersey.api.client.WebResource.handle(WebResource.java:686)
      at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
      at com.sun.jersey.api.client.WebResource$Builder.put(WebResource.java:537)
      at FooExample.main(FooExample.java:28)
    

    意味着 guice 绑定有效! =)

    【讨论】: