Spring控制器请求参数绑定多构造器对象如何指定使用的构造方法
问题原因说明
Spring MVC在绑定GET请求的自定义对象参数时,默认会优先查找类的无参构造方法实例化对象,再通过Setter注入属性;如果没有无参构造,就要求类只有唯一的公有构造方法。你当前的DateRange有两个公有构造方法,也没有无参构造,所以Spring无法确定使用哪个构造方法,就抛出了对应的异常。
解决方案
方法1:使用@ConstructorProperties指定主构造方法
这是最贴合你需求的方案,不需要修改原有类结构,直接在你要优先使用的双参数构造方法上添加JDK自带的java.beans.ConstructorProperties注解,明确构造参数对应类的属性名即可:
public class DateRange { @DateTimeFormat(iso=DateTimeFormat.ISO.DATE) private YearMonth start; @DateTimeFormat(iso=DateTimeFormat.ISO.DATE) private YearMonth end; // 加该注解指定优先使用该构造方法,参数对应start、end属性 @ConstructorProperties({"start", "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); } }
Spring会自动识别该注解,使用指定构造方法完成参数绑定,传入的2021-01-01格式的日期会被@DateTimeFormat自动转换为YearMonth类型,不需要额外处理日期格式。如果是JDK9+模块化环境无法使用该注解,可以使用下面的方法2。
方法2:补充无参构造+Setter方法
如果你允许修改DateRange的结构,直接补充无参构造方法和对应属性的Setter,Spring会先实例化对象再通过Setter注入属性,也能解决问题:
public class DateRange { @DateTimeFormat(iso=DateTimeFormat.ISO.DATE) private YearMonth start; @DateTimeFormat(iso=DateTimeFormat.ISO.DATE) private YearMonth end; // 新增无参构造 public DateRange() { } 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); } // 新增Setter方法 public void setStart(YearMonth start) { this.start = start; } public void setEnd(YearMonth end) { this.end = end; } }
方法3:自定义参数解析器(适配复杂场景)
如果后续有更复杂的DateRange绑定逻辑,可以自定义参数解析器手动控制实例化逻辑:
- 实现解析器
public class DateRangeArgumentResolver implements HandlerMethodArgumentResolver { @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType().equals(DateRange.class); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { String startStr = webRequest.getParameter("start"); String endStr = webRequest.getParameter("end"); YearMonth start = YearMonth.parse(startStr, DateTimeFormatter.ISO_DATE); YearMonth end = YearMonth.parse(endStr, DateTimeFormatter.ISO_DATE); // 手动选择你需要的构造方法创建对象 return new DateRange(start, end); } }
- 注册到Spring MVC配置
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(new DateRangeArgumentResolver()); } }
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

