webentwicklung-frage-antwort-db.com.de

UnsatisfiedDependencyException: Bei SystemInjecteeImpl war kein Objekt für die Injektion verfügbar

Bei der Verwendung von DI in der Jersey Rest-Anwendung treten Fehler auf:

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)

Ich bin ziemlich neu in dem Konzept und es erscheint ziemlich kompliziert, da es einige Beispiele gibt, die als veraltet gelten. Wie ich es verstehe, gibt es ein paar Möglichkeiten, um DI zum Laufen zu bringen: native HK2, Spring/HK2 Bridge. Was ist einfacher und unkomplizierter zu konfigurieren? Wie programmgesteuert (kein XML-Fan) für Jersey 2.x einrichten?

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

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


Java.lang.AssertionError

Beim Debuggen:

prices.get(0) ist ein Price-Objekt, dem alle Felder null zugewiesen sind.


UPDATE4:

Zu configure() hinzugefügt:

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

Nun ist Junit etwas besser:

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

In der Tat hat die Liste prices die korrekte Anzahl von prices, aber die Felder aller Preise sind null . Dies führt zu der Annahme, dass das Problem möglicherweise das Lesen der Entität ist:

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

So beheben Sie es

Ändern Sie die Moxy-Abhängigkeit in:

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

fügen Sie Anmerkungen zum 'Price'-Objekt hinzu.

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

Vergiss die InjectableProvider. Du brauchst es nicht. Das Problem ist, dass der Dienst Mock nicht der Dienst ist, der eingespritzt wird. Es ist die, die vom DI-Framework erstellt wurde. Sie prüfen also, ob Änderungen an dem Mock-Service vorgenommen wurden, der noch nie angesprochen wurde.

Sie müssen also den Mock mit dem DI-Framework binden. Sie können einfach ein weiteres AbstractBinder zum Testen erstellen. Es kann ein einfacher anonymer sein, bei dem Sie den Mock binden

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

Hier binden Sie einfach den nachgeahmten Service. Das Framework wird also den Mock in die Ressource einfügen. Wenn Sie es jetzt in der Anfrage ändern, werden die Änderungen in der Assertion angezeigt

Oh, und Sie müssen immer noch Ihre when(..).then(..) ausführen, um die Daten im Mock-Service zu initialisieren. Das ist auch was Sie vermissen

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

Ich habe dieses Problem behoben, indem ich meiner Anwendung die folgende Abhängigkeit hinzufügte . Compile-Gruppe: 'org.glassfish.jersey.containers.glassfish', Name: 'jersey-gf-cdi', Version: '2.14'

Dann brauchen Sie keinen mit "AbstractBinder" zusammenhängenden Code.

1
nrkkalyan

Der gleiche Fehler tritt auf, wenn beans.xml fehlt oder falsch platziert ist. Das hat mir geholfen: Wo soll beans.xml platziert werden?

0
luca.vercelli