背景

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对象的过程。

为什么要实现对象的序列化和反序列化?

  1. 咱们创立的Java对象被存储在Java堆中,当程序运行完结后,这些对象会被JVM回收。但在事实的利用中,可能会要求在程序运行完结之后还能读取这些对象,并在当前检索数据,这时就须要用到序列化。
  2. 当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...