前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java属性和Map映射通用方案

Java属性和Map映射通用方案

作者头像
明明如月学长
发布2021-08-31 15:13:03
1.1K0
发布2021-08-31 15:13:03
举报
文章被收录于专栏:明明如月的技术专栏

一、背景

有类似JSON的key和实体属性不对应的场景,可以通过JSON类库提供的注解加在属性上填写值别名,然后JSON转换类可以实现正确的转换。

但是如果需求和JSON没半毛钱关系,如实现Map 到对象属性的映射,而且map中的key和属性名还不一致。肿么办?

注意这里的map的value实际适合属性的类型是保持一致的!!!

二、解决方案

2.1 注解+反射

注解

代码语言:javascript
复制
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 需要映射的字段
 */
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Field2Map {
    /**
     * 别名(属性名和map的key不对应时设置)
     */
    String alias() default "";
}

实体

代码语言:javascript
复制
@Data
public class MyBean {

    @Field2Map(alias = "alias")
    private String name;

    @Field2Map(alias = "myAge")
    private Integer age;

    @Field2Map
    private String nick;

    private Boolean no;
}

封装

代码语言:javascript
复制
import lombok.Data;

import java.lang.reflect.Field;
import java.lang.reflect.Method;


@Data
public class Invoke {
    
    private Field field;
    
    private Method getMethod;
    
    private Method setMethod;
    
    private String key;
}

工具类

代码语言:javascript
复制
package com.chujianyun.web.util;

import com.chujianyun.web.annotation.Field2Map;
import com.chujianyun.web.bean.Invoke;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class InvokeUtil {

    private static Map> cache = new ConcurrentHashMap<>();

    private static List getInvokesUseCacheIfPossible(Class clazz) throws IntrospectionException {
        List invokes;
        if (cache.containsKey(clazz)) {
            invokes = cache.get(clazz);
        } else {
            invokes = getInvokes(clazz);
            cache.put(clazz, invokes);
        }
        return invokes;
    }

    /**
     * 带转Map
     */
    public static  Map toMap(T object) throws InvocationTargetException, IllegalAccessException, IntrospectionException {

        // 这里可以用缓存
        List invokes = getInvokesUseCacheIfPossible(object.getClass());

        if (CollectionUtils.isEmpty(invokes)) {
            return new HashMap<>(0);
        }
        Map data = new HashMap<>(invokes.size());
        for (Invoke invoke : invokes) {
            data.put(invoke.getKey(), invoke.getGetMethod().invoke(object));
        }
        return data;
    }

    /**
     * Map转对象
     */
    public static  T toObject(Map data, Class tClass) throws InvocationTargetException, IllegalAccessException, InstantiationException, IntrospectionException {

        if (MapUtils.isEmpty(data)) {
            return null;
        }

        T object = tClass.newInstance();

        // 这里可以用缓存
        List invokes = getInvokesUseCacheIfPossible(object.getClass());
        if (CollectionUtils.isEmpty(invokes)) {
            return null;
        }

        for (Invoke invoke : invokes) {

            if (data.containsKey(invoke.getKey())) {
                invoke.getSetMethod().invoke(object, data.get(invoke.getKey()));
            }
        }
        return object;
    }

    /**
     * 获取调用对象
     */
    public static List getInvokes(Class clazz) throws IntrospectionException {

        List invokes = new ArrayList<>(2);
        Field[] declaredFields = clazz.getDeclaredFields();

        for (Field field : declaredFields) {
            if (field.isAnnotationPresent(Field2Map.class)) {
                Invoke invoke = new Invoke();
                invoke.setField(field);
                Field2Map field2Map = field.getAnnotation(Field2Map.class);
                String alias = field2Map.alias();
                if (StringUtils.isNotBlank(alias)) {
                    invoke.setKey(alias);
                } else {
                    invoke.setKey(field.getName());
                }
                PropertyDescriptor descriptor = new PropertyDescriptor(field.getName(), clazz);
                invoke.setGetMethod(descriptor.getReadMethod());
                invoke.setSetMethod(descriptor.getWriteMethod());
                invokes.add(invoke);
            }
        }
        return invokes;
    }
}

编写测试类

代码语言:javascript
复制
package com.chujianyun.web.util;

import com.chujianyun.web.bean.Invoke;
import com.chujianyun.web.bean.MyBean;
import org.apache.commons.collections.CollectionUtils;
import org.junit.Assert;
import org.junit.jupiter.api.Test;

import java.beans.IntrospectionException;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

class InvokeUtilTest {

    Map data = new HashMap<>();

    {
        data.put("alias", "test");
        data.put("myAge", 22);
        data.put("nick", "tomcat");
    }

    @Test
    public void toMap() throws IllegalAccessException, IntrospectionException, InvocationTargetException {

        MyBean myBean = new MyBean();
        myBean.setAge(22);
        myBean.setName("test");
        myBean.setNick("tomcat");
        myBean.setNo(Boolean.FALSE);

        Map map = InvokeUtil.toMap(myBean);
        Assert.assertEquals(22, map.get("myAge"));
        Assert.assertEquals("test", map.get("alias"));
        Assert.assertEquals("tomcat", map.get("nick"));
        Assert.assertNull(map.get("no"));
    }

    @Test
    public void toObject() throws IntrospectionException, InstantiationException, IllegalAccessException, InvocationTargetException {
        MyBean myBean = InvokeUtil.toObject(data, MyBean.class);
        Assert.assertNotNull(myBean);
        Assert.assertEquals(Integer.valueOf(22), myBean.getAge());
        Assert.assertEquals("test", myBean.getName());
        Assert.assertEquals("tomcat", myBean.getNick());
        Assert.assertEquals("tomcat", myBean.getNick());
        Assert.assertNull(myBean.getNo());
    }


    @Test
    void getInvokes() throws IntrospectionException, InvocationTargetException, IllegalAccessException {


        List invokes = InvokeUtil.getInvokes(MyBean.class);
        MyBean myBean = new MyBean();

        Set result = new HashSet<>(2);
        for (Invoke invoke : invokes) {
            if (data.containsKey(invoke.getKey())) {
                invoke.getSetMethod().invoke(myBean, data.get(invoke.getKey()));
                result.add(invoke.getGetMethod().invoke(myBean));
            }
        }
        int count = CollectionUtils.countMatches(result, each -> data.values().contains(each));
        Assert.assertEquals(count, data.size());
    }
} 
测试通过. 
上面只是提供一个思考,使用的时候可以在此基础上进行改造。 
在工具类或者Service中定义一个缓存,结构如下  Map> cache = new ConcurrentHashMap(); 
存放解析的类到带有Alias注解属性的调用列表,方便和map直接进行转化。 
  
如果存在就不需要再去反射构造调用列表,提高效率。 
  
2.2 第三方库 
可以使用Orika等属性映射工具来实现类似功能,不过由于个别库需要手动写映射,没有注解这么方便。 
  
 
 如果觉得本文对你有帮助,欢迎点赞评论,欢迎关注我,我将努力创作更多更好的文章。 
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/05/10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、背景
  • 二、解决方案
    • 2.1 注解+反射
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档