quellanan · 2019年09月29日

如何优雅实现属性的动态注入

前言

这是在实际开发项目中遇到的一个问题。从数据库查询返回的 List< Map< String, Object\>\> 的集合。并且返回的列名是中文的,项目也没有使用mybatis 直接使用的jdbcTemplate. 并且字段还超级多,这样将数据转换的时候如果一个一个的注入就会让代码臭长臭长的,所以才有了动态注入。
我这里我整个思路都贴出来。

实例类Entry

我们先建一个entry类。用于对象存储。
我这里 创建一个BaseDateBean 的类

@Setter
@Getter
public class BaseDateBean {

    private String  startTime;
    private String  operator;
    private String  code;
    private String  testNumber;
    private String  iphoneCardCode;
    private String  sampleNumber;
    private String  sampleTime;
    private String  callNumber;
    private String  callStatus;
    private String  downInstantaneousSpeedCard;
    private String  upInstantaneousSpeedCard;
    private String  ssid;
    private String  bssid;
    private String  encryptType;
    private String  intranetIp;
    private String  externalIp;
    private String  rssi;
    private String  WIFIFrequency;
    private String  WIFIChannel;
    private String  baiduLongitude;
    private String  baiduLatitude;
    private String  originalLongitude;
    private String  originalLatitude;
    private String  positioningPrecision;
    private String  positioningType;
    private String  businessType;
    private String  networkType;
    private String  speedType;
    private String  tac;
    private String  eci;
    private String  mnc;
    private String  mcc;
    private String  rsrq;
    private String  earfcnDl;
    private String  earfcnUl;
    private String  frequencyDl;
    private String  band;
    private String  sinr;
    private String  cdmaRxlev;
    private String  evdoRxlev;
    private String  earfcn;
    private String  psc;
    private String  uarfcn;
    private String  rscp;
    private String  rsrp;
    private String  imsi;
    private String  imei;
    private String  lac;
    private String  ci;
    private String  signalStrength;
    private String  snr;
    private String  pci;
    private String  nid;
    private String  bid;
    private String  sid;
    private String  cdmaDbm;
    private String  cdmaEcio;
    private String  evdoDbm;
    private String  evdoEcio;
    private String  evdoSnr;
    private String  arfcn;
    private String  frequencyUl;
    private String  bsic;
    private String  rxlev;
    private String  averageSpeed;
    private String  updatedLongitude;
    private String  updatedLatitude;
    private String  averageUpstreamRate;
    private String  averageDownstreamRate;
}

可以看到在实际项目中属性还是很多的,我这个还只是初版的,所以如果一个一个的set注入就很low了。

创建map映射

在创建好实体类后,还得创建一个静态的map 集合,将数据库的列名和我们实体类的属性名做一个一一对应。这里创建的这个map 集合是我个人愚见。没有想到更好的办法就先这样处理的。
我们创建一个BaseDataMap类

public  class BaseDataMap{
    private BaseDataMap(){}
    public static final Map<String,String> cnEnMap=new HashMap<>();
    static{
        cnEnMap.put("测试开始时间","startTime");
        cnEnMap.put("运营商","operator");
        cnEnMap.put("编号","code");
        cnEnMap.put("测试编号","testNumber");
        cnEnMap.put("手机卡编号","iphoneCardCode");
        cnEnMap.put("采样编号","sampleNumber");
        cnEnMap.put("采样时间","sampleTime");
        cnEnMap.put("呼叫编号","callNumber");
        cnEnMap.put("呼叫状态","callStatus");
        cnEnMap.put("下行瞬时速度","downInstantaneousSpeedCard");
        cnEnMap.put("上行瞬时速度","upInstantaneousSpeedCard");
        cnEnMap.put("SSID","ssid");
        cnEnMap.put("BSSID","bssid");
        cnEnMap.put("加密类型","encryptType");
        cnEnMap.put("内网IP","intranetIp");
        cnEnMap.put("外网IP","externalIp");
        cnEnMap.put("RSSI","rssi");
        cnEnMap.put("WIFI频率","WIFIFrequency");
        cnEnMap.put("WIFI信道","WIFIChannel");
        cnEnMap.put("百度经度","baiduLongitude");
        cnEnMap.put("百度纬度","baiduLatitude");
        cnEnMap.put("原始经度","originalLongitude");
        cnEnMap.put("原始纬度","originalLatitude");
        cnEnMap.put("定位精度","positioningPrecision");
        cnEnMap.put("定位类型","positioningType");
        cnEnMap.put("数据业务类型","businessType");
        cnEnMap.put("网络类型","networkType");
        cnEnMap.put("速度类型","speedType");
        cnEnMap.put("TAC","tac");
        cnEnMap.put("ECI","eci");
        cnEnMap.put("MNC","mnc");
        cnEnMap.put("MCC","mcc");
        cnEnMap.put("RSRQ","rsrq");
        cnEnMap.put("EARFCN DL","earfcnDl");
        cnEnMap.put("EARFCN UL","earfcnUl");
        cnEnMap.put("FREQUENCY DL","frequencyDl");
        cnEnMap.put("BAND","band");
        cnEnMap.put("SINR","sinr");
        cnEnMap.put("CDMA RXLEV","cdmaRxlev");
        cnEnMap.put("EVDO RXLEV","evdoRxlev");
        cnEnMap.put("EARFCN","earfcn");
        cnEnMap.put("PSC","psc");
        cnEnMap.put("UARFCN","uarfcn");
        cnEnMap.put("RSCP","rscp");
        cnEnMap.put("RSRP","rsrp");
        cnEnMap.put("IMSI","imsi");
        cnEnMap.put("IMEI","imei");
        cnEnMap.put("LAC","lac");
        cnEnMap.put("CI","ci");
        cnEnMap.put("信号强度","signalStrength");
        cnEnMap.put("SNR","snr");
        cnEnMap.put("PCI","pci");
        cnEnMap.put("NID","nid");
        cnEnMap.put("BID","bid");
        cnEnMap.put("SID","sid");
        cnEnMap.put("CDMA DBM","cdmaDbm");
        cnEnMap.put("CDMA ECIO","cdmaEcio");
        cnEnMap.put("EVDO DBM","evdoDbm");
        cnEnMap.put("EVDO ECIO","evdoEcio");
        cnEnMap.put("EVDO SNR","evdoSnr");
        cnEnMap.put("ARFCN","arfcn");
        cnEnMap.put("FREQUENCY UL","frequencyUl");
        cnEnMap.put("BSIC","bsic");
        cnEnMap.put("RXLEV","rxlev");
        cnEnMap.put("速率","averageSpeed");
        cnEnMap.put("更正后经度","updatedLongitude");
        cnEnMap.put("更正后纬度","updatedLatitude");
        cnEnMap.put("上行平均速率","averageUpstreamRate");
        cnEnMap.put("下行平均速率","averageDownstreamRate");
    }
}

