深入IDEA Debug问题透析详解

 更新时间:2023年01月08日 15:56:23   作者:郁乎文  
这篇文章主要为大家介绍了深入IDEA Debug问题透析详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

引言

本来通过问题引入,透析 IDEA Debug。通过阅读本文可以学习如何通过 IDEA 的 Debug 功能解决实际问题。本文适合刚刚参加工作并且有使用 Spring 以及 JPA 经验的朋友。

问题引入

最近看了 eclipse 开源的集合 Eclipse Collections,觉得它的 api 相比 JDK 集合 api 简洁,想在实际项目中使用,如下。

JDK api

 // users is List<String> 
 users.stream.map(user -> user.getName()).collect(Collectors.toList());

Eclipse Collections api

 //users is MutableList
 users.collect(user -> user.getName);

项目实际开发中使用集合最多的地方还是来自数据库查询,如下。

JDK api

List<User> findByCity(String city);

我想改成

MutableList<User> findByCity(String city);

然而报错了

org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.util.ArrayList<?>] to type [org.eclipse.collections.api.list.MutableList<?>] for value '[]'; nested exception is java.lang.IllegalArgumentException: Unsupported Collection interface: org.eclipse.collections.api.list.MutableList
    at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:192)
    at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:175)

太长不看直接结论是改成下列代码。

FastList<User> findByCity(String city);

Debug

对代码简单分析

报错的地方都是 Spring 的包,证明我们使用的 Spring Data JPA 访问数据库,事实上也是。

查看类名称,方法名称。 有 convert.ConversionFailedException/convert.support.ConversionUtils.invokeConverter/convert.support.GenericConversionService.convert等等,关键词 convert,我应该联想到这段代码的功能是把什么类型 convert 到什么类型。

再分析报错的那一行我们会更清晰一点。

  • result 是转换的结果。
  • converter是转换器,结合上面的结论,这个类肯定是真正执行转换的类,我们要的核心代码肯定在这里,如果你直接去看的话,它肯定是一个接口,面向接口编程。
  • sourceType 源类型,结合上述分析肯定是原始类型。
  • targetType 目标类型,同上不赘述。

打断点

IDEA 可以直接点击报错 class 定位到源文件,这里我们先点击 ConversionFailedException ,再点击 ConversionUtils.java:47,发现都是报错的异常,对我们没有帮助。最后我们点击 GenericConversionService.java:192,终于看到一行代码了。

Object result = 
    ConversionUtils.invokeConverter(converter, source, sourceType, targetType);

断点分析

执行过程会停留在断点处,我们可以查看上下文变量类的实例。这里我们以 converter 为例。按照数字步骤点击,如下。

可能的 converter 如下:

1. java.lang.String -> java.lang.Enum
2. NO_OP
3. java.lang.Boolean -> java.lang.String
// 等等。。。。。

由于是底层方法,被调用的次数很多,在这个断点停留的次数也很多。很多次不是我们想要的 converter

条件断点

顾名思义 IDEA 会通过我们添加的条件来判断这个断点是否需要被处理。

我们想要的 converter 是什么呢?回到代码分析阶段,我们想要的 convertersourceTypetargetTypetargetType 类型是什么呢?回到我们自己写的代码。

MutableList<User> findByAdress(String address);

可以看到我们需要 targetTypeMutableList class。

下面添加条件断点:

完整的条件如下:

MutableList.class.isAssignableFrom(targetType.getType());

添加成功的标志如下。

单步调试

Debug 模式启动程序,可以看到 IDEA 停留在我们的条件断点上,并且targetType 的类型正是 MutableList

单步调试代码,来到 org.springframework.core.CollectionFactory#createCollection 方法。

部分代码如下:

//省略的代码
// 判断集合类型是不是 ArrayList 或者 List,显然这里不是
else if (ArrayList.class == collectionType || List.class == collectionType) {
  return new ArrayList<>(capacity);
}
//省略的代码
else {
//如果是集合类型的接口 或者 不是集合类型抛出异常
  if (collectionType.isInterface() || !Collection.class.isAssignableFrom(collectionType)) {
    throw new IllegalArgumentException("Unsupported Collection type: " + collectionType.getName());
  }
  try {
  //如果是集合类型的类,直接通过反射实例化。
    return (Collection<E>) ReflectionUtils.accessibleConstructor(collectionType).newInstance();
  }
}

重回代码分析

