背景
JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion
过后开发的时候报了这个错。
看报错就晓得是因为,json的相互依赖。
比方A 中有一个属性是 B , 而B中也有一个属性是A。 这将造成了json序列化的时候,报相互依赖的异样。
而正好,以后的代码就是这么干的。
@Entity
public class DivisionalWorksType {
@ApiModelProperty("评分我的项目模版")
@OneToMany(mappedBy = "divisionalWorksType")
@JsonView(ScoringItemTemplateJsonView.class)
List<ScoringItemTemplate> scoringItemTemplates = new ArrayList<>();
}
@Entity
public 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办法
@JsonManagedReference
public List<DivisionalWorksType> getDivisionalWorksTypes() {
return divisionalWorksTypes;
}
再将@JsonBackReference
增加到子级模型
@JsonBackReference
public DivisionalWorksTemplate getDivisionalWorksTemplate() {
return divisionalWorksTemplate;
}
2.应用@JsonIgnore
注解
@JsonIgnore
public 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
@Configuration
public 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…
发表回复