I have @RestController with the following mapping:
@GetMapping(value = "/periods"})
public PeriodInfoDto get(DateRange dateRange)
DateRange has three constructors:
public class DateRange {
@DateTimeFormat(iso=DateTimeFormat.ISO.DATE)
private YearMonth start;
@DateTimeFormat(iso=DateTimeFormat.ISO.DATE)
private YearMonth end;
public DateRange(YearMonth start, YearMonth end) {
this.start = start;
this.end = end;
}
public DateRange(YearMonth end, Period period) {
this(end.minus(period.minusMonths(1)), end);
}
When the client sends a request, the following exception is reported:
java.lang.IllegalStateException: No primary or single public constructor found for class com.example.domain.DateRange - and no default constructor found either at org.springframework.beans.BeanUtils.getResolvableConstructor(BeanUtils.java:250) ~[spring-beans-5.3.5.jar:5.3.5]
DateRange has a single primary constructor, DateRange(YearMonth, YearMonth). The other constructor call this.
The client passes in a full date string such as 2021-01-01 for end / start date request params, but I only care about the month and year.
Is there a way to tell Spring to use DateRange(YearMonth start, YearMonth end) when binding the request?
The stack trace indicates that Spring needs a so called primary constructor. For Java this means a single constructor that can be used. As you have 2 this mechanism fails.
You can work around this by removing the constructor using a
YearMonthandPeriodand move that to a factory method and use that instead of the constructor.When using the format 2021-09 instead of a full date, Spring will automatically use the
YearMonthFormatter(available since Spring 4.2) to convert from/to aYearMonth. So you don't need the@DateTimeFormatannotation that way.