是否可以将 Spring 控制器请求参数绑定到具有多个构造函数的对象?

Is it possible to bind a Spring controller request parameter to an object with multiple constructors?

@RestController 具有以下映射:

@GetMapping(value = "/periods"})
public PeriodInfoDto get(DateRange dateRange)

DateRange 具有三个构造函数:

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);
    }

客户端发送请求时,报如下异常:

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 有一个主构造函数,DateRange(YearMonth, YearMonth)。另一个构造函数调用这个。

客户端传入一个完整的日期字符串,例如 2021-01-01 作为结束/开始日期请求参数,但我只关心月份和年份。

绑定请求时有没有办法告诉Spring使用DateRange(YearMonth start, YearMonth end)

堆栈跟踪表明 Spring 需要一个所谓的主构造函数。对于 Java 这意味着可以使用单个构造函数。因为你有 2,所以这个机制失败了。

您可以通过使用 YearMonthPeriod 删除构造函数并将其移动到工厂方法并使用它而不是构造函数来解决此问题。

public class DateRange {

    private YearMonth start;    
    private YearMonth end;

    public DateRange(YearMonth start, YearMonth end) {
           this.start = start;
           this.end = end;
    }
            
    public static DateRange of(YearMonth end, Period period) {
        return new DateRange(end.minus(period.minusMonths(1)), end);            
    }
}

当使用格式 2021-09 而不是完整日期时,Spring 将自动使用 YearMonthFormatter(自 Spring 起可用4.2) 将from/to转换成YearMonth。所以你不需要那样的 @DateTimeFormat 注释。