源碼Git地址: https://github.com/git-simm/simm-framework.git (歡迎大家提交優化代碼 ^_^)
一、業務場景
公司先有業務系統,后來覺得需要抽離公共的底層權限服務。再加上之前的業務對象命名不規范,這次想要一次搞定。面對這種場景,擺在我面前的有三套方案。
- 用底層權限服務提供的數據格式,把業務代碼中不規范的引用都改一遍。影響面實在太廣,放棄;
- 加一個數據適配層,從底層權限服務請求到json數據,定義一套匹配的pojo類型進行接收。之后再用適配方法,進行對象轉換,得到適合系統使用的業務對象。這種搞法比較傳統,代理層、適配器都需要自己人工處理。代碼量還是較大,不夠優雅;
- 利用反射+注解的方式,讓程序自動去匹配不對等的屬性,自行完成數據適配的過程。這種搞法就便捷多了,以后遇到名稱不匹配的屬性,我就直接添加個注解就行了。接下來就開擼吧。
二、實現目標
- 簡單對象,屬性都是簡單類型,能夠自動映射;
- 復雜的Class,要能遞歸進行自動映射;
- 復雜的List對象,要能遞歸進行自動映射;
三、實現方案
- 核心轉換實現類 :ProxyJsonUtil
- 映射關系解析實現類: ProxyResolveUtil
- 反射賦值工具類:ReflectUtil
- 權限平台請求代理:AuthorityUtil
1. 新建兩個注解 ProxyEntity、ProxyProp
/** * 代理實體(用於json序列化&反序列化) */ @Retention(value = RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface ProxyEntity { //代理實體名 String value() default ""; } /** * 代理屬性(用於json序列化&反序列化) */ @Retention(value = RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface ProxyProp { //屬性名 String value(); }
2、核心代碼 反射+遞歸 進行字段映射

package simm.framework.jsonext; import org.springframework.data.util.Pair; import simm.framework.jsonext.annotations.ProxyEntity; import simm.framework.jsonext.annotations.ProxyProp; import simm.framework.jsonext.entity.ProxyField; import simm.framework.jsonext.entity.ProxyMsg; import java.lang.reflect.Field; import java.util.*; /** * 代理信息解析工具 */ public class ProxyResolveUtil { /** * 同步鎖 */ private static Object _lock = new Object(); /** * 代理類型緩存 */ //對類型緩存做線程安全處理 private static Map<String,ProxyMsg> clazzCache = Collections.synchronizedMap(new HashMap<String,ProxyMsg>()); /** * 獲取代理信息 * @param clazz * @return */ public static ProxyMsg getProxyMsg(Class clazz){ String key = clazz.getName(); if(clazzCache.containsKey(key)) return clazzCache.get(key); synchronized (_lock){ //雙重檢查 if(clazzCache.containsKey(key)) return clazzCache.get(key); //開始解析 clazzCache.put(key,getClazzProxyMsg(clazz)); } return clazzCache.get(key); } /** * 獲取類型代理信息 * @param clazz * @return */ private static ProxyMsg getClazzProxyMsg(Class clazz){ ProxyEntity proxyEntity = (ProxyEntity) clazz.getAnnotation(ProxyEntity.class); if(proxyEntity==null) return null; ProxyMsg proxyMsg = new ProxyMsg(); proxyMsg.setClazz(clazz); proxyMsg.setProxyName(proxyEntity.value()); //解析字段信息 List<ProxyField> propList = new ArrayList<>(); List<Pair<String, String>> listConfig= new ArrayList<>(); HashMap<String, String> mapConfig= new HashMap<>(); for (Field field: clazz.getDeclaredFields()){ ProxyProp proxyProp = field.getAnnotation(ProxyProp.class); if(proxyProp == null){ mapConfig.put(field.getName(),field.getName()); continue; }else{ ProxyField pField = new ProxyField(); pField.setField(field); pField.setFieldName(field.getName()); pField.setProxyName(proxyProp.value()); propList.add(pField); //beanutils 做屬性拷貝時,使用該參數 listConfig.add(Pair.of(pField.getProxyName(),pField.getFieldName())); mapConfig.put(pField.getProxyName(),pField.getFieldName()); } } proxyMsg.setFields(propList); proxyMsg.setListConfig(listConfig); proxyMsg.setMapConfig(mapConfig); return proxyMsg; } }

package simm.framework.jsonext; import java.lang.reflect.*; import java.util.Collections; import java.util.HashMap; import java.util.Map; public class ReflectUtil { /** * 同步鎖 */ private static Object _lock = new Object(); /** * 代理字段緩存 */ //對類型緩存做線程安全處理 private static Map<String,Field> fieldCache = Collections.synchronizedMap(new HashMap<String,Field>()); /** * 獲取字段信息 * @param entity * @param fieldName * @param <E> * @return */ public static <E> Field getField(E entity,String fieldName){ String key = entity.getClass().getName()+"@"+fieldName; if(fieldCache.containsKey(key)) return fieldCache.get(key); synchronized (_lock){ //雙重檢查 if(fieldCache.containsKey(key)) return fieldCache.get(key); //開始解析 Field f = null; try { f = entity.getClass().getDeclaredField(fieldName); f.setAccessible(true); } catch (NoSuchFieldException e) { e.printStackTrace(); } fieldCache.put(key,f); } return fieldCache.get(key); } /** * 獲取list 屬性的 泛型類型 * @param entity * @param fieldName * @return */ public static <E> Class<?> getActClazz(E entity, String fieldName){ Field f = getField(entity,fieldName); if(f.getType() == java.util.List.class){ // 如果是List類型,得到其Generic的類型 Type genericType = f.getGenericType(); if(genericType == null) return null; // 如果是泛型參數的類型 if(genericType instanceof ParameterizedType){ ParameterizedType pt = (ParameterizedType) genericType; //得到泛型里的class類型對象 Class<?> genericClazz = (Class<?>)pt.getActualTypeArguments()[0]; return genericClazz; } } return (Class<?>) f.getGenericType(); } /** * 獲取字段值 * @param target * @param fieldName * @param <E> * @return * @throws Exception */ public static <E> Object getFieldVal(E target, String fieldName){ try { return getField(target,fieldName).get(target); } catch (IllegalAccessException e) { e.printStackTrace(); return null; } } /** * 設置字段值 * @param target * @param fieldName * @param <E> * @return */ public static <E> void setFieldVal(E target, String fieldName,Object value) { try { getField(target,fieldName).set(target,value); } catch (IllegalAccessException e) { e.printStackTrace(); } } }

package simm.framework.jsonext; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.parser.ParserConfig; import com.alibaba.fastjson.util.TypeUtils; import org.springframework.data.util.Pair; import simm.framework.jsonext.entity.ProxyMsg; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; import java.util.Map; public class ProxyJsonUtil { /** * 批量拷貝 * @param source * @param clazz * @param <E> * @param deepCopy 是否需要遞歸處理 * @return */ public static <E> List<E> cast(List<JSONObject> source, Class<E> clazz,boolean deepCopy) { List<E> result = new ArrayList<>(); for (JSONObject t : source) { E object = cast(t, clazz, deepCopy); result.add(object); } return result; } /** * 單條拷貝 * @param source * @param clazz * @param <E> * @return */ public static <E> E cast(JSONObject source, Class<E> clazz, boolean deepCopy) { return copyProperties(source, clazz, deepCopy); } /** * 拷貝屬性 * @param source * @param clazz * @param <E> * @param deepCopy * @return */ private static <E> E copyProperties(JSONObject source, Class<E> clazz, boolean deepCopy) { try { E object = TypeUtils.castToJavaBean(source, clazz, ParserConfig.getGlobalInstance()); copyProperties(source, object, deepCopy); return object; } catch (Exception e) { e.printStackTrace(); return null; } } /** * JsonObject屬性值拷貝 * @param source * @param target * @param <E> * @param deepCopy */ private static <E> void copyProperties(JSONObject source,E target, boolean deepCopy) throws Exception { if(deepCopy){ //深層遞歸,處理子對象賦值 proxyEntityCast(source, target, deepCopy); } //代理字段賦值 proxyFieldCast(source, target); } /** * 深層遞歸,處理子對象賦值 * @param source * @param target * @param deepCopy * @param <E> * @throws Exception */ private static <E> void proxyEntityCast(JSONObject source, E target, boolean deepCopy) throws Exception { ProxyMsg msg = ProxyResolveUtil.getProxyMsg(target.getClass()); Map<String,String> map = msg.getMapConfig(); for (Map.Entry<String, Object> entry : source.entrySet()){ //映射實體不包含該屬性,則退出代理賦值 if(!map.containsKey(entry.getKey()))continue; //獲取映射實體的字段名 String fieldName = map.get(entry.getKey()); Object value = entry.getValue(); if(value instanceof JSONArray){ jsonArrayCast(target, deepCopy, fieldName, (JSONArray) value); }else if(value instanceof JSONObject){ jsonObjectCast(target, deepCopy, fieldName, (JSONObject) value); } } } /** * JSONObject轉換處理 * @param target * @param deepCopy * @param fieldName * @param value * @param <E> * @throws Exception */ private static <E> void jsonObjectCast(E target, boolean deepCopy, String fieldName, JSONObject value) throws Exception { //屬性是一個JSONObject 對象 Object fieldTarget = ReflectUtil.getFieldVal(target, fieldName); if(fieldTarget == null){ Type fieldType = ReflectUtil.getField(target, fieldName).getGenericType(); ReflectUtil.setFieldVal(target, fieldName,copyProperties(value,(Class)fieldType,deepCopy)); }else{ //遞歸為JsonObject屬性賦值 copyProperties(value,fieldTarget,deepCopy); } } /** * JSONArray 字段處理 * @param target * @param deepCopy * @param fieldName * @param value * @param <E> * @throws Exception */ private static <E> void jsonArrayCast(E target, boolean deepCopy, String fieldName, JSONArray value) throws Exception { //屬性是一個 JSONArray 的列表對象 //獲取需要被賦值的目標對象 Object fieldTarget = ReflectUtil.getFieldVal(target, fieldName); //目標對象為空,退出運行 Class<?> fieldGenericClazz = ReflectUtil.getActClazz(target,fieldName); if(fieldTarget == null){ fieldTarget = new ArrayList(); ReflectUtil.setFieldVal(target, fieldName,fieldTarget); } JSONArray tempList = value; if(fieldTarget instanceof List){ // 如果是List類型,得到其Generic的類型 List<Object> temps = (List<Object>)fieldTarget; for(int i=0;i<tempList.size();i++){ //遞歸為JsonObject屬性賦值 if(temps.size()<=i){ //自動創建新對象 temps.add(copyProperties((JSONObject)tempList.get(i),fieldGenericClazz,deepCopy)); }else{ copyProperties((JSONObject)tempList.get(i),temps.get(i),deepCopy); } } } } /** * 代理字段賦值 * @param source * @param target * @param <E> */ private static <E> void proxyFieldCast(JSONObject source, E target) { //代理字段賦值 ProxyMsg msg = ProxyResolveUtil.getProxyMsg(target.getClass()); for (Pair<String, String> pair : msg.getListConfig()) { String key = pair.getFirst(); if(!source.containsKey(key)) continue; Object value = source.get(key); if(value instanceof JSONArray || value instanceof JSONObject) continue; ReflectUtil.setFieldVal(target, pair.getSecond(),value); } } }