Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

当通过 @AutoOperate#on 从返回值中提取待填充数据时,会偶发空指针异常 #299

Closed
feiyue opened this issue May 30, 2024 · 3 comments
Assignees
Labels
bug Something isn't working
Milestone

Comments

@feiyue
Copy link

feiyue commented May 30, 2024

java.lang.NullPointerException: null
        at cn.crane4j.core.util.CollectionUtils.computeIfAbsent(CollectionUtils.java:430)
        at cn.crane4j.core.util.ReflectUtils.getDeclaredFields(ReflectUtils.java:472)
        at cn.crane4j.core.util.ReflectUtils.lambda$null$12(ReflectUtils.java:499)
        at cn.crane4j.core.util.ReflectUtils.traverseTypeHierarchy(ReflectUtils.java:439)
        at cn.crane4j.core.util.ReflectUtils.lambda$getFields$13(ReflectUtils.java:499)
        at cn.crane4j.core.util.CollectionUtils.computeIfAbsent(CollectionUtils.java:430)
        at cn.crane4j.core.util.ReflectUtils.getFields(ReflectUtils.java:497)
        at cn.crane4j.core.util.ReflectUtils.getField(ReflectUtils.java:484)
        at cn.crane4j.core.support.reflect.ReflectivePropertyOperator$ReflectivePropDesc.findGetterMethod(ReflectivePropertyOperator.java:298)
        at cn.crane4j.core.support.reflect.ReflectivePropertyOperator$ReflectivePropDesc.findGetter(ReflectivePropertyOperator.java:129)
        at cn.crane4j.core.support.reflect.AbstractPropDesc.lambda$obtainInvokerFromCache$1(AbstractPropDesc.java:60)
        at cn.crane4j.core.util.CollectionUtils.computeIfAbsent(CollectionUtils.java:430)
        at cn.crane4j.core.support.reflect.AbstractPropDesc.obtainInvokerFromCache(AbstractPropDesc.java:59)
        at cn.crane4j.core.support.reflect.AbstractPropDesc.getGetter(AbstractPropDesc.java:35)
        at cn.crane4j.core.support.reflect.PropertyOperator.findGetter(PropertyOperator.java:52)
        at cn.crane4j.core.support.reflect.PropertyOperator.readProperty(PropertyOperator.java:39)
        at cn.crane4j.core.support.reflect.ChainAccessiblePropertyOperator.lambda$chainGetter$1(ChainAccessiblePropertyOperator.java:99)
        at cn.crane4j.core.support.auto.DefaultAutoOperateAnnotatedElement.execute(DefaultAutoOperateAnnotatedElement.java:70)
        at cn.crane4j.core.support.aop.MethodResultAutoOperateSupport.afterMethodInvoke(MethodResultAutoOperateSupport.java:80)
        at cn.crane4j.extension.spring.aop.MethodResultAutoOperateAdvisor.invoke(MethodResultAutoOperateAdvisor.java:56)

在新增数据之后,立即调用查询方法,间隔时间比较端,在毫秒级别,就会有一定概率出现这个异常

@feiyue
Copy link
Author

feiyue commented May 30, 2024

同样的场景还会偶发异常:

java.lang.NegativeArraySizeException: -1
        at java.base/java.util.HashSet.toArray(HashSet.java:368)
        at java.base/java.util.LinkedList.addAll(LinkedList.java:412)
        at java.base/java.util.LinkedList.addAll(LinkedList.java:391)
        at cn.crane4j.core.util.CollectionUtils.addAll(CollectionUtils.java:269)
        at cn.crane4j.core.util.ReflectUtils.traverseTypeHierarchy(ReflectUtils.java:444)
        at cn.crane4j.core.util.ReflectUtils.lambda$getFields$13(ReflectUtils.java:499)
        at cn.crane4j.core.util.CollectionUtils.computeIfAbsent(CollectionUtils.java:430)
        at cn.crane4j.core.util.ReflectUtils.getFields(ReflectUtils.java:497)
        at cn.crane4j.core.util.ReflectUtils.getField(ReflectUtils.java:484)
        at cn.crane4j.core.support.reflect.ReflectivePropertyOperator$ReflectivePropDesc.findGetterMethod(ReflectivePropertyOperator.java:298)
        at cn.crane4j.core.support.reflect.ReflectivePropertyOperator$ReflectivePropDesc.findGetter(ReflectivePropertyOperator.java:129)
        at cn.crane4j.core.support.reflect.AbstractPropDesc.lambda$obtainInvokerFromCache$1(AbstractPropDesc.java:60)
        at cn.crane4j.core.util.CollectionUtils.computeIfAbsent(CollectionUtils.java:430)
        at cn.crane4j.core.support.reflect.AbstractPropDesc.obtainInvokerFromCache(AbstractPropDesc.java:59)
        at cn.crane4j.core.support.reflect.AbstractPropDesc.getGetter(AbstractPropDesc.java:35)
        at cn.crane4j.core.support.reflect.PropertyOperator.findGetter(PropertyOperator.java:52)
        at cn.crane4j.core.support.reflect.PropertyOperator.readProperty(PropertyOperator.java:39)
        at cn.crane4j.core.support.reflect.ChainAccessiblePropertyOperator.lambda$chainGetter$1(ChainAccessiblePropertyOperator.java:99)
        at cn.crane4j.core.support.auto.DefaultAutoOperateAnnotatedElement.execute(DefaultAutoOperateAnnotatedElement.java:70)
        at cn.crane4j.core.support.aop.MethodResultAutoOperateSupport.afterMethodInvoke(MethodResultAutoOperateSupport.java:80)
        at cn.crane4j.extension.spring.aop.MethodResultAutoOperateAdvisor.invoke(MethodResultAutoOperateAdvisor.java:56)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:752)
        at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:752)
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:703)

