jmockdata icon indicating copy to clipboard operation
jmockdata copied to clipboard

嵌套泛型会导致堆栈溢出(StackOverflowError)

Open a483210 opened this issue 4 years ago • 1 comments

1、示例

OuterBean

public class OuterBean<T> {

    private T result;

    public T getResult() {
        return result;
    }

    public void setResult(T result) {
        this.result = result;
    }
}

ArrayBean

public class ArrayBean<T> {

    private List<T> list;

    public List<T> getList() {
        return list;
    }

    public void setList(List<T> list) {
        this.list = list;
    }
}

代码

OuterBean<ArrayBean<BasicBean>> outerBean = JMockData.mock(new TypeReference<OuterBean<ArrayBean<BasicBean>>>() {
        });

2、原因

  • 1.MockConfig.init只对当前类泛型做了缓存,导致嵌套泛型无法解析,通过getVariableType获取为null
  • 2.BaseMocker和ArrayMocker中对于TypeVariable处理获取为name,导致相同名称如T返回相同的Type导致StackOverflowError

3、修复建议

MockConfig.init修改如下

public MockConfig init(Type type) {
    if (!(type instanceof ParameterizedType)) {
        return this;
    }

    ParameterizedType paramType = ((ParameterizedType) type);

    Class<?> clazz = (Class<?>) paramType.getRawType();
    Type[] types = paramType.getActualTypeArguments();

    Class<?> supperClazz = clazz;
    while (supperClazz != null && !ReflectionUtils.isSystemClass(clazz.getName())) {
        TypeVariable<?>[] typeVariables = supperClazz.getTypeParameters();
        for (int index = 0; index < typeVariables.length; index++) {
            TypeVariable<?> typeVariable = typeVariables[index];

            String name = ReflectionUtils.getTypeVariableName(typeVariable);

            Type nestType = types[index];

            typeVariableCache.put(name, nestType);

            if (supperClazz == clazz) {
                init(nestType);
            }
        }

        supperClazz = supperClazz.getSuperclass();
    }

    return this;
}

BaseMocker和ArrayMocker获取name修改如下

public static String getTypeVariableName(TypeVariable<?> typeVariable) {
    GenericDeclaration declaration = typeVariable.getGenericDeclaration();
    if (!(declaration instanceof Class<?>)) {
        throw new MockException("unknown error");
    }

    return String.format("%s.%s", ((Class<?>) declaration).getName(), typeVariable.getName());
}

public static boolean isSystemClass(String name) {
    return name.startsWith("java.") || name.startsWith("javax.");
}

Junit

@Test
public void testNestedGeneric() {
    OuterBean<ArrayBean<BasicBean>> outerBean = JMockData.mock(new TypeReference<OuterBean<ArrayBean<BasicBean>>>() {
    });
    System.out.println(JSON.toJSONString(outerBean, true));
    assertNotNull(outerBean);
}

@Test
public void testArrayGeneric() {
    ArrayBean<String> outerBean = JMockData.mock(new TypeReference<ArrayBean<String>>() {
    });
    System.out.println(JSON.toJSONString(outerBean, true));
    assertNotNull(outerBean);
}

a483210 avatar May 09 '20 07:05 a483210

直接提交PR比较好

huayanYu avatar Feb 09 '21 06:02 huayanYu