背景:
在项目中web层->service->data层直接的交互是使用interface 作为参数进行传递,但是有多数地方需要对interface 数据进行转换,Java本身不支持接口转Map,故记录下此工具类
简介:
一个实用的工具类,用于将Java对象(尤其是命令对象,即符合Java Bean规范的对象)转换为Map<String, Object>
。这个工具类通过反射获取对象的所有属性及其值
基本环境:JDK14以上
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
public abstract class CommandParameterUtils {
public CommandParameterUtils() {
throw new IllegalAccessError();
}
@SuppressWarnings("unchecked")
public static Map<String, ?> getParameterMap(Object cmd) {
if (null == cmd) {
return Collections.emptyMap();
}
if (cmd instanceof Map<?, ?>) {
return (Map<String, ?>) cmd;
}
Map<String, Object> parameterMap = (new HashMap<>());
extractParameter(cmd, cmd.getClass(), parameterMap);
return parameterMap;
}
private static void extractParameter(Object obj, Class<?> clazz, Map<String, Object> parameterMap) {
if (null == clazz
|| Object.class.equals(clazz)
|| clazz.isArray()
|| clazz.isPrimitive()
|| clazz.isEnum()
|| Collection.class.isAssignableFrom(clazz)
|| Iterable.class.isAssignableFrom(clazz)) {
return;
}
Arrays.stream(clazz.getDeclaredMethods())
.map(ParameterNode::new)
.filter(n -> n.canAccess(obj))
.forEach(n -> {
String parameterName = n.getParameterName();
if (!parameterMap.containsKey(parameterName)) {
parameterMap.put(parameterName, n.getParameterValue(obj));
}
});
extractParameter(obj, clazz.getSuperclass(), parameterMap);
for (Class<?> inf : clazz.getInterfaces()) {
extractParameter(obj, inf, parameterMap);
}
}
private record ParameterNode(Method method) {
private boolean isReadOnly() {
String name = method.getName();
return (0 == method.getParameterCount() && (name.startsWith("get") || name.startsWith("is")));
}
boolean canAccess(Object obj) {
if (!isReadOnly()) {
return false;
}
try {
return method.canAccess(obj);
} catch (Exception ignore) {
int modifier = method.getModifiers();
return (Modifier.isPublic(modifier));
}
}
String getParameterName() {
String name = method.getName();
if (name.startsWith("get")) {
String pre = name.substring(0, 4);
return name.replace(pre, pre.toLowerCase().substring(3));
}
if (name.startsWith("is")) {
String pre = name.substring(0, 3);
return name.replace(pre, pre.toLowerCase().substring(2));
}
return name;
}
Object getParameterValue(Object ins) {
try {
return method.invoke(ins);
} catch (Exception cause) {
throw (new IllegalStateException(cause));
}
}
}
}
类及方法概述
-
类
CommandParameterUtils
- 这是一个抽象类,目的是提供一个工具方法来转换对象为Map。
- 构造函数是私有的,抛出异常,防止实例化。
-
方法
getParameterMap(Object cmd)
- 这是工具类的主方法,将对象转换为Map。
- 如果传入的是null,返回空Map。
- 如果传入的对象本身就是Map,直接返回。
- 否则,创建一个新的Map,并调用
extractParameter
方法将对象的属性和值提取到Map中。
-
方法
extractParameter(Object obj, Class<?> clazz, Map<String, Object> parameterMap)
- 这是递归方法,用于遍历对象的所有类、父类和接口,提取它们的属性。
- 忽略一些类型如数组、原始类型、枚举、集合和可迭代类型。
- 遍历类的所有方法,利用
ParameterNode
类提取方法信息,并将属性和值存入Map中。 - 递归调用以处理父类和接口的属性。
-
内部类
ParameterNode
这是一个记录类(Java 14引入的特性),用于封装方法,并提供一些便利方法来处理方法的属性。
-
构造函数
ParameterNode(Method method)
:接受一个方法对象,创建一个新的节点。
-
方法
isReadOnly
- 检查方法是否为只读方法。只读方法是没有参数并且以"get"或"is"开头的方法。
-
方法
canAccess
- 检查方法是否可以访问。如果方法不是只读的,返回
false
。 - 尝试检查方法是否可以访问,并捕获可能的异常。如果方法可以访问,返回
true
。如果捕获到异常,则检查方法的修饰符是否为public
。
- 检查方法是否可以访问。如果方法不是只读的,返回
-
方法
getParameterName
- 获取属性名。方法名以"get"开头的属性名去掉"get"前缀并将首字母小写。以"is"开头的属性名去掉"is"前缀并将首字母小写。
-
方法
getParameterValue
- 调用方法获取属性值,并返回结果。如果调用方法时出现异常,抛出
IllegalStateException
。
- 调用方法获取属性值,并返回结果。如果调用方法时出现异常,抛出
使用示例
public interface Command {
String name();
}
public class cmd implements Command {
private String name;
public cmd(String name) {
this.name= name;
}
@Override
public String getName() {
return name;
}
}
使用CommandParameterUtils
类将其转换为Map
public class Main {
public static void main(String[] args) {
Command command = new cmd("test");
Map<String, ?> paramMap = CommandParameterUtils.getParameterMap(command);
System.out.println(paramMap);
}
}