Skip to content

Commit

Permalink
Merge branch 'kanyuxia'
Browse files Browse the repository at this point in the history
  • Loading branch information
默默的看雨下 committed Jan 20, 2018
2 parents dc67640 + 5aa787b commit 1b8bb68
Show file tree
Hide file tree
Showing 14 changed files with 315 additions and 151 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,11 @@ public class GenericData<A, B, C> {
private A a;
private B b;
private C c;
private A[] aArray;
private List<B> bList;
private Map<A, B> map;
private List<C>[] cArray;
private Map<Map<A, B>, List<C>>[] d;
//getter setter省略...
}

Expand Down
4 changes: 4 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
<name>TaoYu</name>
<email>332309254@qq.com</email>
</contributor>
<contributor>
<name>KanYuXia</name>
<email>kanyuxia@outlook.com</email>
</contributor>
</contributors>

<scm>
Expand Down
7 changes: 3 additions & 4 deletions src/main/java/com/github/jsonzou/jmockdata/JMockData.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.github.jsonzou.jmockdata;

import com.github.jsonzou.jmockdata.mocker.ClassMocker;
import com.github.jsonzou.jmockdata.mocker.GenericMocker;
import com.github.jsonzou.jmockdata.mocker.BaseMocker;

/**
* 模拟对象门面类
Expand Down Expand Up @@ -30,7 +29,7 @@ public static <T> T mock(Class<T> clazz) {
* @return 模拟数据对象
*/
public static <T> T mock(Class<T> clazz, MockConfig mockConfig) {
return new ClassMocker<>(clazz).mock(mockConfig);
return new BaseMocker<T>(clazz).mock(mockConfig);
}

/**
Expand All @@ -57,7 +56,7 @@ public static <T> T mock(TypeReference<T> typeReference) {
* @return 模拟数据对象
*/
public static <T> T mock(TypeReference<T> typeReference, MockConfig mockConfig) {
return (T) new GenericMocker(typeReference.getType()).mock(mockConfig);
return new BaseMocker<T>(typeReference.getType()).mock(mockConfig.init(typeReference.getType()));
}

}
36 changes: 32 additions & 4 deletions src/main/java/com/github/jsonzou/jmockdata/MockConfig.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.github.jsonzou.jmockdata;


