【问题标题】:Is it possible to bind a Spring controller request parameter to an object with multiple constructors?是否可以将 Spring 控制器请求参数绑定到具有多个构造函数的对象?
【发布时间】:2021-09-14 01:50:44
【问题描述】:

我有@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:没有主要或单一公共 为类找到构造函数 com.example.domain.DateRange - 并且没有默认值 构造函数在 org.springframework.beans.BeanUtils.getResolvableConstructor(BeanUtils.java:250) ~[spring-beans-5.3.5.jar:5.3.5]

DateRange 有一个主构造函数DateRange(YearMonth, YearMonth)。另一个构造函数调用 this。

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

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

【问题讨论】:

  • 该类有 2 个构造函数,spring 无法确定使用哪一个。你可以做的是创建一个非public no-args 构造函数,Spring 将使用它来构造对象。或者使用Period 删除另一个构造函数,例如用工厂方法替换它。这样就可以使用一个构造函数。
  • 在构造函数问题得到解决后,@M.Deinum 提到的2021-01-01 也不会被转换/解析为YearMonth。输入日期字符串的格式应为2021-01,这样代码才能正常工作。
  • @M. Deinum - 谢谢。这很好,解决了我的问题。特别是,我将DateRange(YearMonth, Period) 构造函数转换为工厂方法。如果您发布答案,我会接受。
  • @R.G. - 谢谢你的评论。你是对的。我更改了我的客户以传递例如2021-01。我很惊讶 Spring 能够将 String 直接绑定到 YearMonth 实例,而无需将 String 绑定到 YearMonth Converter@DateTimeFormat。我看到其他帖子说需要Converter。我猜我使用的 Spring 版本 2.4 增加了支持。

标签: spring spring-boot spring-mvc apache-commons-beanutils


【解决方案1】:

堆栈跟踪表明 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 起可用)与 YearMonth 进行转换。所以你不需要 @DateTimeFormat 这样的注解。

【讨论】:

  • 感谢您还添加了有关 YearMonthFormatter 的信息。非常感激!很有帮助的答案。
猜你喜欢
  • 1970-01-01
  • 2021-05-09
  • 1970-01-01
  • 2019-01-21
  • 2013-02-08
  • 2014-11-04
  • 2019-06-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多