Java解析Excel之應用Reflection等技術實現動態讀取

 目錄樹

  • 背景
  • 技術選型
  • 問題分析
  • 技術要點及難點分析
  • 源碼分析
  • 測試用例

 


 

背景

Tip:由於產品提的需求我都開發完了,進行了項目提測;前天老大走過來講:你用spring-boot開發一個解析Excel的jar包.....詳細對話以下:前端

A:世生,你用spring-boot開發一個解析Excel的jar包。java

B:爲何不在原來的項目上進行開發呢?(很納悶,這個東西不是通常用於前端上傳數據的嘛,這是後端層,咋搞這個)git

A:由於xxxx這個需求有比較多的數據須要初始化,這個jar是做爲一個上線的數據初始化腳本github

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進行開發。spring

 


 

問題分析

  解析Excel的關鍵點是在於從Excel表格中讀取數據到內存(解析Excel),而後多是校驗數據、經過業務邏輯分析數據、最終持久化到數據庫中;其實這其中最重要的不是解析Excel,而是將解析出的數據持久化到數據庫中以備有用之需。而解析Excel的這塊功能只能算是一個Util類,不能與業務代碼耦合一塊兒;而後我看到不少的Excel解析相關的代碼都是在解析數據中混淆業務代碼邏輯,其實這些都是不可取的,這會致使解析Excel邏輯與業務邏輯相耦合也就是冗雜、代碼重用率低、可擴展性低等問題。由於以前在作項目的時候遇到過一個問題:我負責的模塊是一箇中間模塊(通信採用Dubbo),其餘系統要依賴我這個接口進行請求的轉發可我調用其餘系統返回的結果對象卻各個都不一樣,我叫其餘系統負責人說要統一調用接口返回的對象,可是其餘系統的負責人都不是同一我的執行起來效率過低了,在歷經一個星期都無果的狀況下我只能撒下本身的殺手鐗了;在這種極端條件下最終我無論其餘數據的接口返回的對象是什麼,我直接用Object接收返回類型,經過反射獲取決定請求成功與否的屬性值(欣慰的是當時必傳屬性值卻是同樣的)。經過這種方法我能夠少些不少的代碼(當時其餘系統有15+),否則的話每調用不一樣系統的接口我都須要進行邏輯判斷或者是乾脆對於調用他們不一樣的系統我採用不一樣接口進行轉發,但選用這種方法卻便利多了。數據庫

  以上問題分析及一個場景的描述很好理解,可是經過Object接收返回信息得這個場景事實上卻有所欠佳;返回對象不一樣的這個問題最好的處理方案就是統一接口,我那個方案是在需求推進但別人沒法及時配合的極端條件下使用的,是沒辦法中的辦法,但這也是一個沒有辦法中的一個最優的處理方案,兼容性比較強。如下我就用圖來分析這兩種狀況的比較:apache

  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包Git下載地址:https://github.com/GitHubSuper543/auto-resolver-excel-jar.git,源碼地址:https://github.com/GitHubSuper543/auto-resolver-excel-resource.git

  使用方法:新建bean用於存儲Excel數據信息,每一個屬性須要有get、set操做,屬性與Excel首行相同,最重要的一點是要實現Clonesble接口重寫clone方法Excel使用Office編輯,親測Wps編輯的Excel某些屬性值有問題。在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     