import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* 模拟数据配置类
Expand All @@ -12,7 +15,11 @@ public class MockConfig {
/**
* Bean缓存
*/
private Map<String, Object> beanCache = new ConcurrentHashMap<>(16);
private Map<String, Object> beanCache = new HashMap<>();
/**
* TypeVariable缓存
*/
private Map<String, Type> typeVariableCache = new HashMap<>();
private byte[] byteRange = {0, 127};
private short[] shortRange = {0, 1000};
private int[] intRange = {0, 10000};
Expand All @@ -30,14 +37,35 @@ public class MockConfig {
"l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F",
"G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};

public void addCache(String name, Object object) {
public MockConfig() {
}

public void addBeanCache(String name, Object object) {
beanCache.put(name, object);
}

public Object getCacheObject(String name) {
public Object getBeanCacheObject(String name) {
return beanCache.get(name);
}

public MockConfig init(Type type) {
if (type instanceof ParameterizedType) {
Class clazz = (Class) ((ParameterizedType) type).getRawType();
Type[] types = ((ParameterizedType) type).getActualTypeArguments();
TypeVariable[] typeVariables = clazz.getTypeParameters();
if (typeVariables != null && typeVariables.length > 0) {
for (int index = 0; index < typeVariables.length; index++) {
typeVariableCache.put(typeVariables[index].getName(), types[index]);
}
}
}
return this;
}

public Type getVariableType(String name) {
return typeVariableCache.get(name);
}

public MockConfig byteRange(byte min, byte max) {
this.byteRange[0] = min;
this.byteRange[1] = max;
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/github/jsonzou/jmockdata/Mocker.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
*/
public interface Mocker<T> {

/**
* 模拟数据
*
* @param mockConfig 模拟数据配置
* @return 模拟数据对象
*/
T mock(MockConfig mockConfig);

}
88 changes: 80 additions & 8 deletions src/main/java/com/github/jsonzou/jmockdata/mocker/ArrayMocker.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,103 @@
import com.github.jsonzou.jmockdata.Mocker;
import com.github.jsonzou.jmockdata.util.RandomUtils;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
* 数组模拟器
*/
@SuppressWarnings("unchecked")
// TODO 2018/1/20 代码还需要整理
public class ArrayMocker implements Mocker<Object> {

private Class<?> clazz;
private Type type;

private Type componentType;

public ArrayMocker(Class<?> clazz, Type componentType) {
this.clazz = clazz;
this.componentType = componentType;
ArrayMocker(Type type) {
this.type = type;
}

@Override
public Object mock(MockConfig mockConfig) {
// 创建有参数化的数组
if (type instanceof GenericArrayType) {
return createGenericArray(mockConfig);
}
return array(mockConfig);
}

private Object array(MockConfig mockConfig) {
int size = RandomUtils.nextSize(mockConfig.getSizeRange()[0], mockConfig.getSizeRange()[1]);
Object result = Array.newInstance(clazz, size);
Class componentClass = ((Class) type).getComponentType();
Object result = Array.newInstance(componentClass, size);
for (int index = 0; index < size; index++) {
Array.set(result, index, new GenericMocker(componentType).mock(mockConfig));
Object value = new BaseMocker(componentClass).mock(mockConfig);
Array.set(result, index, value);
}
return result;
}

// 由于GenericArrayType无法获得Class,所以递归创建多维数组
private Object createGenericArray(MockConfig mockConfig) {
GenericArrayType genericArrayType = (GenericArrayType) this.type;
// 递归获取该数组的维数,以及最后的Class类型
Map<Integer, Map<Class, Type[]>> map = map(mockConfig, genericArrayType, 0);
Entry<Integer, Map<Class, Type[]>> entry = map.entrySet().iterator().next();
Entry<Class, Type[]> baseEntry = entry.getValue().entrySet().iterator().next();
int[] dimensions = new int[entry.getKey()];
for (int index = 0; index < dimensions.length; index++) {
dimensions[index] = RandomUtils.nextSize(mockConfig.getSizeRange()[0], mockConfig.getSizeRange()[1]);
}
// 创建多维数组每种维度的对象
List<Object> list = new ArrayList<>(dimensions.length);
Class clazz = baseEntry.getKey();
for (int index = dimensions.length - 1; index >= 0; index--) {
Object array = Array.newInstance(clazz, dimensions[index]);
list.add(array);
clazz = array.getClass();
}
// 实例化多维数组
Object baseResult = new BaseMocker(baseEntry.getKey(), baseEntry.getValue()).mock(mockConfig);
for (int i = 0; i < list.size(); i++) {
Object array = list.get(i);
for (int j = 0; j < dimensions[dimensions.length - i - 1]; j++) {
Array.set(array, j, baseResult);
}
baseResult = array;
}
return baseResult;
}

private Map<Integer, Map<Class, Type[]>> map(MockConfig mockConfig, GenericArrayType genericArrayType, int dimension) {
Map<Integer, Map<Class, Type[]>> result = new HashMap<>();
Type componentType = genericArrayType.getGenericComponentType();
dimension++;
if (componentType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) componentType;
Map<Class, Type[]> map = new HashMap<>();
map.put((Class) parameterizedType.getRawType(), parameterizedType.getActualTypeArguments());
result.put(dimension, map);
return result;
}
if (componentType instanceof GenericArrayType) {
return map(mockConfig, (GenericArrayType) componentType, dimension);
}
if (componentType instanceof TypeVariable) {
Map<Class, Type[]> map = new HashMap<>();
map.put((Class) mockConfig.getVariableType(((TypeVariable) componentType).getName()), null);
result.put(dimension, map);
return result;
}
Map<Class, Type[]> map = new HashMap<>();
map.put((Class) componentType, null);
result.put(dimension, map);
return result;
}
}
38 changes: 38 additions & 0 deletions src/main/java/com/github/jsonzou/jmockdata/mocker/BaseMocker.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.github.jsonzou.jmockdata.mocker;

import com.github.jsonzou.jmockdata.MockConfig;
import com.github.jsonzou.jmockdata.Mocker;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

@SuppressWarnings("unchecked")
public class BaseMocker<T> implements Mocker<T> {

private Type type;

private Type[] genericTypes;

public BaseMocker(Type type, Type... genericTypes) {
this.type = type;
this.genericTypes = genericTypes;
}

@Override
public T mock(MockConfig mockConfig) {
Mocker mocker;
if (type instanceof ParameterizedType) {
mocker = new GenericMocker((ParameterizedType) type);
} else if (type instanceof GenericArrayType) {
mocker = new ArrayMocker(type);
} else if (type instanceof TypeVariable) {
Type actualType = mockConfig.getVariableType(((TypeVariable) type).getName());
mocker = new BaseMocker(actualType);
} else {
mocker = new ClassMocker((Class) type, genericTypes);
}
return (T) mocker.mock(mockConfig);
}

}
81 changes: 14 additions & 67 deletions src/main/java/com/github/jsonzou/jmockdata/mocker/BeanMocker.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,89 +5,36 @@
import com.github.jsonzou.jmockdata.Mocker;
import com.github.jsonzou.jmockdata.util.ReflectionUtils;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

/**
* Bean模拟器
*/
@SuppressWarnings("unchecked")
public class BeanMocker<T> implements Mocker<T> {
public class BeanMocker implements Mocker<Object> {

private Class<?> clazz;
private final Class clazz;

private Type[] genericTypes;

private Map<String, Type> typeVariableMap;

public BeanMocker(Class<?> clazz, Type... genericTypes) {
BeanMocker(Class clazz) {
this.clazz = clazz;
this.genericTypes = genericTypes;
TypeVariable<?>[] typeVariables = clazz.getTypeParameters();
if (typeVariables != null && typeVariables.length > 0) {
typeVariableMap = new HashMap<>();
for (int i = 0; i < typeVariables.length; i++) {
typeVariableMap.put(typeVariables[i].getName(), genericTypes[i]);
}
}
}

@Override
public T mock(MockConfig mockConfig) {
if (clazz.isArray()) {
Type componentType = genericTypes.length == 0 ? clazz.getComponentType() : genericTypes[0];
return (T) new ArrayMocker(clazz.getComponentType(), componentType).mock(mockConfig);
} else if (Map.class.isAssignableFrom(clazz)) {
return (T) new MapMocker(genericTypes).mock(mockConfig);
} else if (Collection.class.isAssignableFrom(clazz)) {
return (T) new CollectionMocker(clazz, genericTypes[0]).mock(mockConfig);
}
// 从缓存中取已经构造的Bean
Object cacheBean = mockConfig.getCacheObject(clazz.getName());
return cacheBean != null ? (T) cacheBean : mockBean(mockConfig);
}

private T mockBean(MockConfig mockConfig) {
public Object mock(MockConfig mockConfig) {
try {
T result = (T) clazz.newInstance();
mockConfig.addCache(clazz.getName(), result);
// 从缓存中取已经构造的Bean,解决循环依赖问题
// TODO 2018/1/20 这里解决了循环依赖问题,但是又会出现新的问题, 如List<A>,其中List中的A对象全是相同的。
Object cacheBean = mockConfig.getBeanCacheObject(clazz.getName());
if (cacheBean != null) {
return cacheBean;
}
Object result = clazz.newInstance();
mockConfig.addBeanCache(clazz.getName(), result);
for (Class<?> currentClass = clazz; currentClass != Object.class; currentClass = currentClass.getSuperclass()) {
// 模拟有setter方法的字段
for (Entry<Field, Method> entry : ReflectionUtils.fieldAndSetterMethod(currentClass).entrySet()) {
Field field = entry.getKey();
Type genericType = field.getGenericType();
Method method = entry.getValue();
Class<?> fieldClass = field.getType();
Object value = null;
if (genericType instanceof TypeVariable) {
value = new GenericMocker(typeVariableMap.get(((TypeVariable) genericType).getName())).mock(mockConfig);
} else {
// 判断字段是否是Map or Collection
if (Map.class.isAssignableFrom(fieldClass) || Collection.class.isAssignableFrom(fieldClass)) {
value = new BeanMocker(fieldClass, ((ParameterizedType) field.getGenericType()).getActualTypeArguments()).mock(mockConfig);
// 判断字段是否是数组
} else if (fieldClass.isArray()) {
Type componentType;
Type type = field.getGenericType();
// 字段是数组类型(一维数组)
if (type instanceof GenericArrayType) {
componentType = ((GenericArrayType) type).getGenericComponentType();
} else {
// 字段是多维数组
componentType = ((Class) type).getComponentType();
}
value = new BeanMocker(fieldClass, componentType).mock(mockConfig);
} else {
value = new ClassMocker(fieldClass).mock(mockConfig);
}
}
Type genericType = field.getGenericType();
Object value = new BaseMocker(genericType).mock(mockConfig);
ReflectionUtils.setRefValue(result, method, value);
}
}
Expand Down
Loading

0 comments on commit 1b8bb68

Please sign in to comment.