模擬一個場景:
眾所周知,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 <T> 源數據類型 * @param <E> 目標數據類型 * @return 被賦值后的目標對象集合 * @throws CopyPropertyException 自定義異常,通過反射創建對象或調用方法時拋出的異常 */ public static <T, E> List<E> copyProperties(List<T> source, Class<E> tClass) throws CopyPropertyException { // 判斷傳入源數據是否為空,如果空,則拋自定義異常 if(null == source) { throw new CopyPropertyException("數據源為空"); } // 創建一個集合,用於存儲目標對象,全部數據抓換完成后,將該集合返回 List<E> 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; } }
測試結果
預留占位
開發怎能不留擴展字段 (¬_¬)…