@Createsequence
Copy link
Collaborator

Createsequence commented May 31, 2024

第一个问题看起来是由于 ReflectUtils#getDeclaredFields 方法入参为 null 引起的,不过光靠这个堆栈很难排查出为什么一条路传下来冒出来了一个 null。第二个问题目前还没有什么头绪,感觉可能跟 JDK 版本有关系。

这两个问题我本地都没有复现出来,能否把涉及到的相关代码发上来呢?包括触发填充的方法和它返回的类(即被 @AutoOperate 注解的方法),被填充的类以及它的父类和接口(如果有的话),如果有可运行的实例代码就更好了。

@feiyue

@Createsequence Createsequence added the bug Something isn't working label May 31, 2024
@Createsequence Createsequence self-assigned this May 31, 2024
@Createsequence Createsequence added this to the release 2.9.0 milestone May 31, 2024
@Createsequence
Copy link
Collaborator

目前能确认的是,NegativeArraySizeException 这个问题是由一个 HashSet 的并发修改问题引起的。

具体来说,当进行反射的时候,都会调用 ReflectUtils#traverseTypeHierarchy 这个方法遍历类与其层级结构:

public static void traverseTypeHierarchy(Class<?> beanType, Consumer<Class<?>> consumer) {
      Set<Class<?>> accessed = new HashSet<>();
      Deque<Class<?>> typeQueue = new LinkedList<>();
      typeQueue.add(beanType);
      while (!typeQueue.isEmpty()) {
          Class<?> type = typeQueue.removeFirst();
          accessed.add(type);
          // do something for current type
          consumer.accept(type);
          // then find superclass and interfaces
          Set<Class<?>> declaredSuperClassWithInterface = getDeclaredSuperClassWithInterface(type); // 获取父类与接口
          declaredSuperClassWithInterface.remove(Object.class);
          declaredSuperClassWithInterface.removeAll(accessed);
          CollectionUtils.addAll(typeQueue, declaredSuperClassWithInterface);
      }
  }

问题在于,getDeclaredSuperClassWithInterface 这个方法获取的 Set 集合实际上是从缓存中获取的:

public static Set<Class<?>> getDeclaredSuperClassWithInterface(Class<?> type) {
      return CollectionUtils.computeIfAbsent(DECLARED_SUPER_CLASS_WITH_INTERFACE, type, k -> {
          Set<Class<?>> result = new LinkedHashSet<>();
          Class<?> superClass = type.getSuperclass();
          if (superClass != null) {
              result.add(superClass);
          }
          result.addAll(Arrays.asList(type.getInterfaces()));
          return result;
      });
  }

而在 traverseTypeHierarchy 方法中又修改了获取的 Set 集合,在并发环境下,就又可能出现 Set 内部持有的 Map 集合因为并发操作,最终出现 size 被扣减到 0 以下的情况,此后再调用 Set.toArray 就会出现 NegativeArraySizeException 异常。

不过,上面的空指针目前仍然不能确认是什么原因造成的,只能根据堆栈初步判断是在通过 traverseTypeHierarchy 方法遍历类层级结构的时候出现了一个 null,所以目前怀疑可能也是因为这个并发修改的问题导致的。

这周内会发布 2.8.1 先修复 NegativeArraySizeException 这个问题,升级完以后需要再观察一下空指针这个问题是否会再出现。

@Createsequence Createsequence changed the title 偶发空指针异常 当通过 @AutoOperate#on 从返回值中提取待填充数据时,会偶发空指针异常 Jun 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants