Cześć,
Problem jest następujący: wysyłam immutable obiekt w formie JSON do kontrolera Spring MVC: przeszedł testy jednostkowe serializacji JSON, więc się serialuzje / deserializuje. Do obsługi JSON używam Jacksona, całość na Spring Boot 1.5.6. Spring MVC szuka bezargumentowego konstruktora, którego nie utworzę, zamiast tego mam builder (wersja Lombok) lub konstruktor argumentowy (plain java i @JsonCreator). Otrzymuje następujący komunikat:
"timestamp": "2017-08-07T21:24:08.349+0000",
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.beans.BeanInstantiationException",
"message": "Failed to instantiate [pl.margor.immutable.NewPerson]: No default constructor found; nested exception is java.lang.NoSuchMethodException: pl.margor.immutable.NewPerson.<init>()",
"path": "/system/mvc/lombok/persons"
}```
Tak wygląda obiekt:
public final class NewPerson {
public static final String NAME ="name";
public static final String SURNAME = "surname";
public static final String PESEL = "pesel";
@JsonProperty(NAME)
private final String name;
@JsonProperty(SURNAME)
private final String surname;
@JsonProperty(PESEL)
private final String pesel;
@JsonCreator
public NewPerson(@JsonProperty(NAME) String name, @JsonProperty(SURNAME) String surname,
@JsonProperty(PESEL) String pesel) {
this.name = name;
this.surname = surname;
this.pesel = pesel;
}
// getters, equals hashcode itp.
}
lub wersja z Lombok (takiej używam docelowo, pierwsza jest tylko po to, aby wykluczyć problem z powodu użycia Lomboka):
@Value
@Builder(toBuilder = true)
@JsonDeserialize(builder = NewPersonLombok.NewPersonLombokBuilder.class)
public final class NewPersonLombok {
public static final String NAME ="name";
public static final String SURNAME = "surname";
public static final String PESEL = "pesel";
@JsonProperty(NAME)
private final String name;
@JsonProperty(SURNAME)
private final String surname;
@JsonProperty(PESEL)
private final String pesel;
@JsonPOJOBuilder(withPrefix = "")
@JsonIgnoreProperties(ignoreUnknown = true)
public static class NewPersonLombokBuilder {
@JsonProperty(NAME)
private String name;
@JsonProperty(SURNAME)
private String surname;
@JsonProperty(PESEL)
private String pesel;
}
}
Tak wygląda kontroler Spring MVC:
@RestController
@RequestMapping("/mvc/persons")
public class SpringMVCNewPerson {
@PostMapping
public void add(NewPerson person) {
System.out.println(person);
}
}
Takie coś nie współpracuje z immutable parameterem. Znalazłem na JIRA issue i wygląda na to, że pracują nad tym i będzie w Springu 5:
https://jira.spring.io/browse/SPR-15199
.
Postanowiłem spróbować wariantu z JAX-RS (Jersey) na Spring Boot: zadziałało out-of-box (dla Lomboka i bez lomboka). Kontroler wygląda tak:
@Service
@Path("/jersey/newpersonlombok")
@Produces("application/json")
@Consumes("application/json")
public class JerseyNewPersonLombok {
@POST
public void create(NewPersonLombok person)
{
System.out.println(person);
}
}
Pytania:
1. Co trzeba zrobić, aby prawidłowo serializować / deserializować immuable klasę w Springu MVC? Jak podczas codziennej pracy z czystym springiem radzicie sobie z tego typu problemem? Może brakuje mi jakiejś pierdoły, przecież to jest podstawowa rzecz, więc powinna być od dawna wspierana.
2. Może warto dać se siana do czasu Spring Boot 2.0 (opartego na Spring 5) i po prostu używać Springa wraz JAX-RS? Aż tak bardzo nie zależy mi na Spring MVC, jeśli mam robić na to jakieś haki. Ale nie ukrywam, że preferuje natywną technologię.
Na chwile obecną przychodzi mi tylko stworzenie CustomArgumentResolvera na potrzeby immutable klas: https://sdqali.in/blog/2016/01/29/using-custom-arguments-in-spring-mvc-controllers/. Zastanawiam się czy można prościej i czy jest gotowiec, który po prostu wykorzysta @JsonCreator / Lombok builder. Na pewno nie wchodz w grę implementacja resolvera dla każdego typu oddzielnie.