模拟一个场景:
众所周知,EasyExcel导出Excel文档是依赖于注解完成的,在实体类需要导出的属性上面加上注解,导出的时候会自动识别该属性。
假如我们现在需要导出用户的信息,又不想污染原本的实体类,又要过滤掉password这个属性。那么我们可以另外创建一个实体类,不包含password属性。然后我们将查到的用户信息,赋值给新创建的对象即可。
赋值这一步是比较繁琐的,我们需要写循环,然后判断,然后赋值。等将来需要导出管理员信息的时候我们又要写循环,然后判断,然后赋值、、、
怎么能写一个共用的方法去帮我们做循环、判断、赋值这些事呢?源对象类型不确定,目标对象类型不确定。
于是,反射机制来了。它来了,它来了,它哼着小曲走来了、、、
通过 Java 的反射机制,程序员可以更深入地控制程序的运行过程。例如,在程序运行时由用户输入一个类名,然后动态获取该类拥有的构造、属性和方法,甚至调用任意类的任意方法。
Java 反射机制主要提供了以下功能,这些功能都位于 java.lang.reflect包下。
-
在运行时判断任意一个对象所属的类。
-
在运行时构造任意一个类的对象。
-
在运行时判断任意一个类所具有的成员变量和方法。
-
在运行时调用任意一个对象的方法。
-
生成动态代理。
开发环境
请参照: 基于SpringBoot构建分模块项目
创建对象UserOne、UserTwo
UserOne为与数据库表对应的实体类,UserTwo为即将要通过EasyExcel导出的对象
package com.wayne.common.entity; /** * @author Wayne * @date 2019/6/5 */ public class UserOne { private Integer id; private String username; private String password; // Getter and Setter 、、、 }
package com.wayne.common.dto; /** * @author Wayne * @date 2019/6/5 */ public class UserTwo { private Integer id; private String username; // Getter and Setter 、、、 }
编写基于反射的工具类
通过反射创建对象、调用方法
package com.wayne.common.utils; import com.google.common.collect.Lists; import com.wayne.common.exception.CopyPropertyException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.List; /** * @author Wayne * @date 2019/6/5 */ public class BaseUtil { /** * 将一个集合中对象的值拷贝到另一个对象,属性相同即赋值 * @param source 源数据,将此对象数据取出 * @param tClass 目标对象,将取出的数据赋值到该对象中 * @param源数据类型 * @param目标数据类型 * @return 被赋值后的目标对象集合 * @throws CopyPropertyException 自定义异常,通过反射创建对象或调用方法时抛出的异常 */ public staticList copyProperties(List source, Class tClass) throws CopyPropertyException { // 判断传入源数据是否为空,如果空,则抛自定义异常 if(null == source) { throw new CopyPropertyException("数据源为空"); } // 创建一个集合,用于存储目标对象,全部数据抓换完成后,将该集合返回 List targetList = Lists.newArrayList(); // 循环取到单个源对象 for(T t : source) { // 获取源对象的类的详情信息 Class> sClass = t.getClass(); // 获取源对象的所有属性 Field[] sFields = sClass.getDeclaredFields(); // 获取目标对象的所有属性 Field[] tFields = tClass.getDeclaredFields(); E target; try { // 通过类的详情信息,创建目标对象 这一步等同于UserTwo target = new UserTwo(); target = tClass.newInstance(); } catch (Exception e) { e.printStackTrace(); throw new CopyPropertyException("目标对象创建失败"); } // 循环取到源对象的单个属性 for(Field sField : sFields) { // 循环取到目标对象的单个属性 for(Field tField : tFields) { // 判断源对象的属性名、属性类型是否和目标对象的属性名、属性类型一致 if(sField.getName().equals(tField.getName()) && sField.getGenericType().equals(tField.getGenericType())) { try { // 获取源对象的属性名,将属性名首字母大写,拼接如:getUsername、getId的字符串 String sName = sField.getName(); char[] sChars = sName.toCharArray(); sChars[0] -= 32; String sMethodName = "get" + String.valueOf(sChars); // 获得属性的get方法 Method sMethod = sClass.getMethod(sMethodName); // 调用get方法 Object sFieldValue = sMethod.invoke(t); // 获取目标对象的属性名,将属性名首字母大写,拼接如:setUsername、setId的字符串 String tName = tField.getName(); char[] tChars = tName.toCharArray(); tChars[0] -= 32; String tMethodName = "set" + String.valueOf(tChars); // 获得属性的set方法 Method tMethod = tClass.getMethod(tMethodName, tField.getType()); // 调用方法,并将源对象get方法返回值作为参数传入 tMethod.invoke(target, sFieldValue); break; } catch (Exception e) { e.printStackTrace(); throw new CopyPropertyException("转换失败,请检查属性类型是否匹配"); } } } } // 将通过反射创建的目标对象放入集合中 targetList.add(target); } // 返回集合 return targetList; } }
测试结果
预留占位
开发怎能不留扩展字段 (¬_¬)…