可以看到就是一个动态的map。

映射类

接下来就是核心代码啦。我们创建一个ReflectHelper类

@Slf4j
public class ReflectHelper {

    private Class cls;
    /**
     * 传过来的对象
     */
    private Object obj;
    private Hashtable<String, Method> getMethods = null;
    private Hashtable<String, Method> setMethods = null;
    public ReflectHelper(Object o) {
        obj = o;
        initMethods();
    }
    public void initMethods() {
        getMethods = new Hashtable<>();
        setMethods = new Hashtable<>();
        cls = obj.getClass();
        Method[] methods = cls.getMethods();
        // 定义正则表达式,从方法中过滤出getter / setter 函数.
        String gs = "get(\\w+)";
        Pattern getM = Pattern.compile(gs);
        String ss = "set(\\w+)";
        Pattern setM = Pattern.compile(ss);
        // 把方法中的"set" 或者 "get" 去掉,$1匹配第一个
        String rapl = "$1";
        String param;
        for (int i = 0; i < methods.length; ++i) {
            Method m = methods[i];
            String methodName = m.getName();
            if (Pattern.matches(gs, methodName)) {
                param = getM.matcher(methodName).replaceAll(rapl).toLowerCase();
                getMethods.put(param, m);
            } else if (Pattern.matches(ss, methodName)) {
                param = setM.matcher(methodName).replaceAll(rapl).toLowerCase();
                setMethods.put(param, m);
            }
        }
    }
    public boolean setMethodValue(String property,Object object) {
        Method m = setMethods.get(property.toLowerCase());
        if (m != null) {
            try {
                // 调用目标类的setter函数
                m.invoke(obj, object);
                return true;
            } catch (Exception ex) {
                ex.printStackTrace();
                return false;
            }
        }
        return false;
    }
}

上面代码可以看到其实也就两个方法setMethodValue 和initMethods 。
initMethods 方法是在实例化 ReflectHelper 这个类的时候执行的,主要的工作就是找到我们需要动态注入实例类的get 和set 方法。而setMethodValue 方法就是给这个属性赋值的。
# 实现方法
现在准备工作做好了,怎么使用呢?

private List<BaseDateBean> getBaseDateBean(List<Map<String, Object>> mapList){
       List<BaseDateBean> list=new ArrayList<>();
       if(mapList==null||mapList.isEmpty()){
           return list;
       }
       BaseDateBean baseDateBean;
       for(Map<String, Object> map:mapList){
           baseDateBean=new BaseDateBean();
           for(Map.Entry<String, Object> entry : map.entrySet()){
               String mapKey = entry.getKey();
               log.info(mapKey);
               ReflectHelper reflectHelper = new ReflectHelper(baseDateBean);
               log.info(BaseDataMap.cnEnMap.get(mapKey));
               String value=entry.getValue()==null?ConstantPool.SEPARATORNULL:entry.getValue().toString();
               log.info(value);
               if(entry.getValue()!=null){
                   reflectHelper.setMethodValue(BaseDataMap.cnEnMap.get(mapKey),String.valueOf(entry.getValue()));
               }
           }
           list.add(baseDateBean);
       }
       return list;
   }

遍历list 集合中的map,动态的将属性值注入到实体类中。

# 番外
我这里是适合我业务开发设计的思路,给大家借鉴参考。

欢迎大家关注个人公众号 "程序员爱酸奶"

分享各种学习资料,包含java,linux,大数据等。资料包含视频文档以及源码,同时分享本人及投递的优质技术博文。

如果大家喜欢记得关注和分享哟❤
file

推荐阅读
关注数
0
文章数
15
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息