12     public static final String OFFICE_EXCEL_2003_POSTFIX_xls = "xls";
13     public static final String OFFICE_EXCEL_2010_POSTFIX_xlsx = "xlsx";
14     public static final String DATA_TYPE_long ="long";
15     public static final String DATA_TYPE_boolean ="boolean";
16     public static final String DATA_TYPE_int ="int";
17     public static final String DATA_TYPE_float ="float";
18     public static final String DATA_TYPE_double ="double";
19     public static final String DATA_TYPE_Long ="class java.lang.Long";
20     public 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 extends  Exception{
10     /**
11      * 錯誤碼
12      */
13     private String errorCode;
14 
15     /**
16      * 錯誤描述
17      */
18     private String errorMessage;
19 
20     public LRUCacheException(String errorCode, String errorMessage) {
21         this.errorCode = errorCode;
22         this.errorMessage = errorMessage;
23     }
24 
25     public LRUCacheException(String message) {
26         super(message);
27         this.errorMessage = errorMessage;
28     }
29 
30     public String getErrorCode() {
31         return errorCode;
32     }
33 
34     public void setErrorCode(String errorCode) {
35         this.errorCode = errorCode;
36     }
37 
38     public String getErrorMessage() {
39         return errorMessage;
40     }
41 
42     public void setErrorMessage(String errorMessage) {
43         this.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      */
14     private String errorCode;
15 
16     /**
17      * 錯誤描述
18      */
19     private String errorMessage;
20 
21     public ResolveFileException(String errorCode, String errorMessage) {
22         this.errorCode = errorCode;
23         this.errorMessage = errorMessage;
24     }
25 
26     public ResolveFileException(String message) {
27         super(message);
28         this.errorMessage = errorMessage;
29     }
30 
31     public String getErrorCode() {
32         return errorCode;
33     }
34 
35     public void setErrorCode(String errorCode) {
36         this.errorCode = errorCode;
37     }
38 
39     public String getErrorMessage() {
40         return errorMessage;
41     }
42 
43     public void setErrorMessage(String errorMessage) {
44         this.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 {
18     private  static  final Logger LOGGER=LoggerFactory.getLogger(LRUCache.class);
19     //緩存容量
20     private static final int cacheSize = 10;
21 
22     private static final Map<Integer,Method[]> cacheMap = new LinkedHashMap<Integer, Method[]>((int) Math.ceil(cacheSize / 0.75f) + 1, 0.75f, true){
23         @Override
24         protected boolean removeEldestEntry(Map.Entry<Integer,Method[]> eldest){
25 
26             return  size()> cacheSize;
27             
28         }
29     };
30 
31     /**
32      * 設置緩存
33      * @param key
34      * @param methods
35      * @return boolean
36      */
37     public static boolean set (Integer key,Method [] methods) throws LRUCacheException {
38         try {
39                cacheMap.put(key,methods);
40                return true;
41         }
42         catch ( Exception e ){
43               throw new LRUCacheException("Set LRU緩存異常!");
44         }
45     }
46 
47     /**
48      * 獲取緩存的Method
49      * @param key
50      * @return Method
51      */
52     public static Method[] get(Integer key) throws LRUCacheException {
53         Method[] methods=null;
54         try {
55             methods=cacheMap.get(key);
56         }catch ( Exception e ){
57                throw new LRUCacheException("Get LRU緩存異常!{}");
58         }
59         return 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 
 36     private static final Logger LOGGER = LoggerFactory.getLogger(ReadExcelUtil.class);
 37   //存放屬性集
 38     private  Map<Integer,String []> fieldsMap=new HashMap<>();
 39   //存放解析後的對象List
 40     private List<Object> objectsList = new ArrayList<>();
 41   //反射運行時對象
 42     private Object object=null;
 43   //Excel文件路徑
 44     private  String path =null;
 45   //獲取解析後的對象集
 46     public List<Object> getObjectsList() {
 47         return this.objectsList;
 48     }
 49 
 50     public ReadExcelUtil(Object object,String path) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException {
 51         this.object=object;
 52         this.path=path;
 53         readExcel();
 54     }
 55 
 56     /**
 57      * 添加Object到List中
 58      * @param object
 59      * @return
 60      */
 61     public boolean addListObject(Object object){
 62         boolean isSucceed=this.objectsList.add(object);
 63         return  isSucceed;
 64     }
 65 
 66     /**
 67      * 讀取excel,判斷是xls結尾(2010以前);仍是xlsx結尾(2010之後)的Excel
 68      * 
 69      * @return
 70      * @throws IOException
 71      */
 72     public boolean readExcel() throws IOException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
 73         if (StringUtils.isEmpty(path)) {
 74             return false;
 75         } else {
 76             // 截取後綴名,判斷是xls仍是xlsx
 77             String postfix = path.substring(path.lastIndexOf(".") + 1);
 78             if (!StringUtils.isEmpty(postfix)) {
 79                 if (Common.OFFICE_EXCEL_2003_POSTFIX_xls.equals(postfix)) {
 80                     return readXls();
 81                 } else if (Common.OFFICE_EXCEL_2010_POSTFIX_xlsx.equals(postfix)) {
 82                     return readXlsx();
 83                 }
 84             } else {
 85                 LOGGER.error("文件後綴名有誤!");
 86                 throw new ResolveFileException("文件後綴名有誤!" + "[" + path + "]");
 87             }
 88         }
 89         return false;
 90     }
 91 
 92     /**
 93      * 讀取xls(2010)以後的Excel
 94      * 
 95      * @return
 96      * @throws IOException
 97      */
 98     public  boolean readXlsx() throws IOException{
 99         File file = new File(path);
100         InputStream is = new FileInputStream(file);
101         XSSFWorkbook xssfWorkbook = new XSSFWorkbook(is);
102         // 遍歷sheet頁
103         for (int numSheet = 0; numSheet < xssfWorkbook.getNumberOfSheets(); numSheet++) {
104             XSSFSheet xssfSheet = xssfWorkbook.getSheetAt(numSheet);
105             String [] fields=null;
106             if (xssfSheet == null) {
107                 continue;
108             }
109             // 循環行
110             for (int rowNum = 0; rowNum <= xssfSheet.getLastRowNum(); rowNum++) {
111                 XSSFRow xssfRow = xssfSheet.getRow(rowNum);
112                 int cloumns=xssfRow.getLastCellNum();
113                 int i=0;
114                 //獲取第一行的全部屬性
115                 if (rowNum == 0){
116                     fields=getFields(xssfRow,cloumns);
117                     fieldsMap.put(numSheet,fields);
118                     continue;
119                 }
120                 //遍歷數據,反射set值
121                 while (i<cloumns){
122                     XSSFCell field=xssfRow.getCell(i);
123                     String value=getValue(field);
124                     try {
125                         ReflectionInitValue.setValue(object,fields[i],value);
126                     }catch ( Exception e ){
127                         throw new ResolveFileException(e.getMessage());
128                     }
129                     i++;
130                 }
131                 //經過反射執行clone複製對象
132                 Object result=ReflectionInitValue.invokeClone(object,"clone");
133                 this.addListObject(result);
134                // System.out.println(object.toString());
135             }
136         }
137         return true;
138     }
139 
140     /**
141      * 讀取xls(2010)以前的Excel
142      * 
143      * @return
144      * @throws IOException
145      */
146     public boolean readXls() throws IOException, ResolveFileException {
147         InputStream is = new FileInputStream(path);
148         HSSFWorkbook hssfWorkbook = new HSSFWorkbook(is);
149         // 遍歷sheet頁
150         for (int numSheet = 0; numSheet < hssfWorkbook.getNumberOfSheets(); numSheet++) {
151             HSSFSheet hssfSheet = hssfWorkbook.getSheetAt(numSheet);
152             String[] fields = null;
153             if (hssfSheet == null) {
154                 continue;
155             }
156             // 循環行Row
157             for (int rowNum = 0; rowNum <= hssfSheet.getLastRowNum(); rowNum++) {
158                 HSSFRow hssfRow = hssfSheet.getRow(rowNum);
159                 int cloumns=hssfRow.getLastCellNum();
160                 int i=0;
161                 //獲取第一行的全部屬性
162                 if (rowNum == 0){
163                     //獲取屬性字段
164                     fields=getFields(hssfRow,cloumns);
165                     fieldsMap.put(numSheet,fields);
166                     continue;
167                 }
168                 //遍歷數據,反射set值
169                 while (i<cloumns){
170                     HSSFCell field=hssfRow.getCell(i);
171                     String value=getValue(field);
172                     try {
173                         ReflectionInitValue.setValue(object,fields[i],value);
174                     }catch ( Exception e ){
175                         throw  new ResolveFileException(e.getMessage());
176                     }
177                     i++;
178                 }
179                 //經過反射執行clone複製對象
180                 Object result=ReflectionInitValue.invokeClone(object,"clone");
181                 this.addListObject(result);
182             }
183         }
184         return true;
185     }
186 
187     /**
188      * xlsx -根據數據類型,獲取單元格的值
189      * @param xssfRow
190      * @return
191      */
192     @SuppressWarnings({ "static-access" })
193     private static String getValue(XSSFCell xssfRow) {
194         String value=null;
195         try {
196             if (xssfRow.getCellType() == xssfRow.CELL_TYPE_BOOLEAN) {
197                 // 返回布爾類型的值
198                 value=String.valueOf(xssfRow.getBooleanCellValue()).replace(" ","");
199             } else if (xssfRow.getCellType() == xssfRow.CELL_TYPE_NUMERIC) {
200                 // 返回數值類型的值
201                 value= String.valueOf(xssfRow.getNumericCellValue()).replace(" ","");
202             } else {
203                 // 返回字符串類型的值
204                 value= String.valueOf(xssfRow.getStringCellValue()).replace(" ","");
205             }
206         } catch (Exception e) {
207             //單元格爲空,不處理
208             value=null;
209             LOGGER.error("單元格爲空!");
210         }
211         return value;
212     }
213 
214     /**
215      * xls-根據數據類型,獲取單元格的值
216      * @param hssfCell
217      * @return
218      */
219     @SuppressWarnings({ "static-access" })
220     private static String getValue(HSSFCell hssfCell) {
221         String value=null;
222         try {
223             if (hssfCell.getCellType() == hssfCell.CELL_TYPE_BOOLEAN) {
224                 // 返回布爾類型的值
225                 value=String.valueOf(hssfCell.getBooleanCellValue()).replaceAll(" ","");
226             } else if (hssfCell.getCellType() == hssfCell.CELL_TYPE_NUMERIC) {
227                 // 返回數值類型的值
228                 value=String.valueOf(hssfCell.getNumericCellValue()).replaceAll(" ","");
229             } else {
230                 // 返回字符串類型的值
231                 value=String.valueOf(hssfCell.getStringCellValue()).replaceAll(" ","");
232             }
233         } catch (Exception e) {
234             //單元格爲空,不處理
235             value=null;
236             LOGGER.error("單元格爲空!");
237         }
238         return value;
239     }
240 
241     /**
242      * xls Excel文件類型獲取屬性(2010以前)
243      * @param cloumns
244      * @return String[]
245      */
246     private  static String[] getFields (HSSFRow hssfRow,int cloumns){
247         String [] fields=new String[cloumns];
248         int i=0;
249         try {
250             while (i<cloumns){
251                 HSSFCell field=hssfRow.getCell(i);
252                 String value=getValue(field);
253                 fields[i]=value.trim();
254                 i++;
255             }
256         }catch ( Exception e){
257             throw  new ResolveFileException("獲取屬性集失敗!");
258         }
259         return  fields;
260     }
261 
262     /**
263      * xlsx Excel文件類型獲取屬性(2010以後)
264      * @param cloumns
265      * @return String[]
266      */
267     private  static String[] getFields(XSSFRow xssfRow,int cloumns){
268         String [] fields=new String[cloumns];
269         int i=0;
270         try {
271             while (i<cloumns){
272                 XSSFCell field=xssfRow.getCell(i);
273                 String value=getValue(field);
274                 fields[i]=value.trim();
275                 i++;
276             }
277         }catch ( Exception e ){
278             throw  new ResolveFileException("獲取屬性集失敗!");
279         }
280         return  fields;
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 
 20     private 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      */
 30     public static void setValue(Object object, String key, String value) throws LRUCacheException {
 31         String methodName = null;
 32         String paramType = null;
 33         Method[] methods = null;
 34         if (LRUCache.get(threadHashCodeKey) == null) {
 35             Class<?> clazz = object.getClass();
 36             methods = clazz.getDeclaredMethods();
 37             LRUCache.set(threadHashCodeKey, methods);
 38         } else {
 39             methods = LRUCache.get(threadHashCodeKey);
 40         }
 41         for (Method method : methods) {
 42             methodName = method.getName();
 43             if (methodName.startsWith("set") && methodName.toLowerCase().equals("set" + key.toLowerCase())) {
 44                 Type[] types = method.getGenericParameterTypes();
 45                 for (Type type : types) {
 46                     paramType = type.toString();
 47                     // 根據參數類型轉化value,並進行set操做
 48                     excuteInvokeSetvalue(object, method, paramType, value, 0);
 49                 }
 50                 // 該屬性已經執行setValue操做,無需循環
 51                 break;
 52             }
 53         }
 54     }
 55 
 56     /**
 57      * 初始化對象bean
 58      *
 59      * @param object
 60      * @throws Exception
 61      */
 62     public static void initBeans(Object object) throws ResolveFileException, LRUCacheException {
 63         // Class<?> clazz = object.getClass();
 64         String methodName = null;
 65         String paramType = null;
 66         Method[] methods = LRUCache.get(threadHashCodeKey);
 67         try {
 68             for (Method method : methods) {
 69                 methodName = method.getName();
 70                 if (methodName.startsWith("set")) {
 71                     Type[] types = method.getGenericParameterTypes();
 72                     for (Type type : types) {
 73                         paramType = type.getClass().getName();
 74                     }
 75                     // 根據參數類型轉化value,並進行set初始化屬性值
 76                     excuteInvokeSetvalue(object, method, paramType, "", 1);
 77                 }
 78             }
 79         } catch (Exception e) {
 80             throw 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      */
 94     public static void excuteInvokeSetvalue(Object object, Method method, String paramType, String value,
 95                                              int operationType){
 96         try {
 97             switch (paramType) {
 98                 case Common.DATA_TYPE_long: {// 參數屬性long
 99                     if (value !=null && value.contains(".")){
100                         value=value.substring(0,value.lastIndexOf("."));
101                     }
102                     Long temp = Long.valueOf(operationType == 0 && value !=null ? value : "0");
103                     method.invoke(object, temp);
104                     break;
105                 }
106                 case Common.DATA_TYPE_boolean: {// 參數屬性boolean
107                     boolean temp = (operationType == 0 ? (Boolean.valueOf(value != null ? value:"false")) : false);
108                     method.invoke(object, temp);
109                     break;
110                 }
111                 case Common.DATA_TYPE_int: {// 參數屬性int
112                     if (value !=null && value.contains(".")){
113                         value=value.substring(0,value.lastIndexOf("."));
114                     }
115                     int temp = Integer.valueOf(operationType == 0 && value!=null ? value : "0");
116                     method.invoke(object, temp);
117                     break;
118                 }
119                 case Common.DATA_TYPE_float: {// 參數屬性float
120                     if (value !=null && value.contains(".")){
121                         value=value.substring(0,value.lastIndexOf("."));
122                     }
123                     float temp = Float.valueOf(operationType == 0 && value !=null ? value : "0");
124                     method.invoke(object, temp);
125                     break;
126                 }
127                 case Common.DATA_TYPE_double: {// 參數屬性double
128                     double temp = Double.valueOf(operationType == 0 && value !=null ? value : "0");
129                     method.invoke(object, temp);
130                     break;
131                 }
132                 case Common.DATA_TYPE_Long: {// 參數屬性Long
133                     if (value !=null && value.contains(".")){
134                         value=value.substring(0,value.lastIndexOf("."));
135                     }
136                     Long temp = Long.valueOf(operationType == 0 && value!=null ? value : "0");
137                     method.invoke(object, temp);
138                     break;
139                 }
140                 case Common.DATA_TYPE_Integer: {// 參數屬性Integer
141                     if (value !=null && value.contains(".")){
142                         value=value.substring(0,value.lastIndexOf("."));
143                     }
144                     int temp = Integer.valueOf(operationType == 0 && value!=null ? value : "0");
145                     method.invoke(object, temp);
146                     break;
147                 }
148                 default: {// 參數屬性String
149                     if (value !=null && value.contains(".")){
150                         value=value.substring(0,value.lastIndexOf("."));
151                     }
152                     method.invoke(object, operationType == 0 ? value : null);
153                     break;
154                 }
155             }
156 
157         } catch ( IllegalAccessException e ) {
158             throw new ResolveFileException("invoke方法錯誤![Method:" + method.getName() + " [value:" + value + " ]");
159         } catch ( InvocationTargetException e ) {
160             throw new ResolveFileException("invoke方法錯誤![Method:" + method.getName() + " [value:" + value + " ]");
161         } catch (Exception e) {
162             throw 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      */
175     public static Object invokeClone (Object object,String methodName){
176         Class clazz=object.getClass();
177         try {
178             Method method=clazz.getMethod(methodName);
179             Object result=method.invoke(object);
180             return  result;
181         }catch ( Exception e ){
182             throw 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      */
 18     private Integer id;
 19     /**
 20      * 學號
 21      */
 22     private String no;
 23     /**
 24      * 姓名
 25      */
 26     private String name;
 27     /**
 28      * 學院
 29      */
 30     private String age;
 31     /**
 32      * 成績
 33      */
 34     private float score;
 35 
 36     /**
 37      * 地址
 38      */
 39     private String adress;
 40 
 41     private List<Student> studentsList=new ArrayList<>();
 42 
 43     public Integer getId() {
 44         return id;
 45     }
 46 
 47     public void setId(Integer id) {
 48         this.id = id;
 49     }
 50 
 51     public String getNo() {
 52         return no;
 53     }
 54 
 55     public void setNo(String no) {
 56         this.no = no;
 57     }
 58 
 59     public String getName() {
 60         return name;
 61     }
 62 
 63     public void setName(String name) {
 64         this.name = name;
 65     }
 66 
 67     public String getAge() {
 68         return age;
 69     }
 70 
 71     public void setAge(String age) {
 72         this.age = age;
 73     }
 74 
 75     public float getScore() {
 76         return score;
 77     }
 78 
 79     public void setScore(float score) {
 80         this.score = score;
 81     }
 82 
 83     public String getAdress() {
 84         return adress;
 85     }
 86 
 87     public void setAdress(String adress) {
 88         this.adress = adress;
 89     }
 90 
 91     public List<Student> getStudentsList() {
 92         return studentsList;
 93     }
 94 
 95     public Object clone() throws CloneNotSupportedException{
 96         return super.clone();
 97     }
 98 
 99     @Override
100     public String toString() {
101         return "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  * @author  cnblogs-WindsJune
11  * @date    2018年9月23日 上午1:16:34
12  * 
13  */
14 public class Test {
15     public static void main(String[] args) {
16         Student student=new Student();
17         String filePath="E:/test.xlsx";
18         try {
19             ReadExcelUtil readExcelUtil=new ReadExcelUtil(student,filePath);
20             List<Object> list=readExcelUtil.getObjectsList();
21             for (Object object:list){
22                 Student test=(Student) object;
23                 System.out.println(test.toString());
24             }
25         } catch (Exception e) {
26             e.printStackTrace();
27         }
28     }
29 }

 

表格規範:

 

執行結果:

 

相關文章
相關標籤/搜索