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

新手上路,请多包涵

在 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)

我对这个概念很陌生,它看起来很复杂,因为有些例子似乎已被弃用。据我所知,有几种方法可以使 DI 工作:本地 HK2、Spring/HK2 Bridge。什么配置起来更容易、更直接?如何以编程方式(不喜欢 XML)为 Jersey 2.x 设置?

资源配置

import org.glassfish.jersey.server.ResourceConfig;

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

抽象活页夹

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

定价资源

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

定价服务

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

}


更新

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

}

更新3:

 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 输出:

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

java.lang.AssertionError

调试时:

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


更新4:

添加到 configure()

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

现在 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}]

确实列表 prices 有正确的数字 prices 但所有价格字段都是 null 。这导致假设问题可能是阅读实体:

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

这是修复方法

将 Moxy 依赖项更改为:

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

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

 @XmlRootElement
@JsonIgnoreProperties(ignoreUnknown = true)

原文由 J.Olufsen 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1k
2 个回答

忘记 InjectableProvider 。你不需要它。问题是 模拟 服务不是被注入的服务。它是由 DI 框架创建的。所以你正在检查模拟服务的变化,它从未被触及过。

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

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

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

哦,您仍然需要执行 when(..).then(..) 来初始化模拟服务中的数据。这也是你所缺少的

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

原文由 Paul Samsotha 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