diff --git a/demo/java-demo/src/main/java/com/alibaba/demo/basic/model/omni/Child.java b/demo/java-demo/src/main/java/com/alibaba/demo/basic/model/omni/Child.java index 2a457d5..e8917fc 100644 --- a/demo/java-demo/src/main/java/com/alibaba/demo/basic/model/omni/Child.java +++ b/demo/java-demo/src/main/java/com/alibaba/demo/basic/model/omni/Child.java @@ -33,7 +33,7 @@ public class Child { * 这是一个私有内部类 * An private inner class */ - private static class InnerChild { + private class InnerChild { private String secret; } diff --git a/demo/java-demo/src/test/java/com/alibaba/demo/basic/DemoOmniMethodsTest.java b/demo/java-demo/src/test/java/com/alibaba/demo/basic/DemoOmniMethodsTest.java index 195d691..740d6de 100644 --- a/demo/java-demo/src/test/java/com/alibaba/demo/basic/DemoOmniMethodsTest.java +++ b/demo/java-demo/src/test/java/com/alibaba/demo/basic/DemoOmniMethodsTest.java @@ -82,7 +82,7 @@ class DemoOmniMethodsTest { assertEquals("demo in batch", parent.getChildren()[1].getGrandChild().getContent()); assertEquals("demo in batch", parent.getChildren()[2].getGrandChild().getContent()); - // 读写私有内部类类型的成员(注意使用类型名引用类别类时,无需带外部类名) + // 读写私有内部类类型的成员(使用类型名引用内部类时,无需带外部类名) assertEquals("", OmniAccessor.getFirst(parent, "subChild/secret")); OmniAccessor.set(parent, "{InnerChild}/secret", "inner-class secret"); assertEquals("inner-class secret", OmniAccessor.getFirst(parent, "subChild/secret")); diff --git a/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java index 4ba2bba..d84a57e 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java @@ -6,10 +6,7 @@ import com.alibaba.testable.core.util.TypeUtil; import java.lang.reflect.Array; import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; import static com.alibaba.testable.core.constant.ConstPool.SLASH; @@ -104,21 +101,23 @@ public class OmniAccessor { } private static List generateMemberIndex(Class clazz) { - return generateMemberIndex("", clazz); + return generateMemberIndex(clazz, "", new HashSet>(6)); } - private static List generateMemberIndex(String basePath, Class clazz) { + private static List generateMemberIndex(Class clazz, String basePath, Set> classPool) { if (isAtomicType(clazz)) { return Collections.emptyList(); } + classPool.add(clazz); List paths = new ArrayList(); for (Field f : TypeUtil.getAllFields(clazz)) { - if (!f.getName().startsWith(THIS_REF_PREFIX)) { + if (!classPool.contains(f.getType()) && !f.getName().startsWith(THIS_REF_PREFIX)) { String fullPath = basePath + SLASH + toPath(f); paths.add(fullPath); - paths.addAll(generateMemberIndex(fullPath, f.getType())); + paths.addAll(generateMemberIndex(f.getType(), fullPath, classPool)); } } + classPool.remove(clazz); return paths; } diff --git a/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java index 45d954c..7b6b3e5 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniConstructor.java @@ -5,12 +5,18 @@ import com.alibaba.testable.core.model.Null; import com.alibaba.testable.core.util.TypeUtil; import java.lang.reflect.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; /** * @author flin */ public class OmniConstructor { + private static final int INITIAL_CAPACITY = 6; + private OmniConstructor() {} /** @@ -20,30 +26,7 @@ public class OmniConstructor { * @return 返回新创建的对象 */ public static T newInstance(Class clazz) { - try { - if (clazz.isPrimitive()) { - return newPrimitive(clazz); - } else if (clazz.isArray()) { - return (T)newArray(clazz.getComponentType(), 0); - } else if (clazz.isEnum()) { - return newEnum(clazz); - } else if (clazz.isInterface()) { - return newInterface(clazz); - } else if (Modifier.isAbstract(clazz.getModifiers())) { - return newAbstractClass(clazz); - } - return newObject(clazz); - } catch (NoSuchMethodException e) { - throw new ClassConstructionException("Failed to find constructor", e); - } catch (IllegalAccessException e) { - throw new ClassConstructionException("Failed to access constructor", e); - } catch (InvocationTargetException e) { - throw new ClassConstructionException("Failed to invoke constructor", e); - } catch (InstantiationException e) { - throw new ClassConstructionException("Failed to complete construction", e); - } catch (ClassCastException e) { - throw new ClassConstructionException("Unexpected type", e); - } + return handleCircleReference(newInstance(clazz, new HashSet>(INITIAL_CAPACITY))); } /** @@ -54,27 +37,62 @@ public class OmniConstructor { * @return 返回新创建的对象数组 */ public static T[] newArray(Class clazz, int size) { - T[] array = (T[])Array.newInstance(clazz, size); - for (int i = 0; i < size; i++) { - Array.set(array, i, newInstance(clazz)); - } - return array; + return handleCircleReference(newArray(clazz, size, new HashSet>(INITIAL_CAPACITY))); } - private static T newObject(Class clazz) + private static T newInstance(Class clazz, Set> classPool) { + if (classPool.contains(clazz)) { + return null; + } + classPool.add(clazz); + try { + if (clazz.isPrimitive()) { + return newPrimitive(clazz); + } else if (clazz.isArray()) { + return (T)newArray(clazz.getComponentType(), 0, classPool); + } else if (clazz.isEnum()) { + return newEnum(clazz); + } else if (clazz.isInterface()) { + return newInterface(clazz); + } else if (Modifier.isAbstract(clazz.getModifiers())) { + return newAbstractClass(clazz); + } + return newObject(clazz, classPool); + } catch (NoSuchMethodException e) { + throw new ClassConstructionException("Failed to find constructor", e); + } catch (IllegalAccessException e) { + throw new ClassConstructionException("Failed to access constructor", e); + } catch (InvocationTargetException e) { + throw new ClassConstructionException("Failed to invoke constructor", e); + } catch (InstantiationException e) { + throw new ClassConstructionException("Failed to complete construction", e); + } catch (ClassCastException e) { + throw new ClassConstructionException("Unexpected type", e); + } finally { + classPool.remove(clazz); + } + } + + private static T newObject(Class clazz, Set> classPool) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { - Constructor constructor = getBestConstructor(clazz); - constructor.setAccessible(true); - Object ins = createInstance(constructor); + Object ins = createInstance(getBestConstructor(clazz), classPool); for (Field f : TypeUtil.getAllFields(clazz)) { f.setAccessible(true); if (f.get(ins) == null) { - f.set(ins, newInstance(f.getType())); + f.set(ins, newInstance(f.getType(), classPool)); } } return (T)ins; } + private static T[] newArray(Class clazz, int size, Set> classPool) { + T[] array = (T[])Array.newInstance(clazz, size); + for (int i = 0; i < size; i++) { + Array.set(array, i, newInstance(clazz, classPool)); + } + return array; + } + private static T newAbstractClass(Class clazz) { return null; } @@ -110,21 +128,59 @@ public class OmniConstructor { return null; } - private static Object createInstance(Constructor constructor) + private static T handleCircleReference(T instance) { + try { + if (instance.getClass().isArray()) { + for (int i = 0; i < Array.getLength(instance); i++) { + handleCircleReference(Array.get(instance, i), new HashMap, Object>(INITIAL_CAPACITY)); + } + } else { + handleCircleReference(instance, new HashMap, Object>(INITIAL_CAPACITY)); + } + } catch (IllegalAccessException e) { + throw new ClassConstructionException("Failed to access field", e); + } + return instance; + } + + private static void handleCircleReference(T instance, Map, Object> classPool) + throws IllegalAccessException { + classPool.put(instance.getClass(), instance); + for (Field f : TypeUtil.getAllFields(instance.getClass())) { + f.setAccessible(true); + Object fieldIns = f.get(instance); + Class fieldType = f.getType(); + if (fieldType.isArray()) { + for (int i = 0; i < Array.getLength(fieldIns); i++) { + handleCircleReference(Array.get(fieldIns, i), classPool); + } + } else if (!fieldType.isPrimitive() && !fieldType.isEnum()) { + if (fieldIns == null && classPool.containsKey(fieldType)) { + f.set(instance, classPool.get(fieldType)); + } else if (fieldIns != null && !classPool.containsKey(fieldType)) { + handleCircleReference(fieldIns, classPool); + } + } + } + classPool.remove(instance.getClass()); + } + + private static Object createInstance(Constructor constructor, Set> classPool) throws InstantiationException, IllegalAccessException, InvocationTargetException { + constructor.setAccessible(true); Class[] types = constructor.getParameterTypes(); if (types.length == 1 && types[0].equals(Null.class)) { return constructor.newInstance(new Null()); } else { Object[] args = new Object[types.length]; for (int i = 0; i < types.length; i++) { - args[i] = newInstance(types[i]); + args[i] = newInstance(types[i], classPool); } return constructor.newInstance(args); } } - private static Constructor getBestConstructor(Class clazz) throws NoSuchMethodException { + private static Constructor getBestConstructor(Class clazz) { Constructor bestConstructor = null; int minimalParametersSize = 999; for (Constructor constructor : clazz.getDeclaredConstructors()) {