Java高級(jí)特性-注解:注解實(shí)現(xiàn)Excel導(dǎo)出功能
注解是 Java 的一個(gè)高級(jí)特性,Spring 更是以注解為基礎(chǔ),發(fā)展出一套“注解驅(qū)動(dòng)編程”。
這聽起來高大上,但畢竟是框架的事,我們也能用好注解嗎?
的確,我們很少有機(jī)會(huì)自己寫注解,導(dǎo)致我們搞不清楚注解是怎么回事,更別提用好注解了。
既然這樣,我們就從具體的工作出發(fā),開發(fā)一個(gè) Excel 導(dǎo)出功能。我相信,你在搞懂這個(gè)例子后,就能明白注解是怎么個(gè)用法。
Excel 導(dǎo)出-需求拆解
在后臺(tái)管理系統(tǒng)中,常常需要把數(shù)據(jù)導(dǎo)出 Excel 表。
比如,在雙十一過后,銷售部要把商品訂單錄入到 Excel 表,財(cái)務(wù)部要把支付訂單錄入到 Excel 表,然后各部門匯總分析,最后找個(gè)時(shí)間討論怎么改善公司的服務(wù)。
你想呀,雙十一的訂單成千上萬,靠人工錄入,少說也要花三四天,而且還特別容易出錯(cuò)。所以,你必須開發(fā) Excel 導(dǎo)出功能。
那么,具體怎么做呢?
上次我們提到,注解想發(fā)揮作用,有三個(gè)要素:定義、使用、讀取。這次,我們就利用注解的三個(gè)特性,來實(shí)現(xiàn) Excel 導(dǎo)出功能,設(shè)計(jì)過程是這樣的。
第一步,我們要?jiǎng)?chuàng)建不同的 Excel 模型。雙十一過后,銷售部要訂單數(shù)據(jù),財(cái)務(wù)部要支付數(shù)據(jù),兩個(gè)部門要的 Excel 表肯定也不一樣,這就得幫每個(gè)部門創(chuàng)建不同的 Excel 模型,他們拿到想要的數(shù)據(jù)。
第二步,我們要根據(jù) Excel 模型,來導(dǎo)出 Excel 表。
看到這,你應(yīng)該明白 Excel 導(dǎo)出的設(shè)計(jì)過程了。接下來,我們就來一步步實(shí)現(xiàn)這個(gè)功能。
創(chuàng)建 Excel 模型
創(chuàng)建 Excel 模型,涉及到注解三要素中的定義、使用。
首先,定義 Excel 注解,我們直接看關(guān)鍵代碼。
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public?@interface?ExcelField?{
????/**
?????*?導(dǎo)出字段標(biāo)題
?????*/
????String?title();
????
????/**
?????*?導(dǎo)出字段排序(升序)
?????*/
????int?sort()?default?0;
????
????/**
?????*?對(duì)齊方式(0:自動(dòng);1:靠左;2:居中;3:靠右)
?????*/
????int?align()?default?0;????
}
這里用到了兩個(gè)元注解@Retention和@Target。@Target代表這個(gè)注解只能放在成員變量上;@Retention代表這個(gè)注解要加載到 JVM 內(nèi)存,我們可以用反射來讀取注解。
此外,注解還有 3 個(gè)成員變量,分別對(duì)應(yīng):Excel 的字段標(biāo)題、字段排序、對(duì)齊方式,方便大家微調(diào)表格。到了這,定義 Excel 注解就完成了。
接下來,使用注解,我們還是直接看代碼。
public?class?OrderModel?{
????@ExcelField(title?=?"訂單號(hào)",?align?=?2,?sort?=?20)
????private?String?orderNo;
????@ExcelField(title?=?"金額",?align?=?2,?sort?=?20)
????private?String?amount;
????
????//?創(chuàng)建時(shí)間
????private?Date?createTime;
????
????//?省略?getter/setter?方法
}
訂單模型有 3 個(gè)字段:訂單號(hào)、金額、創(chuàng)建時(shí)間,但這里注解只加到訂單號(hào)、金額上,表示這兩個(gè)字段會(huì)導(dǎo)出 Excel 表,而創(chuàng)建時(shí)間會(huì)忽略,你可以看看這副圖片。

