背景
JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion
过后开发的时候报了这个错。
看报错就晓得是因为,json的相互依赖。
比方A 中有一个属性是 B , 而B中也有一个属性是A。 这将造成了json序列化的时候,报相互依赖的异样。
而正好,以后的代码就是这么干的。
@Entitypublic class DivisionalWorksType { @ApiModelProperty("评分我的项目模版") @OneToMany(mappedBy = "divisionalWorksType") @JsonView(ScoringItemTemplateJsonView.class) List<ScoringItemTemplate> scoringItemTemplates = new ArrayList<>();}
@Entitypublic class ScoringItemTemplate { @ManyToOne @ApiModelProperty("所属散布工程模版类型") @JsonView(DivisionalWorksTypeJsonView.class) private DivisionalWorksType divisionalWorksType;}
序列化
可能还有的人不太分明序列化。
什么是序列化与反序列化?
Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列复原为Java对象的过程。
为什么要实现对象的序列化和反序列化?
- 咱们创立的Java对象被存储在Java堆中,当程序运行完结后,这些对象会被JVM回收。但在事实的利用中,可能会要求在程序运行完结之后还能读取这些对象,并在当前检索数据,这时就须要用到序列化。
- 当Java对象通过网络进行传输的时候。因为数据只可能以二进制的模式在网络中进行传输,因而当把对象通过网络发送进来之前须要先序列化成二进制数据,在接收端读到二进制数据之后反序列化成Java对象。
spring boot中如何实现的序列化?
答案是:通过Serializable
接口
能够发现,该接口是空的。
实际上,Serializable
这个标记接口是给java虚拟机参考的,java虚拟机看到这个接口之后,会为该类主动生成一个序列化版本号。
有人感到纳闷:理论在开发中,有时候咱们的类并没有实现Serializable接口。然而为什么依然能通过网络传输?
答案兴许是:类里的属性的根本类型,都根本实现了Serializable接口
能够看到,很多的java类型都实现了Serializable接口。 这时候,就能够很快地将该对象里的属性序列化。
同时Spring框架帮咱们做了另一些一些事件:Spring并不是间接把Object进行网络传输,而是先把Object通过序列化转换成json格局的字符串,而后再进行传输的。
这里的序列化也能够示意为: 序列化就是将对象转换成Json格局的字符串,反序列化就是逆过程,将Json串转换成对象。
Spring框架如何将数据转换成Json?
通常,咱们应用的时候,只须要在Controller类中做如下定义:
@RestController@RequestMapping("User")public class UserController { @PostMapping @ResponseStatus(HttpStatus.CREATED) public User save(@RequestBody User user) { return this.userService.save(user); }}
在 Controller 中应用 @ResponseBody
注解即可返回 Json 格局的数据,
@ResponseBody
的作用,其实是将Controller的办法返回的对象通过转换为指定格局之后,写入到response对象的body区,通常用来返回json或者xml数据
而 @RestController 注解蕴含了 @ResponseBody
注解,所以默认状况下, @RestController即可将返回的数据结构转换成Json格局。
所以,咱们应用@RestController注解即可。
这些注解之所以能够进行 Json 与 Java类 之间的互相转换,就是因为HttpMessageConverter施展着作用。
HttpMessageConverter
前端发来申请后,
1.调用HttpInputMessage从输出流中获取Json字符串
2.在HttpMessageConverter中把Json转换为接口须要的形参类型。
3.在HttpMessageConverter将Json转换为Java实体类
至于HttpMessageConverter 怎么解决,这里就不开展阐明了
解决问题
回到背景问题。
过后去谷歌之后有两个比拟不便的办法:
1.应用@JsonManagedReference
和@JsonBackReference
注解。
将@JsonManagedReference
正文增加到父级模型,即父级的getter办法
@JsonManagedReferencepublic List<DivisionalWorksType> getDivisionalWorksTypes() { return divisionalWorksTypes;}
再将@JsonBackReference
增加到子级模型
@JsonBackReferencepublic DivisionalWorksTemplate getDivisionalWorksTemplate() { return divisionalWorksTemplate;}
2.应用@JsonIgnore
注解
@JsonIgnorepublic List<DivisionalWorksType> getDivisionalWorksTypes() { return divisionalWorksTypes;}
原理:
@JsonManagedReference
:治理援用的一方,能够了解为具备援用的一方,被这个注解的属性序列化时会失常获取@JsonBackReference
:反向援用,能够了解为此属性为反向援用,被这个注解的属性序列化时会疏忽
一个用于父级角色,另一个用于子级角色:
用了这两个注解后,作用即: 一方不被序列化
再来看@JsonIgnore
@JsonIgnore并不是为解决有限递归问题而设计的,它只是疏忽了正文属性不被序列化或反序列化。然而,如果字段之间存在双向链接,则因为@JsonIgnore会疏忽带正文的属性,因而能够防止有限递归。
后话
过后用了@JsonManagedReference 和 @JsonBackReference 这对注解后,便没有再报错了。
然而之后删除这两个注解后,也并不会报错。
或者是因为起初加上了 @JsonView 注解,管制输入输出后的json。
entity:
@ApiModelProperty("散布工程类型") @OneToMany(mappedBy = "divisionalWorksTemplate") @JsonView(DivisionalWorksTypesJsonView.class) List<DivisionalWorksType> divisionalWorksTypes = new ArrayList<>();
controller:
@GetMapping("page")@JsonView(PageJsonView.class)public Page<DivisionalWorksTemplate> page( @RequestParam(required = false) String name, @SortDefault.SortDefaults(@SortDefault(sort = "id", direction = Sort.Direction.DESC)) Pageable pageable) {return this.divisionalWorksTemplateService.page(name, pageable);}private class PageJsonView implements DivisionalWorksTemplate.DivisionalWorksTypesJsonView, DivisionalWorksType.ScoringItemTemplateJsonView {}
配置@JsonView
想管制json格局输入的时候,若在实体中,一个个地加上@JsonView注解太麻烦了;
咱们就能够配置@JsonView
@Configurationpublic class WebConfig implements WebMvcConfigurer { /** * 配置JsonView */ @Override public void configureMessageConverters(final List<HttpMessageConverter<?>> converters) { final ObjectMapper mapper = Jackson2ObjectMapperBuilder.json().defaultViewInclusion(true).build(); converters.add(new MappingJackson2HttpMessageConverter(mapper)); }
这里设置了defaultViewInclusion(true)
,看javadoc说这是一个双向开关,开启将输入没有JsonView注解的属性,false敞开将输入有JsonView注解的属性。
所以咱们就能够反转 @JsonView 注解的应用了。
参考文章: https://blog.csdn.net/qq_4261...