UnsatisfiedDependencyException:SystemInjecteeImpl中没有可用于注入的对象

时间:2022-05-01 19:32:56

There are errors when using DI in Jersey Rest application:

在Jersey Rest应用程序中使用DI时出错:

org.glassfish.hk2.api.UnsatisfiedDependencyException: There was no object available for injection at SystemInjecteeImpl(requiredType=PricingService,parent=PricingResource,qualifiers={},position=0,optional=false,self=false,unqualified=null,1633188703)

I am quite new to the concept and it appears quite complicated since there are some examples seems to be deprecated. As I understand there are a few ways to make DI work: native HK2, Spring/HK2 Bridge. What is easier and more straightforward to configure? How to set up programmatically (not a fan of XML) for Jersey 2.x?

我对这个概念很陌生,看起来很复杂,因为有些例子似乎已被弃用了。据我所知,有几种方法可以使DI工作:原生HK2,Spring / HK2 Bridge。配置更简单,更直接?如何为Jersey 2.x以编程方式(而不是XML的粉丝)进行设置?

ResourceConfig

import org.glassfish.jersey.server.ResourceConfig;

public class ApplicationConfig  extends ResourceConfig {
    public ApplicationConfig() {
        register(new ApplicationBinder());
        packages(true, "api");
    }
}

AbstractBinder

public class ApplicationBinder extends AbstractBinder {
    @Override
    protected void configure() {
        bind(PricingService.class).to(PricingService.class).in(Singleton.class);
    }
}

PricingResource

@Path("/prices")
public class PricingResource {
    private final PricingService pricingService;

    @Inject
    public PricingResource(PricingService pricingService) {
        this.pricingService = pricingService;
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Collection<Price> findPrices() {
        return pricingService.findPrices();
    }
}

PricingService

@Singleton
public class PricingService {
   // no constructors...
// findPrices() ...

}

UPDATE

public class Main {
    public static final String BASE_URI = "http://localhost:8080/api/";

    public static HttpServer startServer() {
        return createHttpServerWith(new ResourceConfig().packages("api").register(JacksonFeature.class));
    }

    private static HttpServer createHttpServerWith(ResourceConfig rc) {
        HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
        StaticHttpHandler staticHttpHandler = new StaticHttpHandler("src/main/webapp");
        staticHttpHandler.setFileCacheEnabled(false);
        staticHttpHandler.start();
        httpServer.getServerConfiguration().addHttpHandler(staticHttpHandler);
        return httpServer;
    }

    public static void main(String[] args) throws IOException {
        System.setProperty("java.util.logging.config.file", "src/main/resources/logging.properties");
        final HttpServer server = startServer();

        System.out.println(String.format("Jersey app started with WADL available at "
                + "%sapplication.wadl\nHit enter to stop it...", BASE_URI));
        server.start();
        System.in.read();
        server.stop();
    }

}

UPDATE3:

public class PricingResourceTest extends JerseyTest {
    @Mock
    private PricingService pricingServiceMock;

    @Override
    protected Application configure() {
        MockitoAnnotations.initMocks(this);
        enable(TestProperties.LOG_TRAFFIC);
        enable(TestProperties.DUMP_ENTITY);

        ResourceConfig config = new ResourceConfig(PricingResource.class);
        config.register(new AbstractBinder() {
            @Override
            protected void configure() {
                bind(pricingServiceMock).to(PricingService.class);
            }
        });
        return config;
    }

    @Test
    public void testFindPrices(){
        when(pricingServiceMock.findPrices()).thenReturn(getMockedPrices());
        Response response  = target("/prices")
                .request()
                .get();
        verify(pricingServiceMock).findPrices();
        List<Price> prices = response.readEntity(new GenericType<List<Price>>(){});
//        assertEquals("Should return status 200", 200, response.getStatus());
        assertTrue(prices.get(0).getId() == getMockedPrices().get(0).getId());
    }

    private List<Price> getMockedPrices(){
        List<Price> mockedPrices = Arrays.asList(new Price(1L, 12.0, 50.12, 12L));
        return mockedPrices;
    }
}

JUnit output:

INFO: 1 * Client response received on thread main
1 < 200
1 < Content-Length: 4
1 < Content-Type: application/json
[{}]


java.lang.AssertionError

While debugging:

prices.get(0) is Price object that has null assigned to all fields.

prices.get(0)是已将null分配给所有字段的Price对象。


UPDATE4:

Added to configure():

添加到configure():

