handle circle reference

This commit is contained in:
金戟 2021-03-23 00:00:48 +08:00
parent 3feba7fdfa
commit ea8f305152
4 changed files with 102 additions and 47 deletions

View File

@ -33,7 +33,7 @@ public class Child {
* 这是一个私有内部类
* An private inner class
*/
private static class InnerChild {
private class InnerChild {
private String secret;
}

View File

@ -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"));

View File

@ -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<String> generateMemberIndex(Class<?> clazz) {
return generateMemberIndex("", clazz);
return generateMemberIndex(clazz, "", new HashSet<Class<?>>(6));
}
private static List<String> generateMemberIndex(String basePath, Class<?> clazz) {
private static List<String> generateMemberIndex(Class<?> clazz, String basePath, Set<Class<?>> classPool) {
if (isAtomicType(clazz)) {
return Collections.emptyList();
}
classPool.add(clazz);
List<String> paths = new ArrayList<String>();
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;
}

View File

@ -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> T newInstance(Class<T> 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<Class<?>>(INITIAL_CAPACITY)));
}
/**
@ -54,27 +37,62 @@ public class OmniConstructor {
* @return 返回新创建的对象数组
*/
public static <T> T[] newArray(Class<T> 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<Class<?>>(INITIAL_CAPACITY)));
}
private static <T> T newObject(Class<T> clazz)
private static <T> T newInstance(Class<T> clazz, Set<Class<?>> 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> T newObject(Class<T> clazz, Set<Class<?>> 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> T[] newArray(Class<T> clazz, int size, Set<Class<?>> 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> T newAbstractClass(Class<T> clazz) {
return null;
}
@ -110,21 +128,59 @@ public class OmniConstructor {
return null;
}
private static Object createInstance(Constructor<?> constructor)
private static <T> 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<Class<?>, Object>(INITIAL_CAPACITY));
}
} else {
handleCircleReference(instance, new HashMap<Class<?>, Object>(INITIAL_CAPACITY));
}
} catch (IllegalAccessException e) {
throw new ClassConstructionException("Failed to access field", e);
}
return instance;
}
private static <T> void handleCircleReference(T instance, Map<Class<?>, 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<Class<?>> 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()) {