功能概述
- PojoUtils是一個(gè)工具類,能夠進(jìn)行深度遍歷,將簡單類型與復(fù)雜類型的對象進(jìn)行轉(zhuǎn)換,在泛化調(diào)用時(shí)用到(在泛化調(diào)用中,主要將Pojo對象與Map對象進(jìn)行相互轉(zhuǎn)換)
功能分析
核心類PojoUtils分析
主要成員變量分析
private static final ConcurrentMap<String, Method> NAME_METHODS_CACHE = new ConcurrentHashMap<String, Method>();
private static final ConcurrentMap<Class<?>, ConcurrentMap<String, Field>> CLASS_FIELD_CACHE = new ConcurrentHashMap<Class<?>, ConcurrentMap<String, Field>>();
主要成員方法分析
generalize將復(fù)雜對象轉(zhuǎn)換為簡單對象
private static Object generalize(Object pojo, Map<Object, Object> history) { if (pojo == null) {return null;}if (pojo instanceof Enum<?>) {return ((Enum<?>) pojo).name(); }if (pojo.getClass().isArray() && Enum.class.isAssignableFrom(pojo.getClass().getComponentType())) { int len = Array.getLength(pojo);String[] values = new String[len];for (int i = 0; i < len; i++) { values[i] = ((Enum<?>) Array.get(pojo, i)).name();}return values;}if (ReflectUtils.isPrimitives(pojo.getClass())) { return pojo;}if (pojo instanceof Class) { return ((Class) pojo).getName();}Object o = history.get(pojo);if (o != null) {return o;}history.put(pojo, pojo);if (pojo.getClass().isArray()) { int len = Array.getLength(pojo);Object[] dest = new Object[len];history.put(pojo, dest);for (int i = 0; i < len; i++) {Object obj = Array.get(pojo, i);dest[i] = generalize(obj, history);}return dest;}if (pojo instanceof Collection<?>) { Collection<Object> src = (Collection<Object>) pojo;int len = src.size();Collection<Object> dest = (pojo instanceof List<?>) ? new ArrayList<Object>(len) : new HashSet<Object>(len); history.put(pojo, dest);for (Object obj : src) { dest.add(generalize(obj, history));}return dest;}if (pojo instanceof Map<?, ?>) { Map<Object, Object> src = (Map<Object, Object>) pojo;Map<Object, Object> dest = createMap(src); history.put(pojo, dest);for (Map.Entry<Object, Object> obj : src.entrySet()) {dest.put(generalize(obj.getKey(), history), generalize(obj.getValue(), history)); }return dest;}Map<String, Object> map = new HashMap<String, Object>();history.put(pojo, map);if (GENERIC_WITH_CLZ) {map.put("class", pojo.getClass().getName()); }for (Method method : pojo.getClass().getMethods()) {if (ReflectUtils.isBeanPropertyReadMethod(method)) { try {map.put(ReflectUtils.getPropertyNameFromBeanReadMethod(method), generalize(method.invoke(pojo), history));} catch (Exception e) {throw new RuntimeException(e.getMessage(), e);}}}for (Field field : pojo.getClass().getFields()) {if (ReflectUtils.isPublicInstanceField(field)) { try {Object fieldValue = field.get(pojo);if (history.containsKey(pojo)) {Object pojoGeneralizedValue = history.get(pojo); if (pojoGeneralizedValue instanceof Map&& ((Map) pojoGeneralizedValue).containsKey(field.getName())) {continue;}}if (fieldValue != null) {map.put(field.getName(), generalize(fieldValue, history));}} catch (Exception e) {throw new RuntimeException(e.getMessage(), e);}}}return map;
}
- 代碼分析:generalize方法的作用是把復(fù)雜類型對象轉(zhuǎn)換為簡單類型的對象,如將Pojo對象轉(zhuǎn)換為Map對象,Pojo對象的成員對象若還是復(fù)雜類型,會遞歸調(diào)用進(jìn)行轉(zhuǎn)換。
realize將簡單對象轉(zhuǎn)換為復(fù)雜對象
private static Object realize0(Object pojo, Class<?> type, Type genericType, final Map<Object, Object> history) { if (pojo == null) {return null;}if (type != null && type.isEnum() && pojo.getClass() == String.class) { return Enum.valueOf((Class<Enum>) type, (String) pojo);}if (ReflectUtils.isPrimitives(pojo.getClass())&& !(type != null && type.isArray()&& type.getComponentType().isEnum()&& pojo.getClass() == String[].class)) { return CompatibleTypeUtils.compatibleTypeConvert(pojo, type);}Object o = history.get(pojo); if (o != null) {return o;}history.put(pojo, pojo);if (pojo.getClass().isArray()) { if (Collection.class.isAssignableFrom(type)) { Class<?> ctype = pojo.getClass().getComponentType(); int len = Array.getLength(pojo); Collection dest = createCollection(type, len);history.put(pojo, dest);for (int i = 0; i < len; i++) {Object obj = Array.get(pojo, i); Object value = realize0(obj, ctype, null, history); dest.add(value);}return dest;} else {Class<?> ctype = (type != null && type.isArray() ? type.getComponentType() : pojo.getClass().getComponentType());int len = Array.getLength(pojo);Object dest = Array.newInstance(ctype, len);history.put(pojo, dest);for (int i = 0; i < len; i++) {Object obj = Array.get(pojo, i);Object value = realize0(obj, ctype, null, history);Array.set(dest, i, value);}return dest;}}if (pojo instanceof Collection<?>) { if (type.isArray()) { Class<?> ctype = type.getComponentType();Collection<Object> src = (Collection<Object>) pojo;int len = src.size();Object dest = Array.newInstance(ctype, len); history.put(pojo, dest);int i = 0;for (Object obj : src) {Object value = realize0(obj, ctype, null, history); Array.set(dest, i, value);i++;}return dest;} else {Collection<Object> src = (Collection<Object>) pojo;int len = src.size();Collection<Object> dest = createCollection(type, len);history.put(pojo, dest);for (Object obj : src) { Type keyType = getGenericClassByIndex(genericType, 0);Class<?> keyClazz = obj == null ? null : obj.getClass();if (keyType instanceof Class) {keyClazz = (Class<?>) keyType;}Object value = realize0(obj, keyClazz, keyType, history);dest.add(value);}return dest;}}if (pojo instanceof Map<?, ?> && type != null) { Object className = ((Map<Object, Object>) pojo).get("class"); if (className instanceof String) {try {type = ClassUtils.forName((String) className); } catch (ClassNotFoundException e) {}}if (type.isEnum()) { Object name = ((Map<Object, Object>) pojo).get("name"); if (name != null) {return Enum.valueOf((Class<Enum>) type, name.toString()); }}Map<Object, Object> map;if (!type.isInterface() && !type.isAssignableFrom(pojo.getClass())) { try {map = (Map<Object, Object>) type.newInstance();Map<Object, Object> mapPojo = (Map<Object, Object>) pojo;map.putAll(mapPojo);if (GENERIC_WITH_CLZ) {map.remove("class");}} catch (Exception e) {map = (Map<Object, Object>) pojo; }} else {map = (Map<Object, Object>) pojo; }if (Map.class.isAssignableFrom(type) || type == Object.class) { final Map<Object, Object> result;Type mapKeyType = getKeyTypeForMap(map.getClass()); Type typeKeyType = getGenericClassByIndex(genericType, 0); boolean typeMismatch = mapKeyType instanceof Class && typeKeyType instanceof Class&& !typeKeyType.getTypeName().equals(mapKeyType.getTypeName()); if (typeMismatch) { result = createMap(new HashMap(0));} else {result = createMap(map); }history.put(pojo, result);for (Map.Entry<Object, Object> entry : map.entrySet()) {Type keyType = getGenericClassByIndex(genericType, 0); Type valueType = getGenericClassByIndex(genericType, 1);Class<?> keyClazz; if (keyType instanceof Class) { keyClazz = (Class<?>) keyType;} else if (keyType instanceof ParameterizedType) { keyClazz = (Class<?>) ((ParameterizedType) keyType).getRawType();} else { keyClazz = entry.getKey() == null ? null : entry.getKey().getClass();}Class<?> valueClazz; if (valueType instanceof Class) {valueClazz = (Class<?>) valueType;} else if (valueType instanceof ParameterizedType) {valueClazz = (Class<?>) ((ParameterizedType) valueType).getRawType();} else {valueClazz = entry.getValue() == null ? null : entry.getValue().getClass();}Object key = keyClazz == null ? entry.getKey() : realize0(entry.getKey(), keyClazz, keyType, history); Object value = valueClazz == null ? entry.getValue() : realize0(entry.getValue(), valueClazz, valueType, history); result.put(key, value);}return result;} else if (type.isInterface()) { Object dest = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[] {type}, new PojoInvocationHandler(map)); history.put(pojo, dest);return dest;} else {Object dest = newInstance(type); history.put(pojo, dest); for (Map.Entry<Object, Object> entry : map.entrySet()) { Object key = entry.getKey();if (key instanceof String) { String name = (String) key;Object value = entry.getValue();if (value != null) {Method method = getSetterMethod(dest.getClass(), name, value.getClass()); Field field = getField(dest.getClass(), name); if (method != null) {if (!method.isAccessible()) {method.setAccessible(true);}Type ptype = method.getGenericParameterTypes()[0]; value = realize0(value, method.getParameterTypes()[0], ptype, history); try {method.invoke(dest, value); } catch (Exception e) {String exceptionDescription = "Failed to set pojo " + dest.getClass().getSimpleName() + " property " + name+ " value " + value + "(" + value.getClass() + "), cause: " + e.getMessage();logger.error(exceptionDescription, e);throw new RuntimeException(exceptionDescription, e);}} else if (field != null) {value = realize0(value, field.getType(), field.getGenericType(), history);try {field.set(dest, value);} catch (IllegalAccessException e) {throw new RuntimeException("Failed to set field " + name + " of pojo " + dest.getClass().getName() + " : " + e.getMessage(), e);}}}}}if (dest instanceof Throwable) { Object message = map.get("message");if (message instanceof String) {try {Field field = Throwable.class.getDeclaredField("detailMessage");if (!field.isAccessible()) {field.setAccessible(true);}field.set(dest, message);} catch (Exception e) {}}}return dest;}}return pojo;}
關(guān)聯(lián)類PojoInvocationHandler分析
類中核心代碼分析
private static class PojoInvocationHandler implements InvocationHandler { private Map<Object, Object> map; public PojoInvocationHandler(Map<Object, Object> map) {this.map = map;}@Override@SuppressWarnings("unchecked")public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getDeclaringClass() == Object.class) {return method.invoke(map, args);}String methodName = method.getName();Object value = null;if (methodName.length() > 3 && methodName.startsWith("get")) {value = map.get(methodName.substring(3, 4).toLowerCase() + methodName.substring(4)); } else if (methodName.length() > 2 && methodName.startsWith("is")) {value = map.get(methodName.substring(2, 3).toLowerCase() + methodName.substring(3));} else {value = map.get(methodName.substring(0, 1).toLowerCase() + methodName.substring(1));}if (value instanceof Map<?, ?> && !Map.class.isAssignableFrom(method.getReturnType())) { value = realize0((Map<String, Object>) value, method.getReturnType(), null, new IdentityHashMap<Object, Object>());}return value;}
}
- 代碼分析:在轉(zhuǎn)換的目標(biāo)類型為接口時(shí)type.isInterface(),使用jdk動態(tài)代理創(chuàng)建接口的代理對象,PojoInvocationHandler為代理對象的調(diào)用處理器,包含了提取屬性的邏輯
問題點(diǎn)答疑
- 在realize、generalize方法中都包含了參數(shù)Map<Object, Object> history,用途是什么?
- 解答:在Pojo屬性為復(fù)雜類型時(shí),即需要遞歸轉(zhuǎn)換或解析時(shí),就可以把已經(jīng)處理過的結(jié)果放入Map中,傳遞給下一個(gè)轉(zhuǎn)換或解析。下次處理前,會判斷是否已經(jīng)處理過,處理過的就不再處理。這樣已經(jīng)處理過的類型就不用處理了,提升處理性能。
歸納總結(jié)
- PojoUtils轉(zhuǎn)換中,簡單類型包含:基本類型、Number、Date、元素為基本類型的數(shù)組、集合類型等。
- 在類型轉(zhuǎn)換時(shí),會進(jìn)行遞歸調(diào)用,一直解析到位簡單類型為止。