至此,我們完成了定義注解、使用注解,得到了一個(gè) Excel 模型。但要想實(shí)現(xiàn)導(dǎo)出功能,還必須根據(jù)這個(gè)模型,生成出 Excel 表。
讀取 Excel 模型
讀取 Excel 模型,涉及到注解三要素中的讀取。?我們需要讀取注解,生成 Excel 表,這主要分成 3 個(gè)步驟:初始化 Excel 表對(duì)象—>寫入數(shù)據(jù)到 Excel 表對(duì)象—>輸出文件。
第一步,初始化 Excel 表對(duì)象。在這一步中,我們要根據(jù) Excel 模型,生成一個(gè) Excel 表對(duì)象,要?jiǎng)?chuàng)建這幾個(gè)東西:標(biāo)題、表頭、樣式等等。我們來看代碼。
public?class?ExcelExporter?{
????//?...省略無數(shù)代碼
????/*****************************?初始化?Excel?表對(duì)象?****************************/
????/**
?????*?構(gòu)造函數(shù)
?????*?@param?title?表格標(biāo)題,傳“空值”,表示無標(biāo)題
?????*?@param?cls???excel模型對(duì)象
?????*/
????public?ExcelExporter(String?title,?Class>?cls)?{
????????//?獲取注解list
????????Field[]?fs?=?cls.getDeclaredFields();
????????for?(Field?f?:?fs)?{
????????????ExcelField?ef?=?f.getAnnotation(ExcelField.class);
????????????if?(ef?!=?null)?{
????????????????annotationList.add(new?Object[]{ef,?f});
????????????}
????????}
????????annotationList.sort(comparing(o?->?((ExcelField)?o[0]).sort()));
????????//?通過注解獲取表頭
????????List?headerList?=?new?ArrayList<>();
????????for?(Object[]?os?:?annotationList)?{
????????????String?t?=?((ExcelField)?os[0]).title();
????????????headerList.add(t);
????????}
????????//?初始化excel表:創(chuàng)建excel表、添加表標(biāo)題、創(chuàng)建表頭等等
????????initialize(title,?headerList);
????}
}
在初始化的時(shí)候,我們先從 Excel 模型對(duì)象中讀取注解,獲得一個(gè)注解列表;然后,再從注解列表中,讀取 title-字段標(biāo)題;最后,再初始化 Excel 表對(duì)象,包括:創(chuàng)建 Excel 表對(duì)象、添加表標(biāo)題、創(chuàng)建表頭、添加樣式。
第二步,寫入數(shù)據(jù)到 Excel 表對(duì)象。在這一步中,我們要把 Java 的列表數(shù)據(jù)寫到 Excel 表對(duì)象里,讓這些數(shù)據(jù)能變成 Excel 表的一行行信息。還是來看代碼。
public?class?ExcelExporter?{
????/*****************************?初始化?Excel?表對(duì)象?****************************/
????//?...省略無數(shù)代碼
????/*****************************?寫入數(shù)據(jù)到?Excel?表對(duì)象?****************************/
????/**
?????*?寫入數(shù)據(jù)
?????*?@return?list?數(shù)據(jù)列表
?????*/
????public??ExcelExporter?setDataList(List ?list)?{
????????for?(E?dataObj?:?list)?{
????????????//?添加行
????????????Row?row?=?this.addRow();
????????????//?獲取數(shù)據(jù),并寫入單元格
????????????int?cellNo?=?0;
????????????for?(Object[]?os?:?annotationList)?{
????????????????//?獲取成員變量的值
????????????????Object?value?=?null;
????????????????try?{
????????????????????value?=?Reflections.invokeGetter(dataObj,?((Field)?os[1]).getName());
????????????????}?catch?(Exception?ex)?{
????????????????????log.info(ex.toString());
????????????????????value?=?"";
????????????????}
????????????????if?(value?==?null)?{
????????????????????value?=?"";
????????????????}
????????????????//?寫入單元格
????????????????ExcelField?ef?=?(ExcelField)?os[0];
????????????????this.addCell(row,?cellNo++,?value,?ef.align());
????????????}
????????}
????????return?this;
????}
}
我們先傳入一個(gè)數(shù)據(jù)列表 dataList,然后用循環(huán)來遍歷 dataList,在這個(gè)循環(huán)中,我們不斷把數(shù)據(jù)寫進(jìn) Excel 表對(duì)象里,具體操作是:創(chuàng)建了一個(gè)空白行,利用注解獲取成員變量里的值,最后寫進(jìn) Excel 表的單元格里。
第三步,輸出文件。在這一步中,就是 Excel 表對(duì)象變成一個(gè)文件,來看下最后的代碼吧。
public?class?ExcelExporter?{
????/*****************************?初始化?Excel?表對(duì)象?****************************/
????//?...省略無數(shù)代碼
????/*****************************?寫入數(shù)據(jù)到?Excel?表對(duì)象?****************************/
????//?...省略無數(shù)代碼
????/*****************************?輸出相關(guān)?****************************/
????/**
?????*?輸出到文件
?????*?@param?fileName?輸出文件名,加上絕對(duì)路徑
?????*/
????public?ExcelExporter?writeFile(String?fileName)?throws?IOException?{
????????FileOutputStream?os?=?new?FileOutputStream(fileName);
????????this.write(os);
????????return?this;
????}
}
輸出文件就沒什么好說的了,就是指定文件名,然后把文件輸出到指定的地方。
到了這,讀取 Excel 模型就完成了。
當(dāng)然,讀取 Excel 模型涉及到注解的讀取,這是最難理解的地方,因?yàn)樽x取注解要用到 Java 另一個(gè)高級(jí)特性—反射。而且,注解一般是用來簡化業(yè)務(wù),如果你對(duì)業(yè)務(wù)沒有深刻的了解,是很難用好的。
限于篇幅,我只講了最核心的代碼,項(xiàng)目的完整代碼放在文末的鏈接上,大家可以好好看看。
寫在最后
注解想發(fā)揮作用,有三個(gè)要素:定義、使用、讀取。這篇文章利用了注解的三要素,實(shí)現(xiàn)了 Excel 導(dǎo)出功能。
這分成兩步。第一步,創(chuàng)建 Excel 模型,這涉及到注解三要素中的定義、使用;第二步,讀取 Excel 模型,這涉及到注解三要素中的讀取。
總之,注解一般用來簡化業(yè)務(wù),你要想用好注解,不但得熟練掌握 Java 的高級(jí)用法,還得對(duì)業(yè)務(wù)有深刻的理解。
文章演示代碼:復(fù)制鏈接跳轉(zhuǎn)
https://gitee.com/jiarupc/excel-export