我们的 targetType 的类型正是 MutableList,而 MutableList 是接口,走读代码可以发现最终会执行下面的代码,最终导致抛出异常。

if (collectionType.isInterface() || !Collection.class.isAssignableFrom(collectionType)) {
    throw new IllegalArgumentException("Unsupported Collection type: " + collectionType.getName());
  }

翻看控制台找到了下面的异常信息,这也侧面反映我们之前找的报错位置不是很精确。我们寻找异常时应该选择最原始的异常信息。

Caused by: java.lang.IllegalArgumentException: Unsupported Collection type: org.eclipse.collections.api.list.MutableList
	at org.springframework.core.CollectionFactory.createCollection(CollectionFactory.java:205)
	at org.springframework.core.convert.support.CollectionToCollectionConverter.convert(CollectionToCollectionConverter.java:81)

继续分析源码可以发现,如果我们定义的类型不是接口,JPA 就会通过反射创建集合,即如下代码:

return (Collection<E>) ReflectionUtils.accessibleConstructor(collectionType).newInstance();

所以我们只需要将 MutableList 换成它的实现类即可,比如 FastList。最终代码如下:

FastList<User> findByCity(String city);

总结

本来通过解决实际问题介绍了 IDEA Debug 功能的使用。还有以下几点需要注意。

  • 查找异常时要定位到最初始的异常,这样往往能迅速处理问题。
  • 本文的问题只有在 sping boot 2.7.0 以下才会出现,高版本已经修复此问题。参见提交 spring data common
  • 使用非 Java 官方集合需要进行转换,有微小的性能损耗,对于常规内存操作来说影响很小。如果查询数据上千上万条时,应该避免转换。

源码

以上就是深入IDEA Debug问题透析详解的详细内容,更多关于IDEA Debug问题透析的资料请关注脚本之家其它相关文章!

相关文章

  • Java多线程实现同时输出

    Java多线程实现同时输出

    这篇文章主要介绍了Java多线程实现同时打印的相关资料,需要的朋友可以参考下
    2016-03-03
  • java根据图片中绿色像素点的多少进行排序

    java根据图片中绿色像素点的多少进行排序

    这篇文章主要介绍了java根据图片中绿色像素点的多少进行排序,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-03-03
  • 基于Java编写一个粽子大作战小游戏

    基于Java编写一个粽子大作战小游戏

    端午节,又称龙舟节、重午节,是中国的传统节日之一,每年农历五月初五庆祝,虽然端午假期已经过去了,小编还是用Java编写了一个粽子大作战小游戏,感兴趣的可以了解一下
    2023-06-06
  • Java实现FIFO任务调度队列策略

    Java实现FIFO任务调度队列策略

    在工作中,很多高并发的场景中,我们会用到队列来实现大量的任务请求。当任务需要某些特殊资源的时候,我们还需要合理的分配资源,让队列中的任务高效且有序完成任务。本文将为大家介绍通过java实现FIFO任务调度,需要的可以参考一下
    2021-12-12
  • Java基于PDFbox实现读取处理PDF文件

    Java基于PDFbox实现读取处理PDF文件

    PDFbox是一个开源的、基于Java的、支持PDF文档生成的工具库,它可以用于创建新的PDF文档,修改现有的PDF文档,还可以从PDF文档中提取所需的内容。本文将具体介绍一下PDFbox读取处理PDF文件的示例代码,感兴趣的可以学习一下
    2022-02-02
  • java基于odbc连接oracle的实现方法

    java基于odbc连接oracle的实现方法

    这篇文章主要介绍了java基于odbc连接oracle的实现方法,结合实例形式分析了连接操作的具体步骤与相关实现技巧,需要的朋友可以参考下
    2016-09-09
  • Java web自定义filter代码实例

    Java web自定义filter代码实例

    这篇文章主要介绍了Java web自定义filter代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • Spring Boot实现邮件发送功能

    Spring Boot实现邮件发送功能

    这篇文章主要为大家详细介绍了Spring Boot实现邮件发送功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • pom文件中${project.basedir}的使用

    pom文件中${project.basedir}的使用

    这篇文章主要介绍了pom文件中${project.basedir}的使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Java打成各种压缩包的方法详细汇总

    Java打成各种压缩包的方法详细汇总

    在工作过程中,需要将一个文件夹生成压缩文件,然后提供给用户下载,下面这篇文章主要给大家介绍了关于Java打成各种压缩包的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-06-06

最新评论