Comunicación entre microservicios con genéricos abstractos utilizando el descanso primaveral

Similar a esto Estoy tratando de intercambiar información entre dos microservicios usando Spring RestTemplate. La diferencia en mi caso es que estoy trabajando con una clase abstracta como parámetro genérico, y estoy encontrando dificultades.

Aquí hay un ejemplo de mi clase:

public  Page getAllAccounts() {   // not working
    ParameterizedTypeReference> responseType = new ParameterizedTypeReference>() { };
    ResponseEntity> result = restTemplate.exchange("http://localhost:8080/api/v1/accounts/", HttpMethod.GET, null, responseType);

    Page accounts = result.getBody();
    
    return accounts;
}

La función detrás de la llamada API tiene el tipo de retorno public Page getAccounts(...) {...}. El tipo T puede ser cualquier clase que se extiende Account, como hay múltiples Account- Tipos disponibles. Este método funciona bien para las clases no abstractas (por ejemplo, Page), pero falla con clases abstractas.

Entonces, ¿cómo puedo comunicarme entre un microservicio que funciona en el puerto? 8081 y otro microservicio en puerto 8080, utilizando "generales abstractos" (o un concepto similar) con Spring RestTemplate?

Cuando intento llamar a la función, recibo el siguiente mensaje de error:

This application has no explicit mapping for /error, so you are seeing this as a fallback.
Fri Dec 18 14:55:33 CET 2020
There was an unexpected error (type=Internal Server Error, status=500).
Type definition error: [simple type, class org.springframework.data.domain.Page]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.springframework.data.domain.Page` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information at [Source: (PushbackInputStream); line: 1, column: 1]
org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.springframework.data.domain.Page]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.springframework.data.domain.Page` (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information
 at [Source: (PushbackInputStream); line: 1, column: 1]
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:282)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:243)
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:105)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1034)
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1017)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:777)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:710)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:630)
    at de.rwth.swc.lab.ws2021.daifu.businesslogic.services.AccountService.getAllAccounts(AccountService.java:50)
    at de.rwth.swc.lab.ws2021.daifu.businesslogic.api.AccountController.getAllAccounts(AccountController.java:47)
... (rest shortend for better readability)

Pregunta hecha hace 3 años, 4 meses, 29 días - Por syntaxsensei8acf


2 Respuestas:

  • El error que estás viendo se debe a que Jackson (la biblioteca utilizada por Spring para la serialización y deserialización de objetos JSON) está teniendo dificultades para deserializar la respuestas de tu API a un objeto Page genérico debido a que Page es una clase abstracta y no puede ser instanciada directamente.

    Para resolver este problema, puedes crear un tipo concreto que extienda Page para cada tipo de cuenta concreto que tengas (por ejemplo, CustomerAccountPage, AdminAccountPage, etc.). Luego, puedes utilizar esos tipos concretos en lugar de Page abstracta en tu código.

    Aquí hay un ejemplo de cómo podrías modificar tu método getAllAccounts para utilizar un tipo concreto:

    java
    public Page getAllAccounts() {
        ParameterizedTypeReference responseType = new ParameterizedTypeReference() {};
        ResponseEntity result = restTemplate.exchange("http://localhost:8080/api/v1/accounts/", HttpMethod.GET, null, responseType);
    
        CustomerAccountPage accounts = result.getBody();
    
        return accounts;
    }
    

    En este ejemplo, asumimos que CustomerAccountPage es un tipo concreto que extiende Page y representa una página de cuentas de cliente. Puedes seguir el mismo enfoque para otros tipos de cuentas concretas que necesites.

    Al utilizar tipos concretos en lugar de la clase abstracta Page, Jackson podrá deserializar correctamente la respuesta de la API a un objeto concreto y evitará el error de falta de constructor predeterminado en una clase abstracta.

    Respondida el Dec 19, 2020 a las 14:14 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • El error que estás experimentando es causado por el hecho de que la clase Page es una clase abstracta y no se puede instanciar directamente. Para resolver este problema, puedes utilizar un tipo comodín en tu referencia de tipo parametrizado:

    java
    ParameterizedTypeReference> responseType = new ParameterizedTypeReference>() { };
    

    Esto le dice a Spring que el tipo genérico T puede ser cualquier subclase de Account, lo que permitirá que se deserialice el tipo de retorno de la función correctamente.

    Aquí tienes el código corregido:

    java
    public  Page getAllAccounts() {
        ParameterizedTypeReference> responseType = new ParameterizedTypeReference>() { };
        ResponseEntity> result = restTemplate.exchange("http://localhost:8080/api/v1/accounts/", HttpMethod.GET, null, responseType);
    
        Page accounts = result.getBody();
    
        return accounts;
    }
    

    Espero que esto resuelva tu problema. No dudes en preguntar si tienes alguna otra duda.

    Respondida el Dec 19, 2020 a las 14:25 - por Gemini

    Votos positivos: 0 | Votos negativos: 0