JSON.toJSONString使用异常分析

 更新时间:2023年09月11日 15:36:29   作者:土豆肉丝盖浇饭  
这篇文章主要为大家介绍了JSON.toJSONString使用异常分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

先说说坑

JSON.toString在序列化对象时,默认通过的是get*()方法来查找属性,而不是具体某个属性,同时回忽略transient注解的属性。

测试案例如下

public class FastJsonTest {
    public static void main(String[] args) {
        Person person = new Person();
        person.setBirth(new Date());
        System.out.println(JSON.toJSONString(person));
    }
    public static class Person{
        private Integer age =123;
        private transient Date birth;
        public Date getBirth() {
            return birth;
        }
        public void setBirth(Date birth) {
            this.birth = birth;
        }
        public String getName(){
            return "scj";
        }
    }
}

输出

{"name":"scj"}

问题发生

最近在迭代一个老项目,升级中间件框架版本(不升级不给打包部署)后,在项目启动的时候居然抛出以下异常

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.masaike.yama.platform.domain.model.product.ProductServiceEntity.properties, could not initialize proxy - no Session
    at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:582)
    at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:201)
    at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:145)
    at org.hibernate.collection.internal.PersistentBag.size(PersistentBag.java:261)
    at com.masaike.yama.platform.infrastructure.converter.ProductServiceConverterImpl.productServicePropertyVOListToProductServicePropertyDTOList(ProductServiceConverterImpl.java:139)
    at com.masaike.yama.platform.infrastructure.converter.ProductServiceConverterImpl.map(ProductServiceConverterImpl.java:50)
    at com.masaike.yama.platform.infrastructure.converter.ProductServiceConverterImpl.entityListToDTOList(ProductServiceConverterImpl.java:32)
    at com.masaike.yama.platform.query.ProductServiceQueryServiceImpl.getAllProductService(ProductServiceQueryServiceImpl.java:43)
    at com.alibaba.fastjson.serializer.ASMSerializer_12_ProductServiceQueryServiceImpl.write(Unknown Source)
    at com.alibaba.fastjson.serializer.JSONSerializer.writeWithFieldName(JSONSerializer.java:333)
    at com.alibaba.fastjson.serializer.ASMSerializer_1_InterfaceInfo.write(Unknown Source)
    at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:285)
    at com.alibaba.fastjson.JSON.toJSONString(JSON.java:745)
    at com.alibaba.fastjson.JSON.toJSONString(JSON.java:683)
    at com.alibaba.fastjson.JSON.toJSONString(JSON.java:648)
    at com.alibaba.dubbo.config.masaikehttp.ExportedInterfaceManager.addInterface(ExportedInterfaceManager.java:104)//关键点
    at com.alibaba.dubbo.config.ServiceConfig.doExport(ServiceConfig.java:321)
    at com.alibaba.dubbo.config.ServiceConfig.export(ServiceConfig.java:218)
    at com.alibaba.dubbo.config.spring.ServiceBean.onApplicationEvent(ServiceBean.java:123)
    at com.alibaba.dubbo.config.spring.ServiceBean.onApplicationEvent(ServiceBean.java:49)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:400)
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:354)
    at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:886)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.finishRefresh(ServletWebServerApplicationContext.java:161)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:754)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:386)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1242)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1230)
    at com.masaike.yama.bootstrap.BootStrapApplication.main(BootStrapApplication.java:36)

这个一个使用JPA时常见问题:延迟加载的时候session不存在

关于延迟加载no-session问题,可以看如何解决JPA延迟加载no Session报错

从日志定位到抛出异常的方法为

@Override
@Transactional(rollbackFor = Exception.class)
public List<XXDTO> getAllXX() {
    List<XXEntity> result = xXQueryRepository.findAll();
    //下面的converter会触发延迟加载
    return XXConverter.INSTANCE.entityListToDTOList(result);
}

这边存在两个迷惑性行为

  • 启动的时候怎么调用了getAllXX方法
  • getAllXX我加了@Transactional,理论上是有session的

问题排查

精简上面的异常栈

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.masaike.yama.platform.domain.model.product.ProductServiceEntity.properties, could not initialize proxy - no Session
    at com.masaike.yama.platform.infrastructure.converter.ProductServiceConverterImpl.productServicePropertyVOListToProductServicePropertyDTOList(ProductServiceConverterImpl.java:139)
    at com.masaike.yama.platform.infrastructure.converter.ProductServiceConverterImpl.map(ProductServiceConverterImpl.java:50)
    at com.masaike.yama.platform.infrastructure.converter.ProductServiceConverterImpl.entityListToDTOList(ProductServiceConverterImpl.java:32)
    at com.masaike.yama.platform.query.ProductServiceQueryServiceImpl.getAllProductService(ProductServiceQueryServiceImpl.java:43)
    at com.alibaba.fastjson.JSON.toJSONString(JSON.java:648)
    at com.alibaba.dubbo.config.masaikehttp.ExportedInterfaceManager.addInterface(ExportedInterfaceManager.java:104)//关键点
    at com.alibaba.dubbo.config.ServiceConfig.doExport(ServiceConfig.java:321)
    at com.alibaba.dubbo.config.ServiceConfig.export(ServiceConfig.java:218)