 config.register(JacksonFeature.class);
 config.register(JacksonJsonProvider.class);

Now Junit output a bit better:

现在Junit输出更好一点:

INFO: 1 * Client response received on thread main
1 < 200
1 < Content-Length: 149
1 < Content-Type: application/json
[{"id":2,"recurringPrice":122.0,"oneTimePrice":6550.12,"recurringCount":2},{"id":2,"recurringPrice":122.0,"oneTimePrice":6550.12,"recurringCount":2}]

Indeed list prices has correct number of prices but all prices' fields is null. That leads to assumption that problem might be reading entity:

事实上,清单价格的价格是正确的,但所有价格的字段都是空的。这导致假设问题可能是阅读实体:

List<Price> prices = response.readEntity(new GenericType<List<Price>>(){});

Here is how to fix it

Change Moxy dependency to:

将Moxy依赖项更改为:

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
</dependency>

and add annotations on 'Price' object.

并在“价格”对象上添加注释。

@XmlRootElement
@JsonIgnoreProperties(ignoreUnknown = true)

2 个解决方案

#1


5  

Forget the InjectableProvider. You don't need it. The problem is that the mock service is not the one being injected. It is the one created by the DI framework. So you are checking for changes on the mock service, which has never been touched.

忘记InjectableProvider。你不需要它。问题是模拟服务不是被注入的服务。它是由DI框架创建的。因此,您正在检查模拟服务的更改,这些更改从未被触及过。

So what you need to do is bind the mock with the DI framework. You can simply create another AbstractBinder for testing. It can be a simple anonymous one, where you will bind the mock

所以你需要做的是将模拟与DI框架绑定。您可以简单地创建另一个AbstractBinder进行测试。它可以是一个简单的匿名,你将绑定模拟

ResourceConfig config = new ResourceConfig(PricingResource.class);
config.register(new AbstractBinder() {
    @Override
    protected void configure() {
        bind(pricingServiceMock).to(PricingService.class);
    }
});

Here you are simply binding the mocked service. So the framework will inject the mock into the resource. Now when you modify it in the request, the changes will be seen in the assertion

在这里,您只需绑定模拟服务。所以框架会将模拟注入资源。现在,当您在请求中修改它时,将在断言中看到更改

Oh and you still need to do your when(..).then(..) to initialize the data in the mock service. That is also what you are missing

哦,你仍然需要做你的(..)。然后(..)初始化模拟服务中的数据。这也是你所缺少的

@Test
public void testFindPrices(){
    Mockito.when(pricingServiceMock.findSomething()).thenReturn(list);

#2


1  

I fixed this problem by adding the following dependency to my application. compile group: 'org.glassfish.jersey.containers.glassfish', name: 'jersey-gf-cdi', version: '2.14'

我通过向我的应用程序添加以下依赖项来解决此问题。编译组:'org.glassfish.jersey.containers.glassfish',名称:'jersey-gf-cdi',版本:'2.14'

Then there is no need to have any "AbstractBinder" related code.

然后就不需要任何“AbstractBinder”相关代码了。

#1


5  

Forget the InjectableProvider. You don't need it. The problem is that the mock service is not the one being injected. It is the one created by the DI framework. So you are checking for changes on the mock service, which has never been touched.

忘记InjectableProvider。你不需要它。问题是模拟服务不是被注入的服务。它是由DI框架创建的。因此,您正在检查模拟服务的更改,这些更改从未被触及过。

So what you need to do is bind the mock with the DI framework. You can simply create another AbstractBinder for testing. It can be a simple anonymous one, where you will bind the mock

所以你需要做的是将模拟与DI框架绑定。您可以简单地创建另一个AbstractBinder进行测试。它可以是一个简单的匿名,你将绑定模拟

ResourceConfig config = new ResourceConfig(PricingResource.class);
config.register(new AbstractBinder() {
    @Override
    protected void configure() {
        bind(pricingServiceMock).to(PricingService.class);
    }
});

Here you are simply binding the mocked service. So the framework will inject the mock into the resource. Now when you modify it in the request, the changes will be seen in the assertion

在这里,您只需绑定模拟服务。所以框架会将模拟注入资源。现在,当您在请求中修改它时,将在断言中看到更改

Oh and you still need to do your when(..).then(..) to initialize the data in the mock service. That is also what you are missing

哦,你仍然需要做你的(..)。然后(..)初始化模拟服务中的数据。这也是你所缺少的

@Test
public void testFindPrices(){
    Mockito.when(pricingServiceMock.findSomething()).thenReturn(list);

#2


1  

I fixed this problem by adding the following dependency to my application. compile group: 'org.glassfish.jersey.containers.glassfish', name: 'jersey-gf-cdi', version: '2.14'

我通过向我的应用程序添加以下依赖项来解决此问题。编译组:'org.glassfish.jersey.containers.glassfish',名称:'jersey-gf-cdi',版本:'2.14'

Then there is no need to have any "AbstractBinder" related code.

然后就不需要任何“AbstractBinder”相关代码了。