Spring WebFlux+Thymeleaf @ModelAttribute绑定Enum报构造函数缺失问题咨询
问题:响应式Thymeleaf开发中@ModelAttribute传递枚举参数抛出IllegalStateException异常
相关代码
控制器方法
@PostMapping("/lvl") public Mono<String> getAllByLvl(@ModelAttribute Level level, Model model) { log.info(" level is: " +level); return Mono.just(model) .flatMap(m -> { m.addAttribute("skill",new Skill()); m.addAttribute("skills",new ReactiveDataDriverContextVariable(service.findAllByLevel(level))); return Mono.just(TEMPLATE); }); }
Level枚举定义
@Getter @NoArgsConstructor public enum Level { A, B, C, D; }
前端表单代码
<div> <h4>filter by lvl</h4> <form th:action="@{/skill/lvl}" th:object="${skill}" method="post"> <select> <option value ="${skill.level}">A</option> <option value ="${skill.level}">B</option> <option value ="${skill.level}">C</option> <option value ="${skill.level}">D</option> <input type="submit" value="Submit"/> </select> </form> </div>
Skill实体定义
@Data @Table @NoArgsConstructor @AllArgsConstructor public class Skill implements Persistable<UUID> { @Id private UUID id; @Column("skill_name") private String name; private Level level; private Priority priority; @Column("skill_group_name") private String skillGroupName; @Override public boolean isNew() { boolean result = Objects.isNull(id); this.id = result ? UUID.randomUUID() : this.id; return result; } }
报错信息
java.lang.IllegalStateException: No primary or single public constructor found for class com.gmail.qwertygoog.roadmap.model.Level - and no default constructor found either
at org.springframework.beans.BeanUtils.getResolvableConstructor(BeanUtils.java:250) ~[spring-beans-5.3.7.jar:5.3.7]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
解决方案
问题核心原因有两个:
- 错误对枚举类使用
@NoArgsConstructor注解,且直接用@ModelAttribute绑定枚举类型参数,Spring尝试反射实例化枚举类失败(枚举类为固定实例的单例模式,不允许随意实例化) - 前端表单
select控件缺少name属性,option的value配置错误,导致参数无法正确传递到后端
以下为可落地的解决方法,按实现成本从低到高排序:
方案1:修改参数绑定方式(推荐)
- 控制器参数将
@ModelAttribute Level level改为@RequestParam Level level,Spring会自动将请求中的字符串参数转换为对应的枚举实例 - 前端
select标签添加name="level"属性,修改option的value为对应枚举值:
<select name="level"> <option value="A">A</option> <option value="B">B</option> <option value="C">C</option> <option value="D">D</option> <input type="submit" value="Submit"/> </select>- 删除Level枚举类上的
@NoArgsConstructor注解,该注解不适用于枚举类型。
- 控制器参数将
方案2:沿用表单对象绑定逻辑
你当前表单已经配置了th:object="${skill}",可以直接绑定Skill对象获取枚举参数:- 控制器参数改为
@ModelAttribute Skill skill,方法内直接调用skill.getLevel()获取枚举值 - 前端
select标签添加th:field="*{level}"属性,自动完成参数绑定,可选优化为动态生成枚举选项:
<select th:field="*{level}"> <option th:each="levelItem : ${T(com.gmail.qwertygoog.roadmap.model.Level).values()}" th:value="${levelItem}" th:text="${levelItem}"> </option> <input type="submit" value="Submit"/> </select>- 删除Level枚举类上的
@NoArgsConstructor注解。
- 控制器参数改为
内容的提问来源于stack exchange,提问作者tarmogoyf
相关产品推荐
相关产品推荐

