Gson临时文件被删除造成的stackOverflowError

错误内容

今天使用Gson序列化的时候出现了stackOverflowError的错误,内容如下:

at com.google.gson.Gson.getAdapter(Gson.java:416)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getFieldAdapter(ReflectiveTypeAdapterFactory.java:135)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.<init>(ReflectiveTypeAdapterFactory.java:105)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:104)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:160)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:96)
	at com.google.gson.Gson.getAdapter(Gson.java:416)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getFieldAdapter(ReflectiveTypeAdapterFactory.java:135)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.<init>(ReflectiveTypeAdapterFactory.java:105)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:104)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:160)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:96)
	at com.google.gson.Gson.getAdapter(Gson.java:416)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getFieldAdapter(ReflectiveTypeAdapterFactory.java:135)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.<init>(ReflectiveTypeAdapterFactory.java:105)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:104)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:160)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:96)
	at com.google.gson.Gson.getAdapter(Gson.java:416)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getFieldAdapter(ReflectiveTypeAdapterFactory.java:135)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.<init>(ReflectiveTypeAdapterFactory.java:105)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:104)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:160)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:96)
	at com.google.gson.Gson.getAdapter(Gson.java:416)
	at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getFieldAdapter(ReflectiveTypeAdapterFactory.java:135)

这个问题我看了很久都没找出问题,终于在同事的帮助下解决了

场景再现

当时是打算写一个切面打印Log,将controller接收的参数使用Gson序列化,注意这个参数是MultipartFile的文件类型,场景如下:

HelloController类:

@Slf4j
@RestController
@RequestMapping("hello")
public class HelloController {

    @Autowired
    private HelloService helloService;

    @PostMapping("dosomething")
    public Result hello(@RequestParam("file") MultipartFile multipartFile) {
           // doSomething();
        }
    }

Log切面:

@Aspect
@Component
@Slf4j
public class LogAspect {

    @Pointcut("execution(* com.controller..*.*(..))")
    private void helloController() {
    }
    
    @Around("helloController()")
    public Object aroundQueryLog(ProceedingJoinPoint pjp) throws Throwable {
        // 逻辑处理。。。。。。。。。。。
        Object[] args = pjp.getArgs(); // 获取HelloController的各个参数
        for (Object arg : args) {
            log.info(new Gson().toJson(arg)); // 序列化每一个参数值
        }
        // 逻辑处理。。。。。。。。。。
    }

}

问题再现并分析

问题:
每次调用hello/dosomething资源,都会报错,就是上文提到的stackOverflowError

分析:

  • 对于MultipartFile类型的参数,Tomcat会在一个特定的目录下临时存放这个文件,正常情况下当访问结束后还会存在一段时间
  • 由于某些原因切面走到序列化参数值之前,controller已经执行完毕并响应结果
  • 公司的服务器在访问结束后,立马删除了这个临时文件

所以当序列化参数值的时候,MultipartFile对象对应的临时文件不存在了,相当于序列化了一个寂寞。
那么导致出现stackOverflowError的元凶就找出来了。‘

从Gson User Guide的解释上看:

If a field is marked transient, (by default) it is ignored and not included in the JSON serialization or deserialization.

By default, if you mark a field as transient, it will be excluded. As
well, if a field is marked as “static” then by default it will be
excluded.

像这种只存在瞬间的对象,最好排除在序列化之外(这里有一个瞬态和静态的概念)

ps : 不知道是否理解正常,但是当看见这句话时,确实是想到了有一个存在临时文件的可能~

解决方案

  1. 要么不序列化带有MultipartFile的参数
  2. 要么使用Base64将输出流转化为Base64格式进行文件的传递与接收

可以根据这个网站进行操作,也可以看我下面这段代码
https://blog.csdn.net/weixin_45562753/article/details/108255263

/**
 * 将文件转成base64编码
 * 
 */
public String getBase64Code(ByteArrayOutputStream byteArrayOutputStream){
  try {
    byte[] bytes = byteArrayOutputStream.toByteArray();
    String base64Str = Base64.getEncoder().encodeToString(bytes);
    return base64Str;
  } catch (Exception e) {
    e.printStackTrace();
  }
  return null;
}

总结

Gson能够导致stackOverflowError的情况,我就见过两种:

  1. 循环引用造成,这个情况的博客有很多了
  2. 遇见临时文件被删除(临时性的)导致
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值