前期也写beanToMap、mapToBean的工具类,只是有点土而已。
今天的beanToMap、mapToBean比以往更具有健壮性、高效性,也算是了结了。
健壮性体现在:
若Bean中的实例成员变量为null,则在转换成Map后,对应的键值对中的值也为null。
若Map中没有与Bean的实例成员变量相对应键值对,则在转换成Bean后,Bean实例的那些成员变量也为null。
若Map中键值对的值的真实类型全是String,与Bean的实例成员变量类型无法挨个对应,则对Map中的Value挨个进行类型转换。
高效性体现在:
对于同一个Bean不会进行第二次内省和反射操作。在Bean第一次进行内省和反射操作后,将其操作结果存入缓存中。后面若再对Bean进行内省和反射操作时,先在缓存中看看该Bean是否出现过,若出现过,直接由缓存返回结果;若没有出现过,再对该Bean进行内省和反射操作,再将其操作结果存入缓存,最后返回操作结果。
一、核心代码介绍
private static List<Field> getBeanClassField(Class beanClass)
该方法通过传入的参数beanClass,运用内省和反射,将其所有的Field对象(继承的不要,没有对应的setter、getter方法的不要)取出,并存储于List<Field>集合中;再以beanClass的全限定类名为Key,以List<Field>为Value,存入Map<String, List<Field>>中。
这个Map<String, List<Field>>就是用作缓存的,对于同一个beanClass,只会进行一次内省和反射操作,第二次就从缓存中直接取出内省和反射的操作结果。
private static final Map<String, List<Field>> fieldListMap;
/*
该Map<String,List<Field>>用作缓存,避免对同一个beanClass进行重复的内省和反射操作
Key存储beanClass的全限定类名
Value存储关于beanClass的所有Field的集合List<Field>
*/
static {
fieldListMap = new TreeMap<>();
}
private static List<Field> getBeanClassField(Class beanClass) {
if (fieldListMap.containsKey(beanClass.getName()))
//若缓存Map<String, List<Field>>的Key中已存储beanClass的全限定类名
{
System.err.println("Leave it alone--------"+beanClass.getName()+"---------beanClassField from buffer-------");
return fieldListMap.get(beanClass.getName());
//则直接由缓存Map<String, List<Field>>返回List<Field>
//这样一来,就省去下面的一波内省、反射操作了
}
List<Field> fieldList = null;
Method writeMethod;
Method readMethod;
Field beanClassField;
String beanClassFieldName;
try {
BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);//内省
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();//Field的描述器
fieldList = new ArrayList<>();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
readMethod = propertyDescriptor.getReadMethod();//与Field对应的读方法
writeMethod = propertyDescriptor.getWriteMethod();//与Field对应的写方法
if (readMethod != null && writeMethod != null) //读方法、写方法必须同时存在,这样判断是为了排除getClass()方法
{
beanClassFieldName = propertyDescriptor.getName();//获取出Field的名字
beanClassField = beanClass.getDeclaredField(beanClassFieldName);//根据Field的名字获取beanClass的Field对象
beanClassField.setAccessible(true);//暴力反射
fieldList.add(beanClassField);//将beanClass的Field对象依次存入List<Field>
}
}
}
catch (IntrospectionException | NoSuchFieldException e)
{
e.printStackTrace();
}
fieldListMap.put(beanClass.getName(), fieldList);//缓存Map以beanClass的全限定类名为Key,将List<Field>存入Value
return fieldList;
}
二、运行展示
1.?mapToBean
1.1 Map中键值对与Bean中实例成员变量的名字、类型 一 一 对 应
?
?
1.2 Map中只有与Bean中部分实例成员变量的名字、类型对应的键值对
?
1.3 Map中的值全是字符串类型
2. beanToMap
2.1 Bean中实例成员变量均已赋值
?
2.2 Bean中实例成员变量部分赋值
?3.同一个Bean,一次(反射和内省)就好
运行信息由BeanUtility输出。Bean只要进行了内省和反射操作后,相同的Bean后续就不用再进行内省和反射操作了。
这里用一个for循环、两个Bean进行测试,for循环执行五次,循环体内对两个Bean进行beanToMap操作。当i=0时,两个Bean都会进行内省和反射操作;当i=1,2,3,4时,两个Bean分别从缓存中获取内省和反射操作的结果,同时还会输出一行上述的信息,刚好八次。
三、完整代码
3.1Bean代码
package com.rt.pojo;
import java.sql.Date;
public class Student {
private Integer studentId;
private String name;
private Integer age;
private String gender;
private Date birthdate;
private String grade;//年级
private Double comprehensiveScore;//综合评分
public Student(Integer studentId, String name, Integer age, String gender, Date birthdate, String grade, Double comprehensiveScore) {
this.studentId = studentId;
this.name = name;
this.age = age;
this.gender = gender;
this.birthdate = birthdate;
this.grade = grade;
this.comprehensiveScore = comprehensiveScore;
}
public Student() {
}
public Integer getStudentId() {
return studentId;
}
public void setStudentId(Integer studentId) {
this.studentId = studentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public Date getBirthdate() {
return birthdate;
}
public void setBirthdate(Date birthdate) {
this.birthdate = birthdate;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
public Double getComprehensiveScore() {
return comprehensiveScore;
}
public void setComprehensiveScore(Double comprehensiveScore) {
this.comprehensiveScore = comprehensiveScore;
}
@Override
public String toString() {
return "Student{" +
"studentId=" + studentId +
", name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", birthdate=" + birthdate +
", grade='" + grade + '\'' +
", comprehensiveScore=" + comprehensiveScore +
'}';
}
}
3.2主要代码BeanUtility.java
package com.rt.util;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class BeanUtility {
private static final Map<String, List<Field>> fieldListMap;
/*
该Map<String,List<Field>>用作缓存,避免对同一个beanClass进行重复的内省和反射操作
Key存储beanClass的全限定类名
Value存储关于beanClass的所有Field的集合List<Field>
*/
static {
fieldListMap = new TreeMap<>();
}
private static List<Field> getBeanClassField(Class beanClass) {
if (fieldListMap.containsKey(beanClass.getName()))
//若缓存Map<String, List<Field>>的Key中已存储beanClass的全限定类名
{
System.err.println("Leave it alone--------"+beanClass.getName()+"---------beanClassField from buffer-------");
return fieldListMap.get(beanClass.getName());
//则直接由缓存Map<String, List<Field>>返回List<Field>
//这样一来,就省去下面的一波内省、反射操作了
}
List<Field> fieldList = null;
Method writeMethod;
Method readMethod;
Field beanClassField;
String beanClassFieldName;
try {
BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);//内省
PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();//Field的描述器
fieldList = new ArrayList<>();
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
readMethod = propertyDescriptor.getReadMethod();//与Field对应的读方法
writeMethod = propertyDescriptor.getWriteMethod();//与Field对应的写方法
if (readMethod != null && writeMethod != null) //读方法、写方法必须同时存在,这样判断是为了排除getClass()方法
{
beanClassFieldName = propertyDescriptor.getName();//获取出Field的名字
beanClassField = beanClass.getDeclaredField(beanClassFieldName);//根据Field的名字获取beanClass的Field对象
beanClassField.setAccessible(true);//暴力反射
fieldList.add(beanClassField);//将beanClass的Field对象依次存入List<Field>
}
}
}
catch (IntrospectionException | NoSuchFieldException e)
{
e.printStackTrace();
}
fieldListMap.put(beanClass.getName(), fieldList);//缓存Map以beanClass的全限定类名为Key,将List<Field>存入Value
return fieldList;
}
public static <T> T mapToBean(Map<String, Object> beanMap, Class<T> beanClass) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException
{
if (beanMap.isEmpty())
return null;
List<Field> fieldList = getBeanClassField(beanClass);
T beanObject = beanClass.getConstructor().newInstance();
Object valueOfMap;
Object valueOfField;
Class fieldClass;
Method method;
String keyOfMap;
for (Field field : fieldList)
{
keyOfMap=field.getName();
valueOfMap = beanMap.get(keyOfMap);
if(valueOfMap==null)//若beanMap中没有与Field名字对应的Value
continue;//直接跳过本次对beanObject实例成员变量的赋值
fieldClass=field.getType();//获取Field对应的类型Class
if (valueOfMap.getClass()==fieldClass)//若beanMap的Value类型与beanObject的Field类型相同
{
field.set(beanObject,valueOfMap);//则直接为beanObject对应的Field赋值
}
else//若beanMap的Value类型与beanClass的Field类型不同
{
method=fieldClass.getMethod("valueOf",String.class);//从Field类型对应的Class中获取静态方法"valueOf"对象,即Method对象
valueOfField=method.invoke(null,valueOfMap.toString());//用Method对象重新封装beanMap的Value
field.set(beanObject,valueOfField);//将重新封装的Value赋值给beanObject对应的Field
}
}
return beanObject;
}
public static <T> Map<String, Object> beanToMap(T beanObject) throws IllegalAccessException {
List<Field> fieldList = getBeanClassField(beanObject.getClass());
Map<String, Object> beanMap = new TreeMap<>();
for (Field field : fieldList) {
beanMap.put(field.getName(), field.get(beanObject));
}
return beanMap;
}
}
四、感受
写代码能够随心所欲,写注释有点很费劲,不过很练脑子,对技术的认识和把握,又会更上一层?楼!加油!
|