web-dev-qa-db-fra.com

UnsatisfiedDependencyException: il n'y avait aucun objet disponible pour l'injection à SystemInjecteeImpl

Il y a des erreurs lors de l'utilisation de DI dans l'application Jersey Rest:

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)

Je suis assez nouveau dans le concept et il semble assez compliqué car il y a quelques exemples qui semblent être obsolètes. Si je comprends bien, il existe plusieurs façons de faire fonctionner l’ID: HK2 natif, Spring/HK2 Bridge. Qu'est-ce qui est plus facile et plus simple à configurer? Comment configurer par programme (pas un fan de XML) pour Jersey 2.x?

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() ...

}

METTRE &AGRAVE; JOUR

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 sortie:

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


Java.lang.AssertionError

Pendant le débogage:

prices.get(0) est un objet Price auquel null est affecté à tous les champs.


UPDATE4:

Ajouté à configure():

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

Maintenant, Junit produit un peu mieux:

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

En effet, la liste prices a le nombre correct de prices mais les champs de tous les prix sont null . Cela conduit à supposer que le problème pourrait être une entité de lecture:

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

Voici comment y remédier

Remplacez la dépendance Moxy par:

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

et ajouter des annotations sur l'objet 'Price'.

@XmlRootElement
@JsonIgnoreProperties(ignoreUnknown = true)
8
J.Olufsen

Oubliez la InjectableProvider. Tu n'en as pas besoin. Le problème est que le service mock n'est pas celui qui est injecté. C'est celui créé par le framework DI. Donc, vous vérifiez les modifications sur le service factice, ce qui n’a jamais été touché.

Donc, ce que vous devez faire est de lier la maquette avec le cadre DI. Vous pouvez simplement créer une autre AbstractBinder à tester. Ce peut être un simple anonyme, où vous lierez la maquette

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

Ici, vous liez simplement le service simulé. Le cadre va donc injecter la maquette dans la ressource. Maintenant, lorsque vous le modifiez dans la requête, les modifications apparaîtront dans l'assertion.

Oh, et vous devez toujours utiliser votre when(..).then(..) pour initialiser les données dans le service fictif. C'est aussi ce qui vous manque

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

J'ai résolu ce problème en ajoutant la dépendance suivante à mon application . compiler le groupe: 'org.glassfish.jersey.containers.glassfish', nom: 'jersey-gf-cdi', version: '2.14'

Il n’est alors pas nécessaire d’avoir un code associé à "AbstractBinder".

1
nrkkalyan