POI解析Excel之應用反射等技術實現動態讀取
目錄樹
- 背景
- 技術選型
- 問題分析
- 技術要點及難點分析
- 原始碼分析
- 測試用例
背景
Tip:因為產品提的需求我都開發完了,進行了專案提測;前天老大走過來說:你用spring-boot開發一個解析Excel的jar包.....詳細對話如下:
A:世生,你用spring-boot開發一個解析Excel的jar包。
B:為什麼不在原來的專案上進行開發呢?(很納悶,這個東西不是一般用於前端上傳資料的嘛,這是後端層,咋搞這個)
A:因為xxxx這個需求有比較多的資料需要初始化,這個jar是作為一個上線的資料初始化指令碼
B:嗯,好的
技術選型
畢竟是第一次寫解析Excel程式碼,問了度娘說是有兩種方式可以做到。一是利用 wso2的 jxl 解析 , 二是利用 apache的poi 解析;我去maven repository官網搜尋了這兩個jar包,對比了下jml的最新版本是2.6.12竟然是2011.05月更新的,而poi的最新版本是4.0.x是2018.08更新的;個人覺得jml最新版本都是2011.05更新的,相對於apache的poi包來說更不靠譜;不斷持續更新的開源專案或者開源jar包不管是bug還是相容性相對來說是越來越好。所以最終選定用apache大佬的poi進行開發。
問題分析
解析Excel的關鍵點是在於從Excel表格中讀取資料到記憶體(解析Excel),然後可能是校驗資料、通過業務邏輯分析資料、最終持久化到資料庫中;其實這其中最重要的不是解析Excel,而是將解析出的資料持久化到資料庫中以備有用之需。而解析Excel的這塊功能只能算是一個Util類,不能與業務程式碼耦合一起;然後我看到很多的Excel解析相關的程式碼都是在解析資料中混淆業務程式碼邏輯,其實這些都是不可取的,這會導致解析Excel邏輯與業務邏輯相耦合也就是冗雜、程式碼重用率低、可擴充套件性低等問題。因為之前在做專案的時候遇到過一個問題:我負責的模組是一箇中間模組(通訊採用Dubbo),其他系統要依賴我這個介面進行請求的轉發可我呼叫其他系統返回的結果物件卻各個都不同,我叫其他系統負責人說要統一呼叫介面返回的物件,但是其他系統的負責人都不是同一個人執行起來效率太低了,在歷經一個星期都無果的情況下我只能撒下自己的殺手鐗了;在這種極端條件下最終我不管其他資料的介面返回的物件是什麼,我直接用Object接收返回型別,通過反射獲取決定請求成功與否的屬性值(欣慰的是當時必傳屬性值倒是一樣的)。通過這種方法我可以少些很多的程式碼(當時其他系統有15+),不然的話每呼叫不同系統的介面我都需要進行邏輯判斷或者是乾脆對於呼叫他們不同的系統我採用不同介面進行轉發,但選用這種方法卻便利多了。
以上問題分析及一個場景的描述很好理解,但是通過Object接收返回資訊得這個場景事實上卻有所欠佳;返回物件不同的這個問題最好的處理方案就是統一介面,我那個方案是在需求推動但別人無法及時配合的極端條件下使用的,是沒辦法中的辦法,但這也是一個沒有辦法中的一個最優的處理方案,相容性比較強。以下我就用圖來分析這兩種情況的比較:
1.非動態模式 :將Excel資料載入到記憶體與業務程式碼邏輯混合,資料在解析期間交叉傳遞。 弊端 :每新增一個需要解析的Excel,解析Excel程式碼塊就需要重新開發,程式碼複用率底下、可擴充套件性也低。
2.動態模式:將Excel資料載入到記憶體與業務程式碼邏輯分開;Excel資料載入到記憶體之後才將資料傳遞給業務程式碼邏輯處理,解析Excel與業務程式碼之間分開; 優點 :將解析Excel的這部分程式碼封裝為一個ExcelUtil,程式碼複用率明顯提高,而且解析與業務程式碼間實行解耦,可擴充套件性增強。
技術要點及難點分析
要實現動態解析,實現解析與業務程式碼邏輯相解耦;那麼我們不難會想起一個Java的一個關鍵技術-Reflection(反射原理),Python、Ruby等是動態語言而理論上Java是一門靜態語言,但是Java引入了Reflection技術實現了動態性。反射原理我們都比較熟悉,就是在執行期間動態獲取類的所有屬性及其方法,可以對這些資料進行相關的操作。以上動態解析Excel的實現就需要用到Java這項的高階技術了,通過這項技術可以實現動態解析、解析與業務邏輯解耦等。為了實現動態解析的目的我應用了Java反射技術,但是在開發的過程我發現反射執行一行資料結束的時候如何儲存呢?換句話說就是:解析的時候一行的Excel資料封裝後就是一個bean,一個Excel表格就是多個bean 即“beans”;如果我們直接將反射執行後的結果儲存至List中,當解析整個Excel結束後我們會發現,整個List裡面的物件的值完全一樣的?what?這是什麼原因導致的呢?這就是類似於:Object obj=new Object(),我們每次解析都只是把 obj 放在List中,List中的每一個物件都是同一個 obj(引用不變,例項也不變),所以自然也就相同了;因為當一個類執行反射的時候其實它的執行時狀態只有一個,也就是類似於只有一個例項,而傳統的解析Excel是解析出一條資料就new一個物件進行封裝資料,然後將bean存放至List。然而有什麼方法能夠解決這一類問題呢?那就是Object 的native clone()方法了,clone()這個大佬是比較牛逼的一個人物,在不建立物件的情況下將屬性值複製給另一個物件,具體實現需要實現Cloneable介面並重寫clone()。而解決這個問題的方式就是在每解析完一行Excel資料的時候,反射呼叫該物件的clone方法。動態解析 具體實現應用了 Apache POI、 LRUCache(LRU快取)、Reflection(反射)、java的Clone等技術 。如果以上技術沒有了解過的朋友可以去自行了解,這裡不加贅述。
前提條件 :因為要實現動態解析,動態設定值,那麼我們在反射執行set操作的時候就需要知道相應的setMethod(),那麼我們可以在Excel規定第一行就是屬性欄位,並且欄位名稱跟bean的名稱一樣,讀取的時候先把第一行的資料放在一個String []陣列中。具體實現請參照以下原始碼。我已經把相關程式碼打包成Jar,需要的朋友可以自行下載; Jar包下載 ofollow,noindex">連結:https://pan.baidu.com/s/1fKCCh54S3ZtHfv66T2pk2w 密碼:nur8
使用方法:新建bean用於儲存Excel資料資訊,每個屬性需要有get、set操作,屬性與Excel首行相同,最重要的一點是要 實現Clonesble介面重寫clone方法 。在new ReadExcelUtil 的時候只需要將物件型別與Excel檔案路徑傳入建構函式即可,然後呼叫 ReadExcelUtil的getObjectList即可得到解析後的所有物件。至於這個物件你可以用任何的物件,你可以換成Teacher、OrderInfo、UserInfo......但是前面提到的:Excel第一行的屬性欄位需要與bean的屬性欄位一致,否則無法呼叫目標方法,具體可參見ReflectionInitValue的方法。 具體實現請參見:文章末尾的Test類測試。
原始碼分析
- 前提條件 :引入Apache POI 的Maven倉庫座標,我這裡使用的是3.25版本的。
1 <!-- https://mvnrepository.com/artifact/org.apache.poi/poi --> 2 <dependency> 3<groupId>org.apache.poi</groupId> 4<artifactId>poi</artifactId> 5<version>3.15</version> 6 </dependency> 7 <dependency> 8<groupId>org.apache.poi</groupId> 9<artifactId>poi-ooxml</artifactId> 10<version>3.15</version> 11 </dependency>
- 主要類 :Common.java、 LRUCache.java 、LRUCacheException.java、ResolveFileException.java、 ReadExcelUtil.java、ReflectionInitValue.java、 Student、Test
- Common.java:基礎常量池,主要用於反射執行Method方法時判斷Method的引數型別的常量。
1 package com.hdbs.common; 2 3 /** 4* @author :cnblogs-WindsJune 5* @version :1.1.0 6* @date :2018年9月20日 下午6:33:54 7* @comments :解析Excel公共類常量類 8*/ 9 10 public class Common { 11 12public static final String OFFICE_EXCEL_2003_POSTFIX_xls = "xls"; 13public static final String OFFICE_EXCEL_2010_POSTFIX_xlsx = "xlsx"; 14public static final String DATA_TYPE_long ="long"; 15public static final String DATA_TYPE_boolean ="boolean"; 16public static final String DATA_TYPE_int ="int"; 17public static final String DATA_TYPE_float ="float"; 18public static final String DATA_TYPE_double ="double"; 19public static final String DATA_TYPE_Long ="class java.lang.Long"; 20public static final String DATA_TYPE_Integer ="class java.lang.Integer"; 21 22 23 }
- LRUCacheException.java;LRU快取自定義異常類。
1 package com.hdbs.exceptions; 2 3 /** 4* Creater: cnblogs-WindsJune 5* Date: 2018/9/21 6* Time: 10:04 7* Description: No Description 8*/ 9 public class LRUCacheException extendsException{ 10/** 11* 錯誤碼 12*/ 13private String errorCode; 14 15/** 16* 錯誤描述 17*/ 18private String errorMessage; 19 20public LRUCacheException(String errorCode, String errorMessage) { 21this.errorCode = errorCode; 22this.errorMessage = errorMessage; 23} 24 25public LRUCacheException(String message) { 26super(message); 27this.errorMessage = errorMessage; 28} 29 30public String getErrorCode() { 31return errorCode; 32} 33 34public void setErrorCode(String errorCode) { 35this.errorCode = errorCode; 36} 37 38public String getErrorMessage() { 39return errorMessage; 40} 41 42public void setErrorMessage(String errorMessage) { 43this.errorMessage = errorMessage; 44} 45 }
- ResolveFileException.java;解析Excel自定義異常類。
1 package com.hdbs.exceptions; 2 /** 3* Creater: cnblogs-WindsJune 4* Date: 2018/9/20 5* Time: 19:44 6* Description: 解析Excel的公共異常類 7*/ 8 9 public class ResolveFileException extends RuntimeException{ 10 11/** 12* 錯誤碼 13*/ 14private String errorCode; 15 16/** 17* 錯誤描述 18*/ 19private String errorMessage; 20 21public ResolveFileException(String errorCode, String errorMessage) { 22this.errorCode = errorCode; 23this.errorMessage = errorMessage; 24} 25 26public ResolveFileException(String message) { 27super(message); 28this.errorMessage = errorMessage; 29} 30 31public String getErrorCode() { 32return errorCode; 33} 34 35public void setErrorCode(String errorCode) { 36this.errorCode = errorCode; 37} 38 39public String getErrorMessage() { 40return errorMessage; 41} 42 43public void setErrorMessage(String errorMessage) { 44this.errorMessage = errorMessage; 45} 46 }
- LRUCache.java:LRU快取池,主要用於不同執行緒反射獲取的Methods,減少相同執行緒反射執行次數,減輕應用的負載、提高執行效率。我這裡是基於LinkedHashMap實現的LRU快取,你也可以用陣列或者其他方式實現該演算法。以下程式碼邏輯如果不能理解的可以先去了解LinkedHashSet的原始碼。
1 package com.hdbs.common; 2 3 import com.hdbs.exceptions.LRUCacheException; 4 import org.slf4j.Logger; 5 import org.slf4j.LoggerFactory; 6 7 import java.lang.reflect.Method; 8 import java.util.LinkedHashMap; 9 import java.util.Map; 10 11 /** 12* Creater: cnblogs-WindsJune 13* Date: 2018/9/20 14* Time: 19:44 15* Description: LinkedHashMap實現LRU快取不同執行緒反射獲取的Method方法 16*/ 17 public class LRUCache { 18privatestaticfinal Logger LOGGER=LoggerFactory.getLogger(LRUCache.class); 19//快取容量 20private static final int cacheSize = 10; 21 22private static final Map<Integer,Method[]> cacheMap = new LinkedHashMap<Integer, Method[]>((int) Math.ceil(cacheSize / 0.75f) + 1, 0.75f, true){ 23@Override 24protected boolean removeEldestEntry(Map.Entry<Integer,Method[]> eldest){ 25 26returnsize()> cacheSize; 27 28} 29}; 30 31/** 32* 設定快取 33* @param key 34* @param methods 35* @return boolean 36*/ 37public static boolean set (Integer key,Method [] methods) throws LRUCacheException { 38try { 39cacheMap.put(key,methods); 40return true; 41} 42catch ( Exception e ){ 43throw new LRUCacheException("Set LRU快取異常!"); 44} 45} 46 47/** 48* 獲取快取的Method 49* @param key 50* @return Method 51*/ 52public static Method[] get(Integer key) throws LRUCacheException { 53Method[] methods=null; 54try { 55methods=cacheMap.get(key); 56}catch ( Exception e ){ 57throw new LRUCacheException("Get LRU快取異常!{}"); 58} 59return methods; 60} 61 }
- ReadExcelUtil.java;解析Excel資料工具類(將Excel載入到記憶體)
1 package com.hdbs.resolver; 2 3 import com.hdbs.common.Common; 4 import com.hdbs.exceptions.ResolveFileException; 5 import org.apache.commons.lang3.StringUtils; 6 import org.apache.poi.hssf.usermodel.HSSFCell; 7 import org.apache.poi.hssf.usermodel.HSSFRow; 8 import org.apache.poi.hssf.usermodel.HSSFSheet; 9 import org.apache.poi.hssf.usermodel.HSSFWorkbook; 10 import org.apache.poi.xssf.usermodel.XSSFCell; 11 import org.apache.poi.xssf.usermodel.XSSFRow; 12 import org.apache.poi.xssf.usermodel.XSSFSheet; 13 import org.apache.poi.xssf.usermodel.XSSFWorkbook; 14 import org.slf4j.Logger; 15 import org.slf4j.LoggerFactory; 16 17 import java.io.File; 18 import java.io.FileInputStream; 19 import java.io.IOException; 20 import java.io.InputStream; 21 import java.lang.reflect.InvocationTargetException; 22 import java.util.ArrayList; 23 import java.util.HashMap; 24 import java.util.List; 25 import java.util.Map; 26 27 /** 28* @author :cnblogs-WindsJune 29* @version :1.1.0 30* @date :2018年9月20日 下午6:13:43 31* @comments : 32*/ 33 34 public class ReadExcelUtil { 35 36private static final Logger LOGGER = LoggerFactory.getLogger(ReadExcelUtil.class); 37 38privateMap<Integer,String []> fieldsMap=new HashMap<>(); 39 40private List<Object> objectsList = new ArrayList<>(); 41 42private Object object=null; 43 44privateString path =null; 45 46public List<Object> getObjectsList() { 47return this.objectsList; 48} 49 50public ReadExcelUtil(Object object,String path) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException { 51this.object=object; 52this.path=path; 53readExcel(); 54} 55 56/** 57* 新增Object到List中 58* @param object 59* @return 60*/ 61public boolean addListObject(Object object){ 62boolean isSucceed=this.objectsList.add(object); 63returnisSucceed; 64} 65 66/** 67* 讀取excel,判斷是xls結尾(2010之前);還是xlsx結尾(2010以後)的Excel 68* 69* @return 70* @throws IOException 71*/ 72public boolean readExcel() throws IOException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { 73if (StringUtils.isEmpty(path)) { 74return false; 75} else { 76// 擷取字尾名,判斷是xls還是xlsx 77String postfix = path.substring(path.lastIndexOf(".") + 1); 78if (!StringUtils.isEmpty(postfix)) { 79if (Common.OFFICE_EXCEL_2003_POSTFIX_xls.equals(postfix)) { 80return readXls(); 81} else if (Common.OFFICE_EXCEL_2010_POSTFIX_xlsx.equals(postfix)) { 82return readXlsx(); 83} 84} else { 85LOGGER.error("檔案字尾名有誤!"); 86throw new ResolveFileException("檔案字尾名有誤!" + "[" + path + "]"); 87} 88} 89return false; 90} 91 92/** 93* 讀取xls(2010)之後的Excel 94* 95* @return 96* @throws IOException 97*/ 98publicboolean readXlsx() throws IOException{ 99File file = new File(path); 100InputStream is = new FileInputStream(file); 101XSSFWorkbook xssfWorkbook = new XSSFWorkbook(is); 102// 遍歷sheet頁 103for (int numSheet = 0; numSheet < xssfWorkbook.getNumberOfSheets(); numSheet++) { 104XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(numSheet); 105String [] fields=null; 106if (xssfSheet == null) { 107continue; 108} 109// 迴圈行 110for (int rowNum = 0; rowNum <= xssfSheet.getLastRowNum(); rowNum++) { 111XSSFRow xssfRow = xssfSheet.getRow(rowNum); 112int cloumns=xssfRow.getLastCellNum(); 113int i=0; 114//獲取第一行的所有屬性 115if (rowNum == 0){ 116fields=getFields(xssfRow,cloumns); 117fieldsMap.put(numSheet,fields); 118continue; 119} 120//遍歷資料,反射set值 121while (i<cloumns){ 122XSSFCell field=xssfRow.getCell(i); 123String value=getValue(field); 124try { 125ReflectionInitValue.setValue(object,fields[i],value); 126}catch ( Exception e ){ 127throw new ResolveFileException(e.getMessage()); 128} 129i++; 130} 131//通過反射執行clone複製物件 132Object result=ReflectionInitValue.invokeClone(object,"clone"); 133this.addListObject(result); 134// System.out.println(object.toString()); 135} 136} 137return true; 138} 139 140/** 141* 讀取xls(2010)之前的Excel 142* 143* @return 144* @throws IOException 145*/ 146public boolean readXls() throws IOException, ResolveFileException { 147InputStream is = new FileInputStream(path); 148HSSFWorkbook hssfWorkbook = new HSSFWorkbook(is); 149// 遍歷sheet頁 150for (int numSheet = 0; numSheet < hssfWorkbook.getNumberOfSheets(); numSheet++) { 151HSSFSheet hssfSheet = hssfWorkbook.getSheetAt(numSheet); 152String[] fields = null; 153if (hssfSheet == null) { 154continue; 155} 156// 迴圈行Row 157for (int rowNum = 0; rowNum <= hssfSheet.getLastRowNum(); rowNum++) { 158HSSFRow hssfRow = hssfSheet.getRow(rowNum); 159int cloumns=hssfRow.getLastCellNum(); 160int i=0; 161//獲取第一行的所有屬性 162if (rowNum == 0){ 163//獲取屬性欄位 164fields=getFields(hssfRow,cloumns); 165fieldsMap.put(numSheet,fields); 166continue; 167} 168//遍歷資料,反射set值 169while (i<cloumns){ 170HSSFCell field=hssfRow.getCell(i); 171String value=getValue(field); 172try { 173ReflectionInitValue.setValue(object,fields[i],value); 174}catch ( Exception e ){ 175thrownew ResolveFileException(e.getMessage()); 176} 177i++; 178} 179//通過反射執行clone複製物件 180Object result=ReflectionInitValue.invokeClone(object,"clone"); 181this.addListObject(result); 182} 183} 184return true; 185} 186 187/** 188* xlsx -根據資料型別,獲取單元格的值 189* @param xssfRow 190* @return 191*/ 192@SuppressWarnings({ "static-access" }) 193private static String getValue(XSSFCell xssfRow) { 194String value=null; 195try { 196if (xssfRow.getCellType() == xssfRow.CELL_TYPE_BOOLEAN) { 197// 返回布林型別的值 198value=String.valueOf(xssfRow.getBooleanCellValue()).replace(" ",""); 199} else if (xssfRow.getCellType() == xssfRow.CELL_TYPE_NUMERIC) { 200// 返回數值型別的值 201value= String.valueOf(xssfRow.getNumericCellValue()).replace(" ",""); 202} else { 203// 返回字串型別的值 204value= String.valueOf(xssfRow.getStringCellValue()).replace(" ",""); 205} 206} catch (Exception e) { 207//單元格為空,不處理 208value=null; 209LOGGER.error("單元格為空!"); 210} 211return value; 212} 213 214/** 215* xls-根據資料型別,獲取單元格的值 216* @param hssfCell 217* @return 218*/ 219@SuppressWarnings({ "static-access" }) 220private static String getValue(HSSFCell hssfCell) { 221String value=null; 222try { 223if (hssfCell.getCellType() == hssfCell.CELL_TYPE_BOOLEAN) { 224// 返回布林型別的值 225value=String.valueOf(hssfCell.getBooleanCellValue()).replaceAll(" ",""); 226} else if (hssfCell.getCellType() == hssfCell.CELL_TYPE_NUMERIC) { 227// 返回數值型別的值 228value=String.valueOf(hssfCell.getNumericCellValue()).replaceAll(" ",""); 229} else { 230// 返回字串型別的值 231value=String.valueOf(hssfCell.getStringCellValue()).replaceAll(" ",""); 232} 233} catch (Exception e) { 234//單元格為空,不處理 235value=null; 236LOGGER.error("單元格為空!"); 237} 238return value; 239} 240 241/** 242* xls Excel檔案型別獲取屬性(2010之前) 243* @param cloumns 244* @return String[] 245*/ 246privatestatic String[] getFields (HSSFRow hssfRow,int cloumns){ 247String [] fields=new String[cloumns]; 248int i=0; 249try { 250while (i<cloumns){ 251HSSFCell field=hssfRow.getCell(i); 252String value=getValue(field); 253fields[i]=value.trim(); 254i++; 255} 256}catch ( Exception e){ 257thrownew ResolveFileException("獲取屬性集失敗!"); 258} 259returnfields; 260} 261 262/** 263* xlsx Excel檔案型別獲取屬性(2010之後) 264* @param cloumns 265* @return String[] 266*/ 267privatestatic String[] getFields(XSSFRow xssfRow,int cloumns){ 268String [] fields=new String[cloumns]; 269int i=0; 270try { 271while (i<cloumns){ 272XSSFCell field=xssfRow.getCell(i); 273String value=getValue(field); 274fields[i]=value.trim(); 275i++; 276} 277}catch ( Exception e ){ 278thrownew ResolveFileException("獲取屬性集失敗!"); 279} 280returnfields; 281} 282 283 }
- ReflectionInitValue.java;
1 package com.hdbs.resolver; 2 3 import com.hdbs.common.Common; 4 import com.hdbs.common.LRUCache; 5 import com.hdbs.exceptions.LRUCacheException; 6 import com.hdbs.exceptions.ResolveFileException; 7 8 import java.lang.reflect.InvocationTargetException; 9 import java.lang.reflect.Method; 10 import java.lang.reflect.Type; 11 12 /** 13* Creater: cnblogs-WindsJune 14* Date: 2018/9/21 15* Time: 9:54 16* Description: No Description 17*/ 18 public class ReflectionInitValue { 19 20private static int threadHashCodeKey=Thread.currentThread().toString().hashCode(); 21 22/** 23* 通過反射動態將Excel讀取的資訊設定到對應的bean中 24* 25* @param object-儲存物件bean 26* @param key-屬性引數名 27* @param value-屬性值 28* @throws Exception 29*/ 30public static void setValue(Object object, String key, String value) throws LRUCacheException { 31String methodName = null; 32String paramType = null; 33Method[] methods = null; 34if (LRUCache.get(threadHashCodeKey) == null) { 35Class<?> clazz = object.getClass(); 36methods = clazz.getDeclaredMethods(); 37LRUCache.set(threadHashCodeKey, methods); 38} else { 39methods = LRUCache.get(threadHashCodeKey); 40} 41for (Method method : methods) { 42methodName = method.getName(); 43if (methodName.startsWith("set") && methodName.toLowerCase().equals("set" + key.toLowerCase())) { 44Type[] types = method.getGenericParameterTypes(); 45for (Type type : types) { 46paramType = type.toString(); 47// 根據引數型別轉化value,並進行set操作 48excuteInvokeSetvalue(object, method, paramType, value, 0); 49} 50// 該屬性已經執行setValue操作,無需迴圈 51break; 52} 53} 54} 55 56/** 57* 初始化物件bean 58* 59* @param object 60* @throws Exception 61*/ 62public static void initBeans(Object object) throws ResolveFileException, LRUCacheException { 63// Class<?> clazz = object.getClass(); 64String methodName = null; 65String paramType = null; 66Method[] methods = LRUCache.get(threadHashCodeKey); 67try { 68for (Method method : methods) { 69methodName = method.getName(); 70if (methodName.startsWith("set")) { 71Type[] types = method.getGenericParameterTypes(); 72for (Type type : types) { 73paramType = type.getClass().getName(); 74} 75// 根據引數型別轉化value,並進行set初始化屬性值 76excuteInvokeSetvalue(object, method, paramType, "", 1); 77} 78} 79} catch (Exception e) { 80throw new ResolveFileException("初始化bean錯誤!Method:[ " + methodName + " ]"); 81} 82} 83 84/** 85* 根據引數型別轉化value,並進行set操作 86* 87* @param object-儲存物件bean 88* @param method-執行的set對應屬性的方法 89* @param paramType-屬性引數型別 90* @param value-屬性值 91* @param operationType-操作型別(0-設定屬性,1-初始化bean) 92* @throws Exception 93*/ 94public static void excuteInvokeSetvalue(Object object, Method method, String paramType, String value, 95int operationType){ 96try { 97switch (paramType) { 98case Common.DATA_TYPE_long: {// 引數屬性long 99if (value !=null && value.contains(".")){ 100value=value.substring(0,value.lastIndexOf(".")); 101} 102Long temp = Long.valueOf(operationType == 0 && value !=null ? value : "0"); 103method.invoke(object, temp); 104break; 105} 106case Common.DATA_TYPE_boolean: {// 引數屬性boolean 107boolean temp = (operationType == 0 ? (Boolean.valueOf(value != null ? value:"false")) : false); 108method.invoke(object, temp); 109break; 110} 111case Common.DATA_TYPE_int: {// 引數屬性int 112if (value !=null && value.contains(".")){ 113value=value.substring(0,value.lastIndexOf(".")); 114} 115int temp = Integer.valueOf(operationType == 0 && value!=null ? value : "0"); 116method.invoke(object, temp); 117break; 118} 119case Common.DATA_TYPE_float: {// 引數屬性float 120if (value !=null && value.contains(".")){ 121value=value.substring(0,value.lastIndexOf(".")); 122} 123float temp = Float.valueOf(operationType == 0 && value !=null ? value : "0"); 124method.invoke(object, temp); 125break; 126} 127case Common.DATA_TYPE_double: {// 引數屬性double 128double temp = Double.valueOf(operationType == 0 && value !=null ? value : "0"); 129method.invoke(object, temp); 130break; 131} 132case Common.DATA_TYPE_Long: {// 引數屬性Long 133if (value !=null && value.contains(".")){ 134value=value.substring(0,value.lastIndexOf(".")); 135} 136Long temp = Long.valueOf(operationType == 0 && value!=null ? value : "0"); 137method.invoke(object, temp); 138break; 139} 140case Common.DATA_TYPE_Integer: {// 引數屬性Integer 141if (value !=null && value.contains(".")){ 142value=value.substring(0,value.lastIndexOf(".")); 143} 144int temp = Integer.valueOf(operationType == 0 && value!=null ? value : "0"); 145method.invoke(object, temp); 146break; 147} 148default: {// 引數屬性String 149if (value !=null && value.contains(".")){ 150value=value.substring(0,value.lastIndexOf(".")); 151} 152method.invoke(object, operationType == 0 ? value : null); 153break; 154} 155} 156 157} catch ( IllegalAccessException e ) { 158throw new ResolveFileException("invoke方法錯誤![Method:" + method.getName() + " [value:" + value + " ]"); 159} catch ( InvocationTargetException e ) { 160throw new ResolveFileException("invoke方法錯誤![Method:" + method.getName() + " [value:" + value + " ]"); 161} catch (Exception e) { 162throw new ResolveFileException("欄位屬性錯誤![Method:" + method.getName() + " [value:" + value + " ]"); 163} 164 165 166} 167 168/** 169* 170* @param object 171* @param methodName 172* @return 173* @throws ResolveFileException 174*/ 175public static Object invokeClone (Object object,String methodName){ 176Class clazz=object.getClass(); 177try { 178Method method=clazz.getMethod(methodName); 179Object result=method.invoke(object); 180returnresult; 181}catch ( Exception e ){ 182throw new ResolveFileException("解析Excel,反射執行set操作異常!"); 183} 184 185} 186 187 188 }
- Student.java;用於儲存資料資訊得bean。
1 package com.hdbs.beans; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 /** 7* @author :WindsJune/部落格園:WindsJune 8* @version :1.1.0 9* @date :2018年9月20日 下午6:05:57 10* @comments : 11*/ 12 13 public class Student implements Cloneable{ 14 15/** 16* id 17*/ 18private Integer id; 19/** 20* 學號 21*/ 22private String no; 23/** 24* 姓名 25*/ 26private String name; 27/** 28* 學院 29*/ 30private String age; 31/** 32* 成績 33*/ 34private float score; 35 36/** 37* 地址 38*/ 39private String adress; 40 41private List<Student> studentsList=new ArrayList<>(); 42 43public Integer getId() { 44return id; 45} 46 47public void setId(Integer id) { 48this.id = id; 49} 50 51public String getNo() { 52return no; 53} 54 55public void setNo(String no) { 56this.no = no; 57} 58 59public String getName() { 60return name; 61} 62 63public void setName(String name) { 64this.name = name; 65} 66 67public String getAge() { 68return age; 69} 70 71public void setAge(String age) { 72this.age = age; 73} 74 75public float getScore() { 76return score; 77} 78 79public void setScore(float score) { 80this.score = score; 81} 82 83public String getAdress() { 84return adress; 85} 86 87public void setAdress(String adress) { 88this.adress = adress; 89} 90 91public List<Student> getStudentsList() { 92return studentsList; 93} 94 95public Object clone() throws CloneNotSupportedException{ 96return super.clone(); 97} 98 99@Override 100public String toString() { 101return "Student{" + 102"id=" + id + 103", no='" + no + '\'' + 104", name='" + name + '\'' + 105", age='" + age + '\'' + 106", score=" + score + 107", adress='" + adress + '\'' + 108'}'; 109} 110 }
- Test.java ;測試方法,在new ReadExcelUtil 的時候只需要將物件型別與Excel檔案路徑傳入建構函式即可,然後呼叫 ReadExcelUtil的getObjectList即可得到解析後的所有物件。至於這個物件你可以用任何的物件,你可以換成Teacher、OrderInfo、UserInfo......但是前面提到的:Excel第一行的屬性欄位需要與bean的屬性欄位一致,否則無法呼叫目標方法,具體可參見ReflectionInitValue的方法。
1 package hello; 2 3 import java.util.List; 4 5 import com.hdbs.beans.Student; 6 import com.hdbs.resolver.ReadExcelUtil; 7 8 /** 9* @version 1.0.0 10* @authorcnblogs-WindsJune 11* @date2018年9月23日 上午1:16:34 12* 13*/ 14 public class Test { 15public static void main(String[] args) { 16Student student=new Student(); 17String filePath="E:/test.xlsx"; 18try { 19ReadExcelUtil readExcelUtil=new ReadExcelUtil(student,filePath); 20List<Object> list=readExcelUtil.getObjectsList(); 21for (Object object:list){ 22Student test=(Student) object; 23System.out.println(test.toString()); 24} 25} catch (Exception e) { 26e.printStackTrace(); 27} 28} 29 }
表格規範:
執行結果: