有类似JSON的key和实体属性不对应的场景,可以通过JSON类库提供的注解加在属性上填写值别名,然后JSON转换类可以实现正确的转换。
但是如果需求和JSON没半毛钱关系,如实现Map
注意这里的map的value实际适合属性的类型是保持一致的!!!
注解
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 "";
}
实体
@Data
public class MyBean {
@Field2Map(alias = "alias")
private String name;
@Field2Map(alias = "myAge")
private Integer age;
@Field2Map
private String nick;
private Boolean no;
}
封装
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;
}
工具类
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;
}
}
编写测试类
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
测试通过.
上面只是提供一个思考,使用的时候可以在此基础上进行改造。
在工具类或者Service中定义一个缓存,结构如下 Map
存放解析的类到带有Alias注解属性的调用列表,方便和map直接进行转化。
如果存在就不需要再去反射构造调用列表,提高效率。
可以使用Orika等属性映射工具来实现类似功能,不过由于个别库需要手动写映射,没有注解这么方便。
如果觉得本文对你有帮助,欢迎点赞评论,欢迎关注我,我将努力创作更多更好的文章。