这篇博客是通过反射进行实现转换的
在学习redis中,发现了一个知识点,就是Java对象转map,视频中的内容是通过hutool工具转换的,但是我们学习者肯定不能只通过工具来进行转换,更多的是通过这个知识点学习到他的底层是如何进行转换的。
// 新建一个对象
UserDTO userDTO = new UserDTO(1L,"zhangsan","123");
// 通过reflect获取所有属性
// userDTO.getClass().getDeclaredFields() // 暴力获取所有的属性字段
for (Field field : userDTO.getClass().getDeclaredFields()) {
// 设置暴力反射,获取私有属性
field.setAccessible(true);
try {
/**
field.getName() 获取属性字段的字段名称
field.get(userDTO) 相当于 userDTO.getField();
*/
map.put(field.getName(),field.get(userDTO));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
for (String s : map.keySet()) {
System.out.println(s+"=="+map.get(s));
}
@Test
void contextLoads() {
Map<String, Object> map = new HashMap<>();
UserDTO userDTO = new UserDTO(1L,"zhangsan","123");
// 通过reflect获取所有属性
for (Field field : userDTO.getClass().getDeclaredFields()) {
// 设置暴力反射,获取私有属性
field.setAccessible(true);
try {
map.put(field.getName(),field.get(userDTO));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
for (String s : map.keySet()) {
System.out.println(s+"=="+map.get(s));
}
}
实体类
package com.sky;
import com.sky.dto.UserDTO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/11/9
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Clazz {
private String ClazzName;
@OneSelf
List<UserDTO> userDTOList;
}
自定义注解
package com.sky;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* @author 尹稳健~
* @version 1.0
* @time 2022/11/9
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface OneSelf {
}
转换方法:
public Map<String,Object> beanToMap(Object o){
Map<String, Object> map = new HashMap<>();
// 通过reflect获取所有树形
for (Field field : o.getClass().getDeclaredFields()) {
// 设置暴力反射,获取私有属性
field.setAccessible(true);
try {
if (field.get(o) != null){
Class<?> aClass = field.get(o).getClass();
OneSelf annotation = aClass.getAnnotation(OneSelf.class);
if (annotation!=null){
Map<String, Object> beanToMap = beanToMap(field.get(o));
map.put(field.getName(),beanToMap);
}else{
map.put(field.getName(),field.get(o));
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return map;
}
在实际项目中相信大多数人都使用过工具类,比如说commons下的和hutool工具类,但是我们只是知道如何调用这个API,而不知道他方法的底层是通过什么样的思路进行实现的。
通过对一些底层的了解和学习,能学到更多的知识,了解底层的一些实现方式,拓展自己的思维。
我将从下面开始介绍如何将JavaBean对象转换为Map。
Introspector是JDK中java.beans包下的类,它为目标JavaBean提供了一种了解原类方法、属性和事件的标准方法。通俗的说,就是可以通过Introspector构建一个BeanInfo对象,而这个BeanInfo对象中包含了目标类中的属性、方法和事件的描述信息,然后可以使用这个BeanInfo对象对目标对象进行相关操作。
JDK原文:
方法介绍:
修饰符 | 返回类型 | 方法名称和参数 | 描述 |
---|---|---|---|
static | String | decapitalize(String name) | 实用方法来取一个字符串并将其转换为正常的Java变量名称大小写。 |
static | void | flushCaches() | 冲洗所有Introspector的内部缓存。 |
static | void | flushFromCaches(类 clz) | 刷新内部缓存信息给一个给定的类。 |
static | BeanInfo | getBeanInfo(类 beanClass) | 内省Java Bean并了解其所有属性,暴露的方法和事件。 |
static | BeanInfo | getBeanInfo(类 beanClass, 类 stopClass) | 内省Java bean并了解其属性,暴露的方法,低于给定的“停止”点。 |
static | BeanInfo | getBeanInfo(类 beanClass, 类 stopClass, int flags) | 对Java Bean进行内省,并了解其所有属性,暴露的方法和事件,低于给定的 stopClass点,受到一些控制 flags 。 |
static | BeanInfo | getBeanInfo(类 beanClass, int flags) | 对Java bean进行内省,并了解其所有属性,公开方法和事件,并遵守一些控制标志。 |
static | String[] | getBeanInfoSearchPath() | 获取将用于查找BeanInfo类的包名称列表。 |
static | void | setBeanInfoSearchPath(String[] path) | 更改将用于查找BeanInfo类的包名称列表。 |
JDK原文:
关键方法介绍:
修饰符 | 返回类型 | 方法名称和参数 | 描述 |
---|---|---|---|
无 | PropertyDescriptor[] | getPropertyDescriptors() | 返回bean的所有属性的描述符。 |
实体类:
package com.sky.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDTO {
private Long id;
private String nickName;
private String icon;
}
方法封装:
反射知识介绍:
// 利用反射调用构造器实例化对象
Object object = beanClass.getDeclaredConstructor().newInstance();
// 通过实例化对象的class对象,获取所有的字段
Field[] fields = object.getClass().getDeclaredFields();
// 返回属性字段的修饰符
int mod = field.getModifiers();
关键方法:
public Object mapToBean(Map<String,Object> map,Class<?> beanClass) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 利用反射调用构造器实例化对象
Object object = beanClass.getDeclaredConstructor().newInstance();
// 通过实例化对象的class对象,获取所有的字段
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields) {
// 返回属性字段的修饰符
int mod = field.getModifiers();
// 如果是静态或者final修饰的不需要添加
if (Modifier.isStatic(mod)|| Modifier.isFinal(mod)){
continue;
}
// 暴力获取私有属性
field.setAccessible(true);
// 相当于object.setterField()
field.set(object,map.get(field.getName()));
}
return object;
}
public Object mapToBean2(Map<String,Object> map,Class<?> beanClass) throws Exception{
// 利用class对象调用构造器实例化对象
Object object = beanClass.getDeclaredConstructor().newInstance();
// 内省Java bean并了解其属性,暴露的方法,==简单来说就是将属性封装到了BeanInfo里面==
BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
// 返回bean的所有属性的描述符。
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
/**
* java.beans.PropertyDescriptor[
* name=icon; values={expert=false; visualUpdate=false; hidden=false;
* enumerationValues=[Ljava.lang.Object;@75c77add; required=false};
* propertyType=class java.lang.String; readMethod=public java.lang.String com.sky.dto.UserDTO.getIcon();
* writeMethod=public void com.sky.dto.UserDTO.setIcon(java.lang.String)]
*/
System.out.println(propertyDescriptor);
// 获取属性的setter方法
Method setter = propertyDescriptor.getWriteMethod();
if (setter!=null){
// 获取值
Object o = map.get(propertyDescriptor.getName());
if (o!=null){
// 利用反射将属性赋值
setter.invoke(object,o);
}
}
}
return object;
}