一、前言
关于alibaba的dubbo源码包com.alibaba.dubbo.common.utils.PojoUtils简单对象工具类,对简单对象集合、复杂数据类型与简单数据类型转换处理、,详情参见源码说明。
二、源码说明
package com.alibaba.dubbo.common.utils;@b@@b@import java.lang.reflect.Array;@b@import java.lang.reflect.Constructor;@b@import java.lang.reflect.Field;@b@import java.lang.reflect.InvocationHandler;@b@import java.lang.reflect.InvocationTargetException;@b@import java.lang.reflect.Method;@b@import java.lang.reflect.Modifier;@b@import java.lang.reflect.ParameterizedType;@b@import java.lang.reflect.Proxy;@b@import java.lang.reflect.Type;@b@import java.util.ArrayList;@b@import java.util.Collection;@b@import java.util.Collections;@b@import java.util.HashMap;@b@import java.util.HashSet;@b@import java.util.Hashtable;@b@import java.util.IdentityHashMap;@b@import java.util.LinkedHashMap;@b@import java.util.List;@b@import java.util.Map;@b@import java.util.Properties;@b@import java.util.TreeMap;@b@import java.util.WeakHashMap;@b@import java.util.concurrent.ConcurrentHashMap;@b@import java.util.concurrent.ConcurrentMap;@b@import java.util.concurrent.ConcurrentSkipListMap;@b@@b@ @b@public class PojoUtils {@b@@b@ private static final ConcurrentMap<String, Method> NAME_METHODS_CACHE = new ConcurrentHashMap<String, Method>();@b@ private static final ConcurrentMap<Class<?>, ConcurrentMap<String, Field>> CLASS_FIELD_CACHE = new ConcurrentHashMap<Class<?>, ConcurrentMap<String, Field>>();@b@@b@ public static Object[] generalize(Object[] objs) {@b@ Object[] dests = new Object[objs.length];@b@ for (int i = 0; i < objs.length; i++) {@b@ dests[i] = generalize(objs[i]);@b@ }@b@ return dests;@b@ }@b@@b@ public static Object[] realize(Object[] objs, Class<?>[] types) {@b@ if (objs.length != types.length)@b@ throw new IllegalArgumentException("args.length != types.length");@b@ Object[] dests = new Object[objs.length];@b@ for (int i = 0; i < objs.length; i++) {@b@ dests[i] = realize(objs[i], types[i]);@b@ }@b@ return dests;@b@ }@b@@b@ public static Object[] realize(Object[] objs, Class<?>[] types, Type[] gtypes) {@b@ if (objs.length != types.length || objs.length != gtypes.length)@b@ throw new IllegalArgumentException("args.length != types.length");@b@ Object[] dests = new Object[objs.length];@b@ for (int i = 0; i < objs.length; i++) {@b@ dests[i] = realize(objs[i], types[i], gtypes[i]);@b@ }@b@ return dests;@b@ }@b@@b@ public static Object generalize(Object pojo) {@b@ return generalize(pojo, new IdentityHashMap<Object, Object>());@b@ }@b@@b@ @SuppressWarnings("unchecked")@b@ private static Object generalize(Object pojo, Map<Object, Object> history) {@b@ if (pojo == null) {@b@ return null;@b@ }@b@@b@ if (pojo instanceof Enum<?>) {@b@ return ((Enum<?>) pojo).name();@b@ }@b@ if (pojo.getClass().isArray() && Enum.class.isAssignableFrom(pojo.getClass().getComponentType())) {@b@ int len = Array.getLength(pojo);@b@ String[] values = new String[len];@b@ for (int i = 0; i < len; i++) {@b@ values[i] = ((Enum<?>) Array.get(pojo, i)).name();@b@ }@b@ return values;@b@ }@b@@b@ if (ReflectUtils.isPrimitives(pojo.getClass())) {@b@ return pojo;@b@ }@b@@b@ if (pojo instanceof Class) {@b@ return ((Class) pojo).getName();@b@ }@b@@b@ Object o = history.get(pojo);@b@ if (o != null) {@b@ return o;@b@ }@b@ history.put(pojo, pojo);@b@@b@ if (pojo.getClass().isArray()) {@b@ int len = Array.getLength(pojo);@b@ Object[] dest = new Object[len];@b@ history.put(pojo, dest);@b@ for (int i = 0; i < len; i++) {@b@ Object obj = Array.get(pojo, i);@b@ dest[i] = generalize(obj, history);@b@ }@b@ return dest;@b@ }@b@ if (pojo instanceof Collection<?>) {@b@ Collection<Object> src = (Collection<Object>) pojo;@b@ int len = src.size();@b@ Collection<Object> dest = (pojo instanceof List<?>) ? new ArrayList<Object>(len) : new HashSet<Object>(len);@b@ history.put(pojo, dest);@b@ for (Object obj : src) {@b@ dest.add(generalize(obj, history));@b@ }@b@ return dest;@b@ }@b@ if (pojo instanceof Map<?, ?>) {@b@ Map<Object, Object> src = (Map<Object, Object>) pojo;@b@ Map<Object, Object> dest = createMap(src);@b@ history.put(pojo, dest);@b@ for (Map.Entry<Object, Object> obj : src.entrySet()) {@b@ dest.put(generalize(obj.getKey(), history), generalize(obj.getValue(), history));@b@ }@b@ return dest;@b@ }@b@ Map<String, Object> map = new HashMap<String, Object>();@b@ history.put(pojo, map);@b@ map.put("class", pojo.getClass().getName());@b@ for (Method method : pojo.getClass().getMethods()) {@b@ if (ReflectUtils.isBeanPropertyReadMethod(method)) {@b@ try {@b@ map.put(ReflectUtils.getPropertyNameFromBeanReadMethod(method), generalize(method.invoke(pojo), history));@b@ } catch (Exception e) {@b@ throw new RuntimeException(e.getMessage(), e);@b@ }@b@ }@b@ }@b@ // public field@b@ for (Field field : pojo.getClass().getFields()) {@b@ if (ReflectUtils.isPublicInstanceField(field)) {@b@ try {@b@ Object fieldValue = field.get(pojo);@b@ if (history.containsKey(pojo)) {@b@ Object pojoGenerilizedValue = history.get(pojo);@b@ if (pojoGenerilizedValue instanceof Map@b@ && ((Map) pojoGenerilizedValue).containsKey(field.getName())) {@b@ continue;@b@ }@b@ }@b@ if (fieldValue != null) {@b@ map.put(field.getName(), generalize(fieldValue, history));@b@ }@b@ } catch (Exception e) {@b@ throw new RuntimeException(e.getMessage(), e);@b@ }@b@ }@b@ }@b@ return map;@b@ }@b@@b@ public static Object realize(Object pojo, Class<?> type) {@b@ return realize0(pojo, type, null, new IdentityHashMap<Object, Object>());@b@ }@b@@b@ public static Object realize(Object pojo, Class<?> type, Type genericType) {@b@ return realize0(pojo, type, genericType, new IdentityHashMap<Object, Object>());@b@ }@b@@b@ private static class PojoInvocationHandler implements InvocationHandler {@b@@b@ private Map<Object, Object> map;@b@@b@ public PojoInvocationHandler(Map<Object, Object> map) {@b@ this.map = map;@b@ }@b@@b@ @SuppressWarnings("unchecked")@b@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {@b@ if (method.getDeclaringClass() == Object.class) {@b@ return method.invoke(map, args);@b@ }@b@ String methodName = method.getName();@b@ Object value = null;@b@ if (methodName.length() > 3 && methodName.startsWith("get")) {@b@ value = map.get(methodName.substring(3, 4).toLowerCase() + methodName.substring(4));@b@ } else if (methodName.length() > 2 && methodName.startsWith("is")) {@b@ value = map.get(methodName.substring(2, 3).toLowerCase() + methodName.substring(3));@b@ } else {@b@ value = map.get(methodName.substring(0, 1).toLowerCase() + methodName.substring(1));@b@ }@b@ if (value instanceof Map<?, ?> && !Map.class.isAssignableFrom(method.getReturnType())) {@b@ value = realize0((Map<String, Object>) value, method.getReturnType(), null, new IdentityHashMap<Object, Object>());@b@ }@b@ return value;@b@ }@b@ }@b@@b@ @SuppressWarnings("unchecked")@b@ private static Collection<Object> createCollection(Class<?> type, int len) {@b@ if (type.isAssignableFrom(ArrayList.class)) {@b@ return new ArrayList<Object>(len);@b@ }@b@ if (type.isAssignableFrom(HashSet.class)) {@b@ return new HashSet<Object>(len);@b@ }@b@ if (!type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {@b@ try {@b@ return (Collection<Object>) type.newInstance();@b@ } catch (Exception e) {@b@ // ignore@b@ }@b@ }@b@ return new ArrayList<Object>();@b@ }@b@@b@ private static Map createMap(Map src) {@b@ Class<? extends Map> cl = src.getClass();@b@ Map result = null;@b@ if (HashMap.class == cl) {@b@ result = new HashMap();@b@ } else if (Hashtable.class == cl) {@b@ result = new Hashtable();@b@ } else if (IdentityHashMap.class == cl) {@b@ result = new IdentityHashMap();@b@ } else if (LinkedHashMap.class == cl) {@b@ result = new LinkedHashMap();@b@ } else if (Properties.class == cl) {@b@ result = new Properties();@b@ } else if (TreeMap.class == cl) {@b@ result = new TreeMap();@b@ } else if (WeakHashMap.class == cl) {@b@ return new WeakHashMap();@b@ } else if (ConcurrentHashMap.class == cl) {@b@ result = new ConcurrentHashMap();@b@ } else if (ConcurrentSkipListMap.class == cl) {@b@ result = new ConcurrentSkipListMap();@b@ } else {@b@ try {@b@ result = cl.newInstance();@b@ } catch (Exception e) { /* ignore */ }@b@@b@ if (result == null) {@b@ try {@b@ Constructor<?> constructor = cl.getConstructor(Map.class);@b@ result = (Map) constructor.newInstance(Collections.EMPTY_MAP);@b@ } catch (Exception e) { /* ignore */ }@b@ }@b@ }@b@@b@ if (result == null) {@b@ result = new HashMap<Object, Object>();@b@ }@b@@b@ return result;@b@ }@b@@b@ @SuppressWarnings({"unchecked", "rawtypes"})@b@ private static Object realize0(Object pojo, Class<?> type, Type genericType, final Map<Object, Object> history) {@b@ if (pojo == null) {@b@ return null;@b@ }@b@@b@ if (type != null && type.isEnum() && pojo.getClass() == String.class) {@b@ return Enum.valueOf((Class<Enum>) type, (String) pojo);@b@ }@b@@b@ if (ReflectUtils.isPrimitives(pojo.getClass())@b@ && !(type != null && type.isArray()@b@ && type.getComponentType().isEnum()@b@ && pojo.getClass() == String[].class)) {@b@ return CompatibleTypeUtils.compatibleTypeConvert(pojo, type);@b@ }@b@@b@ Object o = history.get(pojo);@b@@b@ if (o != null) {@b@ return o;@b@ }@b@@b@ history.put(pojo, pojo);@b@@b@ if (pojo.getClass().isArray()) {@b@ if (Collection.class.isAssignableFrom(type)) {@b@ Class<?> ctype = pojo.getClass().getComponentType();@b@ int len = Array.getLength(pojo);@b@ Collection dest = createCollection(type, len);@b@ history.put(pojo, dest);@b@ for (int i = 0; i < len; i++) {@b@ Object obj = Array.get(pojo, i);@b@ Object value = realize0(obj, ctype, null, history);@b@ dest.add(value);@b@ }@b@ return dest;@b@ } else {@b@ Class<?> ctype = (type != null && type.isArray() ? type.getComponentType() : pojo.getClass().getComponentType());@b@ int len = Array.getLength(pojo);@b@ Object dest = Array.newInstance(ctype, len);@b@ history.put(pojo, dest);@b@ for (int i = 0; i < len; i++) {@b@ Object obj = Array.get(pojo, i);@b@ Object value = realize0(obj, ctype, null, history);@b@ Array.set(dest, i, value);@b@ }@b@ return dest;@b@ }@b@ }@b@@b@ if (pojo instanceof Collection<?>) {@b@ if (type.isArray()) {@b@ Class<?> ctype = type.getComponentType();@b@ Collection<Object> src = (Collection<Object>) pojo;@b@ int len = src.size();@b@ Object dest = Array.newInstance(ctype, len);@b@ history.put(pojo, dest);@b@ int i = 0;@b@ for (Object obj : src) {@b@ Object value = realize0(obj, ctype, null, history);@b@ Array.set(dest, i, value);@b@ i++;@b@ }@b@ return dest;@b@ } else {@b@ Collection<Object> src = (Collection<Object>) pojo;@b@ int len = src.size();@b@ Collection<Object> dest = createCollection(type, len);@b@ history.put(pojo, dest);@b@ for (Object obj : src) {@b@ Type keyType = getGenericClassByIndex(genericType, 0);@b@ Class<?> keyClazz = obj.getClass();@b@ if (keyType instanceof Class) {@b@ keyClazz = (Class<?>) keyType;@b@ }@b@ Object value = realize0(obj, keyClazz, keyType, history);@b@ dest.add(value);@b@ }@b@ return dest;@b@ }@b@ }@b@@b@ if (pojo instanceof Map<?, ?> && type != null) {@b@ Object className = ((Map<Object, Object>) pojo).get("class");@b@ if (className instanceof String) {@b@ try {@b@ type = ClassHelper.forName((String) className);@b@ } catch (ClassNotFoundException e) {@b@ // ignore@b@ }@b@ }@b@@b@ // special logic for enum@b@ if (type.isEnum()) {@b@ Object name = ((Map<Object, Object>) pojo).get("name");@b@ if (name != null) {@b@ return Enum.valueOf((Class<Enum>) type, name.toString());@b@ }@b@ }@b@ Map<Object, Object> map;@b@ // when return type is not the subclass of return type from the signature and not an interface@b@ if (!type.isInterface() && !type.isAssignableFrom(pojo.getClass())) {@b@ try {@b@ map = (Map<Object, Object>) type.newInstance();@b@ Map<Object, Object> mapPojo = (Map<Object, Object>) pojo;@b@ map.putAll(mapPojo);@b@ map.remove("class");@b@ } catch (Exception e) {@b@ //ignore error@b@ map = (Map<Object, Object>) pojo;@b@ }@b@ } else {@b@ map = (Map<Object, Object>) pojo;@b@ }@b@@b@ if (Map.class.isAssignableFrom(type) || type == Object.class) {@b@ final Map<Object, Object> result = createMap(map);@b@ history.put(pojo, result);@b@ for (Map.Entry<Object, Object> entry : map.entrySet()) {@b@ Type keyType = getGenericClassByIndex(genericType, 0);@b@ Type valueType = getGenericClassByIndex(genericType, 1);@b@ Class<?> keyClazz;@b@ if (keyType instanceof Class) {@b@ keyClazz = (Class<?>) keyType;@b@ } else if (keyType instanceof ParameterizedType) {@b@ keyClazz = (Class<?>) ((ParameterizedType) keyType).getRawType();@b@ } else {@b@ keyClazz = entry.getKey() == null ? null : entry.getKey().getClass();@b@ }@b@ Class<?> valueClazz;@b@ if (valueType instanceof Class) {@b@ valueClazz = (Class<?>) valueType;@b@ } else if (valueType instanceof ParameterizedType) {@b@ valueClazz = (Class<?>) ((ParameterizedType) valueType).getRawType();@b@ } else {@b@ valueClazz = entry.getValue() == null ? null : entry.getValue().getClass();@b@ }@b@@b@ Object key = keyClazz == null ? entry.getKey() : realize0(entry.getKey(), keyClazz, keyType, history);@b@ Object value = valueClazz == null ? entry.getValue() : realize0(entry.getValue(), valueClazz, valueType, history);@b@ result.put(key, value);@b@ }@b@ return result;@b@ } else if (type.isInterface()) {@b@ Object dest = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{type}, new PojoInvocationHandler(map));@b@ history.put(pojo, dest);@b@ return dest;@b@ } else {@b@ Object dest = newInstance(type);@b@ history.put(pojo, dest);@b@ for (Map.Entry<Object, Object> entry : map.entrySet()) {@b@ Object key = entry.getKey();@b@ if (key instanceof String) {@b@ String name = (String) key;@b@ Object value = entry.getValue();@b@ if (value != null) {@b@ Method method = getSetterMethod(dest.getClass(), name, value.getClass());@b@ Field field = getField(dest.getClass(), name);@b@ if (method != null) {@b@ if (!method.isAccessible())@b@ method.setAccessible(true);@b@ Type ptype = method.getGenericParameterTypes()[0];@b@ value = realize0(value, method.getParameterTypes()[0], ptype, history);@b@ try {@b@ method.invoke(dest, value);@b@ } catch (Exception e) {@b@ e.printStackTrace();@b@ throw new RuntimeException("Failed to set pojo " + dest.getClass().getSimpleName() + " property " + name@b@ + " value " + value + "(" + value.getClass() + "), cause: " + e.getMessage(), e);@b@ }@b@ } else if (field != null) {@b@ value = realize0(value, field.getType(), field.getGenericType(), history);@b@ try {@b@ field.set(dest, value);@b@ } catch (IllegalAccessException e) {@b@ throw new RuntimeException("Failed to set filed " + name + " of pojo " + dest.getClass().getName() + " : " + e.getMessage(), e);@b@ }@b@ }@b@ }@b@ }@b@ }@b@ if (dest instanceof Throwable) {@b@ Object message = map.get("message");@b@ if (message instanceof String) {@b@ try {@b@ Field filed = Throwable.class.getDeclaredField("detailMessage");@b@ if (!filed.isAccessible()) {@b@ filed.setAccessible(true);@b@ }@b@ filed.set(dest, message);@b@ } catch (Exception e) {@b@ }@b@ }@b@ }@b@ return dest;@b@ }@b@ }@b@ return pojo;@b@ }@b@@b@ /**@b@ * Get parameterized type@b@ *@b@ * @param genericType generic type@b@ * @param index index of the target parameterized type@b@ * @return Return Person.class for List<Person>, return Person.class for Map<String, Person> when index=0@b@ */@b@ private static Type getGenericClassByIndex(Type genericType, int index) {@b@ Type clazz = null;@b@ // find parameterized type@b@ if (genericType instanceof ParameterizedType) {@b@ ParameterizedType t = (ParameterizedType) genericType;@b@ Type[] types = t.getActualTypeArguments();@b@ clazz = types[index];@b@ }@b@ return clazz;@b@ }@b@@b@ private static Object newInstance(Class<?> cls) {@b@ try {@b@ return cls.newInstance();@b@ } catch (Throwable t) {@b@ try {@b@ Constructor<?>[] constructors = cls.getDeclaredConstructors();@b@ if (constructors != null && constructors.length == 0) {@b@ throw new RuntimeException("Illegal constructor: " + cls.getName());@b@ }@b@ Constructor<?> constructor = constructors[0];@b@ if (constructor.getParameterTypes().length > 0) {@b@ for (Constructor<?> c : constructors) {@b@ if (c.getParameterTypes().length < constructor.getParameterTypes().length) {@b@ constructor = c;@b@ if (constructor.getParameterTypes().length == 0) {@b@ break;@b@ }@b@ }@b@ }@b@ }@b@ constructor.setAccessible(true);@b@ return constructor.newInstance(new Object[constructor.getParameterTypes().length]);@b@ } catch (InstantiationException e) {@b@ throw new RuntimeException(e.getMessage(), e);@b@ } catch (IllegalAccessException e) {@b@ throw new RuntimeException(e.getMessage(), e);@b@ } catch (InvocationTargetException e) {@b@ throw new RuntimeException(e.getMessage(), e);@b@ }@b@ }@b@ }@b@@b@ private static Method getSetterMethod(Class<?> cls, String property, Class<?> valueCls) {@b@ String name = "set" + property.substring(0, 1).toUpperCase() + property.substring(1);@b@ Method method = NAME_METHODS_CACHE.get(cls.getName() + "." + name + "(" + valueCls.getName() + ")");@b@ if (method == null) {@b@ try {@b@ method = cls.getMethod(name, valueCls);@b@ } catch (NoSuchMethodException e) {@b@ for (Method m : cls.getMethods()) {@b@ if (ReflectUtils.isBeanPropertyWriteMethod(m) && m.getName().equals(name)) {@b@ method = m;@b@ }@b@ }@b@ }@b@ if (method != null) {@b@ NAME_METHODS_CACHE.put(cls.getName() + "." + name + "(" + valueCls.getName() + ")", method);@b@ }@b@ }@b@ return method;@b@ }@b@@b@ private static Field getField(Class<?> cls, String fieldName) {@b@ Field result = null;@b@ if (CLASS_FIELD_CACHE.containsKey(cls) && CLASS_FIELD_CACHE.get(cls).containsKey(fieldName)) {@b@ return CLASS_FIELD_CACHE.get(cls).get(fieldName);@b@ }@b@ try {@b@ result = cls.getDeclaredField(fieldName);@b@ result.setAccessible(true);@b@ } catch (NoSuchFieldException e) {@b@ for (Field field : cls.getFields()) {@b@ if (fieldName.equals(field.getName()) && ReflectUtils.isPublicInstanceField(field)) {@b@ result = field;@b@ break;@b@ }@b@ }@b@ }@b@ if (result != null) {@b@ ConcurrentMap<String, Field> fields = CLASS_FIELD_CACHE.get(cls);@b@ if (fields == null) {@b@ fields = new ConcurrentHashMap<String, Field>();@b@ CLASS_FIELD_CACHE.putIfAbsent(cls, fields);@b@ }@b@ fields = CLASS_FIELD_CACHE.get(cls);@b@ fields.putIfAbsent(fieldName, result);@b@ }@b@ return result;@b@ }@b@@b@ public static boolean isPojo(Class<?> cls) {@b@ return !ReflectUtils.isPrimitives(cls)@b@ && !Collection.class.isAssignableFrom(cls)@b@ && !Map.class.isAssignableFrom(cls);@b@ }@b@@b@}