关于遍历复杂对象中目标属性的值的文章,这是我写的第三篇,也是目前为止我能想到的遍历效率最高的一篇,再前两篇基础上做了相关完善和优化,主要是思路改变了。
具体实现思路:在复杂对象的目标属性上增加自定义注解,目标属性所属类也可能是某个bo的属性,则在这个属性上增加另一个自定义注解,然后利用递归遍历。
闲话少说,直接上代码
一、两个自定义注解
/** * @author: lsl * @date: 2019/3/1 * 作用:字典类属性注解 */ @Target(value = {ElementType.TYPE,ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface DictClass { public String value() default ""; }
/** * @author: lsl * @date: 2019/3/1 * 字典属性注解 */ @Target(value = {ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface DictField { public String dictCode(); public String transColumName() default ""; public String isSpeRule() default "0"; }
二、核心代码,递归遍历类。主要方法是ergodicObject。入口方法是objectCodeTrans。
package com.newcore.pcms.common.support.codetrans.util; import com.halo.core.cache.api.CacheMetaInfoFactory; import com.halo.core.cache.api.CacheService; import com.halo.core.cache.models.IndexItem; import com.halo.core.cache.support.redis.holder.TableHolder; import com.halo.core.common.SpringContextHolder; import com.halo.core.common.models.XOR; import com.halo.core.exception.BusinessException; import com.newcore.pcms.common.support.codetrans.annotation.DictClass; import com.newcore.pcms.common.support.codetrans.annotation.DictField; import com.newcore.pcms.common.support.codetrans.dao.api.ServiceAttrDictDao; import com.newcore.pcms.common.support.codetrans.models.po.pcms.PCodeTransDefPO; import com.newcore.pcms.common.support.codetrans.models.po.pcms.ServiceAttrDictBO; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * 对象级的码表转换工具类 * @author: lsl * @date: 2018/12/12 */ public class CodeTransHolderUtils { private static Logger logger = LoggerFactory.getLogger(CodeTransHolderUtils.class); int num=0; // public static CacheMetaInfoFactory getCacheMetaInfoFactory() { // return (CacheMetaInfoFactory) SpringContextHolder.getBean("cacheMetaInfoFactory"); // } // // public static CacheService getCacheService() { // return (CacheService) SpringContextHolder.getBean("cacheService"); // } /** * 对服务中参数中的属性是字典,进行全部码值转换 * @param srcSysCode 源系统 * @param destSysCode 目标系统 * @param obj 服务中方法入参 * @return */ public static List<String> objectCodeTrans(String srcSysCode, String destSysCode, Object obj) { List<String> listNotIncludeAttr = new ArrayList<>(); // boolean methodParamNameNotBlank = (ConstantTools.FLAG_Y).equals(forwardFlag) ? StringUtils.isNotBlank(serMethodParamName) : true; boolean notBlankFlag = StringUtils.isNotBlank(srcSysCode) && StringUtils.isNotBlank(destSysCode) && (null!= obj) ; logger.info("objectCodeTrans方法的参数:srcSysCode=" + srcSysCode + ",destSysCode=" + destSysCode ); CodeTransHolderUtils utils = new CodeTransHolderUtils(); long startTime = System.currentTimeMillis(); if (notBlankFlag){ if (obj instanceof List<?>) { utils.ergodicList(srcSysCode, destSysCode, (List) obj , listNotIncludeAttr); } else { utils.ergodicObject(srcSysCode, destSysCode, obj, listNotIncludeAttr); } } long endTime = System.currentTimeMillis(); System.err.println("对象级码表转换耗时=" + (endTime-startTime) + ",递归次数=" + utils.num); return listNotIncludeAttr; } /** * * @param srcSysCode 源系统 * @param destSysCode 目标系统 * @param listParam 服务方法入参(list) * @param listNotIncludeAttr 入参字典属性未在码表中找到对应的目标系统字典值的属性名 * @param <T> 码表转换后的入参 */ public <T> void ergodicList(String srcSysCode, String destSysCode, List<T> listParam, List<String> listNotIncludeAttr) { listParam.stream().forEach(t -> { ergodicObject(srcSysCode, destSysCode, t, listNotIncludeAttr); }); } /** * 对象级码表转换。把t中指定attribute属性的值更改为目标系统的字典值,并把在目标系统中没有找到字典值的属性放到listNotIncludeAttr * 注意: * 1、t中的属性类型不能包含以下集合嵌套类型:Set、List<Map<K,V>、Map<K,List<BO>> * 2、t中的属性类型可以是BO、数组、List<BO>、Map<K,BO> * 3、属性名一定要规范,比如private PCodeTransDefPO pCodeTransDefPO(不规范),这样利用反射找不到getter方法,应该改成codeTransDefPO或者PCodeTransDefPO * @param srcSysCode 源系统 * @param destSysCode 目标系统 * @param t 入参对象(是BO非list) * @param listNotIncludeAttr 没有在码表中找到目标系统中对应的码值的字典属性 * @param <T> 码表转换后的入参 */ public <T> void ergodicObject(String srcSysCode, String destSysCode, T t, List<String> listNotIncludeAttr) { num++; Class clazz = t.getClass(); Field[] fields = clazz.getDeclaredFields(); try { for (Field field : fields) { field.setAccessible(true); // String attrType = field.getType().getSimpleName();//属性类型 String attrShortName = field.getName();//属性名 if (field.isAnnotationPresent(DictField.class)){ String attrValue = (String)field.get(t);//属性值 String dictCode = field.getAnnotation(DictField.class).dictCode();//字典名 String transColumName = field.getAnnotation(DictField.class).transColumName();//词汇名 String isSpeRule = field.getAnnotation(DictField.class).isSpeRule();//是否特殊 if(ConstantTools.DEFAULT_0.equals(isSpeRule) ){ transColumName=""; } // String destCodeVal = ""; String destCodeVal =CodeTransDefTools.queryOneUnCodeTransValPOrder(srcSysCode,destSysCode,dictCode,attrValue,isSpeRule,transColumName); //如果t中的目标属性的值为null或者"",则不需要修改其值 if (attrValue != null && !"".equals(attrValue)){ if (destCodeVal != null && !"".equals(destCodeVal)) { //如果从目标系统中查到的字典值非null或者非空字符串,则重新给属性赋值,否则把属性放到listNotIncludeAttr field.set(t, destCodeVal); } else { listNotIncludeAttr.add(attrShortName); logger.info("在目标系统" + destSysCode + "中,没有找到源系统" + srcSysCode + "中属性是" + attrShortName + "对应的字典" + dictCode + ",找到的字典值是:" + destCodeVal); } } }else if (field.isAnnotationPresent(DictClass.class)){ if (isList(field)){ //对List/ArrayList类型的属性遍历 try { PropertyDescriptor descriptor = new PropertyDescriptor(attrShortName, clazz); Method method = descriptor.getReadMethod(); List list = (List) method.invoke(t); if (list != null && list.size() > 0) { list.stream().forEach(p -> { // 递归 ergodicObject(srcSysCode, destSysCode, p, listNotIncludeAttr); }); } } catch (IntrospectionException e) { logger.info(e.getMessage()); } catch (InvocationTargetException e) { logger.info(e.getMessage()); } }else if(isMap(field)){ //对Map/HashMap类型的属性遍历 try { PropertyDescriptor descriptor = new PropertyDescriptor(attrShortName, clazz); Method method = descriptor.getReadMethod(); Map map = (Map) method.invoke(t); if (map != null && map.size() > 0) { for (Object obj : map.values()) { // 递归 ergodicObject(srcSysCode, destSysCode, obj, listNotIncludeAttr); } } } catch (IntrospectionException e) { logger.info(e.getMessage()); } catch (InvocationTargetException e) { logger.info(e.getMessage()); } }else if (field.getType().isArray()) { //对数组类型的属性遍历 field.setAccessible(true); Object[] objArr = (Object[]) field.get(t); if (objArr != null && objArr.length > 0) { for (Object arr : objArr) { //递归 ergodicObject(srcSysCode, destSysCode, arr, listNotIncludeAttr); } } }else { //对自定义类的属性递归 try { PropertyDescriptor descriptor = new PropertyDescriptor(attrShortName, clazz); Method method = descriptor.getReadMethod(); Object obj = method.invoke(t); if (obj != null) { //递归 ergodicObject(srcSysCode, destSysCode, obj, listNotIncludeAttr); } else { continue; } } catch (IntrospectionException e) { logger.info(e.getMessage()); } catch (InvocationTargetException e) { logger.info(e.getMessage()); } } }else { continue; } } } catch (IllegalAccessException e) { logger.info(e.getMessage()); } } /** * 判断是否是List或者ArrayList * @param field * @return */ public boolean isList(Field field){ boolean flag = false; String simpleName = field.getType().getSimpleName(); if ("List".equals(simpleName) || "ArrayList".equals(simpleName)){ flag = true; } return flag; } /** * 判断是否是Map或者HashMap * @param field * @return */ public boolean isMap(Field field){ boolean flag = false; String simpleName = field.getType().getSimpleName(); if ("Map".equals(simpleName) || "HashMap".equals(simpleName)){ flag = true; } return flag; } }
三、如何使用
复杂对象如下:
package com.lsl.model; import com.lsl.annotion.DictClass; import java.util.List; /** * @author: lsl * @date: 2019/2/27 */ public class ParentClass { private String fatherName; private int fatherAge; @DictClass private List<SubClass> list; @DictClass private SubClass subClass; public String getFatherName() { return fatherName; } public void setFatherName(String fatherName) { this.fatherName = fatherName; } public int getFatherAge() { return fatherAge; } public void setFatherAge(int fatherAge) { this.fatherAge = fatherAge; } public List<SubClass> getList() { return list; } public void setList(List<SubClass> list) { this.list = list; } public SubClass getSubClass() { return subClass; } public void setSubClass(SubClass subClass) { this.subClass = subClass; } }
复杂对象ParentClass包含子对象SubClass
子对象SubClass代码:
package com.lsl.model; import com.lsl.annotion.DictField; /** * @author: lsl * @date: 2019/2/27 */ public class SubClass { @DictField(dictCode = "aaa") private String subName; private int subAge; public String getSubName() { return subName; } public void setSubName(String subName) { this.subName = subName; } public int getSubAge() { return subAge; } public void setSubAge(int subAge) { this.subAge = subAge; } }
简单测试,和二、核心代码部分略有区别
package com.lsl; import com.alibaba.fastjson.JSON; import com.lsl.annotion.DictClass; import com.lsl.annotion.DictField; import com.lsl.model.ParentClass; import com.lsl.model.SubClass; import com.lsl.service.MyService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.RequestMapping; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * @author: lsl * @date: 2019/2/15 */ @Controller @RequestMapping("/lsl") public class MyTest { public static void main(String [] args){ SubClass sub1 = new SubClass(); sub1.setSubAge(21); sub1.setSubName("aaa"); SubClass sub2 = new SubClass(); sub2.setSubName("bbb"); sub2.setSubAge(20); SubClass sub3 = new SubClass(); sub3.setSubName("ccc"); sub3.setSubAge(22); List<SubClass> list = new ArrayList(); list.add(sub1); list.add(sub2); ParentClass parent = new ParentClass(); parent.setFatherAge(18); parent.setFatherName("aaa"); parent.setSubClass(sub3); parent.setList(list); ergodicObject(parent); } public static <T> void ergodicObject(T t){ Class clazz = t.getClass(); Field[] fields = clazz.getDeclaredFields(); try { for (Field field : fields) { field.setAccessible(true); // String attrType = field.getType().getSimpleName();//属性类型 String attrShortName = field.getName();//属性名 if (field.isAnnotationPresent(DictField.class)){ String attrValue = (String)field.get(t);//属性值 }else if (field.isAnnotationPresent(DictClass.class)){ if (isList(field)){ //对List/ArrayList类型的属性遍历 try { PropertyDescriptor descriptor = new PropertyDescriptor(attrShortName, clazz); Method method = descriptor.getReadMethod(); List list = (List) method.invoke(t); if (list != null && list.size() > 0) { for(Object obj : list){ ergodicObject(obj); } } } catch (IntrospectionException e) { // logger.info(e.getMessage()); } catch (InvocationTargetException e) { // logger.info(e.getMessage()); } }else if(isMap(field)){ //对Map/HashMap类型的属性遍历 try { PropertyDescriptor descriptor = new PropertyDescriptor(attrShortName, clazz); Method method = descriptor.getReadMethod(); Map map = (Map) method.invoke(t); if (map != null && map.size() > 0) { for (Object obj : map.values()) { // 递归 ergodicObject( obj); } } } catch (IntrospectionException e) { // logger.info(e.getMessage()); } catch (InvocationTargetException e) { // logger.info(e.getMessage()); } }else if (field.getType().isArray()) { //对数组类型的属性遍历 field.setAccessible(true); Object[] objArr = (Object[]) field.get(t); if (objArr != null && objArr.length > 0) { for (Object arr : objArr) { //递归 ergodicObject( arr); } } }else { //对自定义类的属性递归 try { PropertyDescriptor descriptor = new PropertyDescriptor(attrShortName, clazz); Method method = descriptor.getReadMethod(); Object obj = method.invoke(t); if (obj != null) { //递归 ergodicObject( obj); } else { continue; } } catch (IntrospectionException e) { // logger.info(e.getMessage()); } catch (InvocationTargetException e) { // logger.info(e.getMessage()); } } }else { continue; } } } catch (IllegalAccessException e) { e.printStackTrace(); } } public static boolean isList(Field field){ boolean flag = false; String simpleName = field.getType().getSimpleName(); if ("List".equals(simpleName) || "ArrayList".equals(simpleName)){ flag = true; } return flag; } /** * 判断是否是Map或者HashMap * @param field * @return */ public static boolean isMap(Field field){ boolean flag = false; String simpleName = field.getType().getSimpleName(); if ("Map".equals(simpleName) || "HashMap".equals(simpleName)){ flag = true; } return flag; } public static String countMethod(){ StringBuffer a= new StringBuffer("11"); try { try { a = a.append("22"); int m=1/0; } catch (Exception e) { System.err.println("内层catch执行"); throw e; } // return a.toString(); } catch (Exception e) { // return a.toString(); System.err.println("外层catch执行"); throw e; } finally { a = a.append("33"); System.err.println("finally==a==" + a); } return a.toString(); } @Autowired MyService service; // @Transactional @RequestMapping("/lock") public void tranactionalTest(){ BizLock lock = new BizLock(); BizLock bizLock = new BizLock(); bizLock.setBizId("11223344"); bizLock.setBizNo("aabbccdd"); bizLock.setBizType("MTNNO"); long nowTime = System.currentTimeMillis(); bizLock.setCreateTime(new Timestamp(nowTime)); bizLock.setPassTime(new Timestamp(nowTime)); int count = service.addLock(bizLock); if (count==1){ System.err.println("加锁成功" + count); }else { System.err.println("加锁失败" + count); } // int aa = 1/0; // try { // System.err.println("Thread名字"+ Thread.currentThread().getName() + ",开始休眠8888888"); // Thread.sleep(50000L); // } catch (InterruptedException e) { // // } int num=12; System.err.println("endendendend"); // return count; } @RequestMapping("/lock2") public void tranactionalTes2t(){ BizLock lock = new BizLock(); BizLock bizLock = new BizLock(); bizLock.setBizId("11223344"); bizLock.setBizNo("aabbccdd"); bizLock.setBizType("MTNNO"); long nowTime = System.currentTimeMillis(); bizLock.setCreateTime(new Timestamp(nowTime)); bizLock.setPassTime(new Timestamp(nowTime)); int count = service.addLock(bizLock); if (count==1){ System.err.println("加锁成功" + count); }else { System.err.println("加锁失败" + count); } // System.err.println("endendendend"); } }