可以复盘出问题发生的现场

dubbo服务进行export的时候调用了ExportedInterfaceManager.addInterface方法,而在addInterface方法中调用的JSON.toJSONString方法触发了ProductServiceQueryServiceImpl.getAllProductService方法

在看了ExportedInterfaceManager.addInterface源码之后,问题的起因浮出水面

ExportedInterfaceManager这个类是用来针对接口暴露http服务时收集元数据使用

public synchronized void addInterface(Class<?> interfaceCls, Object obj) {
    //如果是代理类获取代理类对象
    obj = getObjectTarget(obj);//获取原始对象
    //...
    InterfaceInfo interfaceInfo = new InterfaceInfo();
    interfaceInfo.setInterfaceName(interfaceName);
    interfaceInfo.setRef(obj);//致命之处
    //...
    logger.info(String.format("start to addInterface into interfaceMap,interfaceName[%s],interfaceInfo[%s]",interfaceName, JSON.toJSONString(interfaceInfo)));//致命之处
    // add interface info to map
    interfaceMap.put(interfaceName, interfaceInfo);
}

对于第一个问题,InterfaceInfo的ref指向ProductServiceQueryServiceImpl,在打印日志的时候,JSON.toJSONString触发了ProductServiceQueryServiceImpl中的get方法

而对于第二个问题,obj = getObjectTarget(obj);这段代码会获取代理的原始对象,导致事务失效。

问题危害

抛开获取原始对象这个逻辑不说,这个bug的致命之处在于,他会调用所暴露dubbo接口中所有get*()格式的方法

问题解决

解决方式很简单,有以下两种

  • 不要序列化ref(加fastjson注解或字段加transient)
  • 去掉打印日志逻辑

在反馈这个问题后,中间件团队的改动如下

以上就是JSON.toJSONString使用异常分析的详细内容,更多关于JSON.toJSONString异常的资料请关注脚本之家其它相关文章!

相关文章

  • 一篇文章带你深入了解Java基础(2)

    一篇文章带你深入了解Java基础(2)

    这篇文章主要给大家介绍了关于Java中方法使用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-08-08
  • mybatis Example Criteria like 模糊查询问题

    mybatis Example Criteria like 模糊查询问题

    这篇文章主要介绍了mybatis Example Criteria like 模糊查询问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • springboot+chatgpt+chatUI Pro开发智能聊天工具的实践

    springboot+chatgpt+chatUI Pro开发智能聊天工具的实践

    本文主要介绍了springboot+chatgpt+chatUI Pro开发智能聊天工具的实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • Java中的Unsafe工具类使用详解

    Java中的Unsafe工具类使用详解

    这篇文章主要介绍了Java中的Unsafe工具类使用详解,Unsafe是jdk提供的一个直接访问操作系统资源的工具类(底层c++实现),它可以直接分配内存,内存复制,copy,提供cpu级别的CAS乐观锁等操作,需要的朋友可以参考下
    2023-12-12
  • SpringBoot实现devtools实现热部署过程解析

    SpringBoot实现devtools实现热部署过程解析

    这篇文章主要介绍了SpringBoot实现devtools实现热部署过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • JAVA版排序算法之快速排序示例

    JAVA版排序算法之快速排序示例

    这篇文章主要介绍了JAVA版排序算法之快速排序,结合实例形式分析了基于java版的遍历、递归实现快速排序功能的具体步骤与操作技巧,需要的朋友可以参考下
    2017-01-01
  • Spring中的@EnableConfigurationProperties使用方式以及作用详解

    Spring中的@EnableConfigurationProperties使用方式以及作用详解

    这篇文章主要介绍了Spring中的@EnableConfigurationProperties使用方式以及作用详解,使用了 @ConfigurationProperties 注解的配置类生效,将该类注入到 IOC 容器中,交由 IOC 容器进行管理,此时则不用再配置类上加上@Component,需要的朋友可以参考下
    2024-01-01
  • SpringController返回值和异常自动包装的问题小结

    SpringController返回值和异常自动包装的问题小结

    今天遇到一个需求,在不改动原系统代码的情况下,将Controller的返回值和异常包装到一个统一的返回对象中去,下面通过本文给大家介绍SpringController返回值和异常自动包装的问题,需要的朋友可以参考下
    2024-03-03
  • 详细总结Java创建文件夹的方法及优缺点

    详细总结Java创建文件夹的方法及优缺点

    很多小伙伴都不知道如何用Java创建文件夹,今天给大家整理了这篇文章,文中有非常详细的方法介绍及方法的优缺点,对正在学习java的小伙伴们有很好地帮助,需要的朋友可以参考下
    2021-05-05
  • Java数据结构之加权无向图的设计实现

    Java数据结构之加权无向图的设计实现

    加权无向图是一种为每条边关联一个权重值或是成本的图模型。这种图能够自然地表示许多应用。这篇文章主要介绍了加权无向图的设计与实现,感兴趣的可以了解一下
    2022-11-11

最新评论