Java?POI讀取Excel所需全部依賴包的實戰(zhàn)指南
簡介:
Java POI是處理Microsoft Office文件的開源庫,尤其適用于讀取和操作Excel文件。本文詳細介紹了使用Java POI讀取Excel時必須導(dǎo)入的核心JAR包及其作用,包括支持.xls和.xlsx格式的主庫、XML解析依賴、OOXML架構(gòu)支持以及輔助工具包。正確配置這些依賴(如poi-3.7.jar、poi-ooxml-schemas、xmlbeans、dom4j等)對程序正常運行至關(guān)重要,缺失任一組件可能導(dǎo)致運行時異常。此外,還提供了示例與擴展功能包,便于學(xué)習(xí)和集成到構(gòu)建流程中。
Java POI 深度解析:從基礎(chǔ)操作到企業(yè)級實戰(zhàn)的全鏈路指南
在現(xiàn)代企業(yè)級應(yīng)用中,Excel 已不僅是辦公工具,更成為數(shù)據(jù)流轉(zhuǎn)的核心載體。無論是財務(wù)報表、用戶行為日志,還是供應(yīng)鏈清單,這些結(jié)構(gòu)化數(shù)據(jù)常常以 .xlsx 或 .xls 的形式穿梭于系統(tǒng)之間。而 Java 作為后端開發(fā)的主力語言,如何高效、穩(wěn)定地處理這些文件?Apache POI 正是這一問題的標(biāo)準(zhǔn)答案。
但現(xiàn)實遠比“調(diào)用 API”復(fù)雜得多。你是否曾遇到過這樣的場景:
- 導(dǎo)入一個 50MB 的訂單表,JVM 瞬間拋出
OutOfMemoryError; - 解析單元格時發(fā)現(xiàn)本該是日期的數(shù)字變成了科學(xué)計數(shù)法字符串;
- 多個模塊依賴不同版本的 POI,運行時報出
NoSuchMethodError; - 明明代碼邏輯正確,卻因為某個隱藏的 XML 節(jié)點導(dǎo)致解析失敗……
這些問題背后,往往不是 API 使用不當(dāng),而是對 POI 底層機制缺乏深度理解。今天,我們就來徹底拆解這套被無數(shù)項目依賴的技術(shù)棧——從最基礎(chǔ)的對象模型,到流式處理的性能瓶頸,再到輔助依賴包之間的隱秘協(xié)作,帶你走進一個真正“可控”的 Excel 自動化世界。
核心架構(gòu)設(shè)計:HSSF 與 XSSF 的雙生子之謎
當(dāng)我們說“用 Java 讀寫 Excel”,其實是在和兩個截然不同的技術(shù)體系打交道: HSSF 和 XSSF 。它們分別對應(yīng)兩種完全不同的文件格式—— .xls 和 .xlsx ,雖然對外提供相似的接口,但內(nèi)部實現(xiàn)天差地別。
HSSFWorkbook vs XSSFWorkbook:二進制與 XML 的分水嶺
// 處理老式 .xls 文件
Workbook hssf = new HSSFWorkbook(new FileInputStream("data.xls"));
// 處理新版 .xlsx 文件
Workbook xssf = new XSSFWorkbook(OPCPackage.open("data.xlsx"));
看起來很像?別被表象迷惑了。這兩行代碼背后的加載過程完全不同。
| 維度 | HSSF(.xls) | XSSF(.xlsx) |
|---|---|---|
| 存儲結(jié)構(gòu) | 二進制 BIFF 格式 | ZIP 壓縮包 + XML |
| 最大行數(shù) | 65,536 行 | 1,048,576 行 |
| 內(nèi)存模型 | 半懶加載 | 全量 DOM 加載 |
| 初始化速度 | 快(直接解析字節(jié)流) | 慢(需解壓并解析多個 XML) |
HSSF 面對的是 Excel 97–2003 年代遺留下來的二進制格式(BIFF),它的優(yōu)點是緊湊、解析快;缺點也很明顯——功能受限、容量小、不支持現(xiàn)代特性如圖表或條件格式。
而 XSSF 則基于 Office Open XML (OOXML) 標(biāo)準(zhǔn)構(gòu)建,本質(zhì)上是一個符合 OPC(Open Packaging Conventions)規(guī)范的 ZIP 歸檔文件。這意味著你可以把它當(dāng)成普通壓縮包打開:
unzip -l report.xlsx
你會看到類似這樣的目錄結(jié)構(gòu):
xl/ ├── workbook.xml # 工作簿元信息 ├── worksheets/sheet1.xml # 實際表格數(shù)據(jù) ├── sharedStrings.xml # 全局字符串池 └── styles.xml # 樣式定義 [Content_Types].xml # MIME 類型映射 _rels/.rels # 關(guān)系描述符
這正是 XSSF 強大之處:它將復(fù)雜的電子文檔分解為可獨立訪問的組件。但也正因如此,當(dāng)你使用 new XSSFWorkbook() 時,POI 會一次性把這些 XML 文件全部加載進內(nèi)存,并轉(zhuǎn)換成 DOM 樹節(jié)點。哪怕你只想讀取第一行數(shù)據(jù),整個文檔也得完整駐留堆中。
小貼士:如果你正在維護一個老系統(tǒng),且只處理小于 10MB 的 .xls 文件,HSSF 依然是輕量高效的首選。但對于任何新項目,尤其是涉及大數(shù)據(jù)量的場景,請果斷轉(zhuǎn)向 .xlsx + XSSF/SXSSF 技術(shù)棧。
對象模型探秘:Workbook → Sheet → Row → Cell 的層級迷宮
無論你是操作 .xls 還是 .xlsx ,POI 都抽象出統(tǒng)一的對象模型:
Workbook → Sheet → Row → Cell
這個看似簡單的四層結(jié)構(gòu),構(gòu)成了所有 Excel 操作的基礎(chǔ)路徑。我們來看一段典型的遍歷代碼:
Sheet sheet = workbook.getSheetAt(0);
for (int i = 0; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
if (row == null) continue;
for (int j = 0; j < row.getLastCellNum(); j++) {
Cell cell = row.getCell(j, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
System.out.print(getCellValue(cell) + "\t");
}
System.out.println();
}
這段代碼雖然簡潔,但藏著不少坑。比如:
getLastRowNum()返回的是 物理存在的最后一行索引 ,中間可能有空行;getRow(i)可能返回null,必須判空;getCell(j)默認(rèn)策略是返回null,如果不設(shè)置MissingCellPolicy,極易引發(fā) NPE;- 單元格類型不確定,直接調(diào)用
getStringCellValue()在非字符串類型上會拋異常!
所以更健壯的做法是封裝一個通用的取值方法:
public static Object getCellValue(Cell cell) {
if (cell == null) return "";
switch (cell.getCellType()) {
case STRING:
return cell.getStringCellValue().trim();
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
return cell.getDateCellValue();
} else {
return BigDecimal.valueOf(cell.getNumericCellValue())
.stripTrailingZeros()
.toPlainString(); // 避免科學(xué)計數(shù)法
}
case BOOLEAN:
return cell.getBooleanCellValue();
case FORMULA:
FormulaEvaluator evaluator = cell.getSheet()
.getWorkbook()
.getCreationHelper()
.createFormulaEvaluator();
return evaluateFormula(evaluator.evaluate(cell));
default:
return "";
}
}
注意這里用了 BigDecimal 而不是 double ,特別適用于金額字段,避免浮點精度丟失問題。同時通過 DataFormatter 或公式求值器進一步提升兼容性。
下面這張 Mermaid 圖清晰展示了對象間的導(dǎo)航關(guān)系:
graph TD
A[Workbook] --> B[Sheet 1]
A --> C[Sheet 2]
B --> D[Row 0]
B --> E[Row 1]
D --> F[Cell A1]
D --> G[Cell B1]
E --> H[Cell A2]
E --> I[Cell B2]
style A fill:#f9f,stroke:#333
style B fill:#bbf,stroke:#333,color:#fff
style D fill:#ffd,stroke:#333
style F fill:#dfd,stroke:#333
每一層都可以通過索引或名稱訪問,例如 workbook.getSheet("銷售明細") 。這種樹狀結(jié)構(gòu)非常符合人類對表格的認(rèn)知習(xí)慣,但在大規(guī)模數(shù)據(jù)下卻成了內(nèi)存殺手——每個 Cell 對象都占用幾十字節(jié),百萬級單元格輕松吃掉數(shù)百 MB 堆空間。
性能危機:當(dāng) XSSFWorkbook 遇上百萬行數(shù)據(jù)
設(shè)想這樣一個需求:客戶上傳一份包含 10 萬行商品信息的 Excel 表,要求系統(tǒng)自動導(dǎo)入數(shù)據(jù)庫。
新手可能會這么寫:
XSSFWorkbook wb = new XSSFWorkbook(fileInputStream);
XSSFSheet sheet = wb.getSheetAt(0);
for (Row row : sheet) {
List<String> values = new ArrayList<>();
for (Cell cell : row) {
values.add(formatCellValue(cell));
}
saveToDatabase(values); // 批量插入
}
運行一次試試?很可能幾秒后 JVM 直接崩潰:
java.lang.OutOfMemoryError: Java heap space
為什么?
因為 XSSFWorkbook 是基于 DOM 模型的——它會把整個 .xlsx 文件解析成內(nèi)存中的對象樹。實驗數(shù)據(jù)顯示,一個 10MB 的 .xlsx 文件,在加載后可能消耗 超過 500MB 的堆內(nèi)存!原因包括:
- XML 解析膨脹 :DOM 解析本身會產(chǎn)生大量臨時對象;
- SharedStringsTable 全量緩存 :所有文本字符串一次性加載進內(nèi)存;
- 樣式表復(fù)制 :每種樣式都被映射為 Java 對象;
- 空單元格占位 :即使為空,也會創(chuàng)建
XSSFCell實例。
這就引出了一個關(guān)鍵抉擇: UserModel vs EventModel
| 特性 | UserModel ( XSSFWorkbook) | EventModel ( XSSFReader) |
|---|---|---|
| 編程模型 | 面向?qū)ο?,直觀易用 | 事件驅(qū)動,需自定義處理器 |
| 內(nèi)存占用 | 高(O(n)) | 極低(O(1)) |
| 適用場景 | 小文件讀寫、模板填充 | 大文件流式解析 |
| 是否支持修改 | 是 | 否(僅讀?。?/td> |
要解決大文件問題,我們必須放棄“先把文件裝進來再處理”的思維定式,轉(zhuǎn)而采用 SAX 流式解析 。
流式革命:用 SAX 模式實現(xiàn)恒定內(nèi)存解析
SAX(Simple API for XML)是一種事件驅(qū)動的 XML 解析方式。它不像 DOM 那樣一次性加載全文,而是邊讀邊觸發(fā)回調(diào)函數(shù)。對于 .xlsx 來說,這意味著我們可以一邊從 ZIP 流中讀取 sheet1.xml ,一邊提取數(shù)據(jù),全程只需幾十 KB 內(nèi)存。
以下是核心實現(xiàn)步驟:
try (OPCPackage pkg = OPCPackage.open("huge.xlsx")) {
XSSFReader reader = new XSSFReader(pkg);
SharedStringsTable sst = reader.getSharedStringsTable();
XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) reader.getSheetsData();
while (iter.hasNext()) {
try (InputStream stream = iter.next()) {
String sheetName = iter.getSheetName();
System.out.println("Processing: " + sheetName);
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
xmlReader.setContentHandler(new StreamingSheetHandler(sst));
xmlReader.parse(new InputSource(stream));
}
}
} catch (Exception e) {
e.printStackTrace();
}
其中 StreamingSheetHandler 是我們的自定義處理器:
class StreamingSheetHandler extends DefaultHandler {
private boolean inCell = false;
private boolean inValue = false;
private StringBuilder contents = new StringBuilder();
private String currentCellRef;
private final SharedStringsTable sst;
public StreamingSheetHandler(SharedStringsTable sst) {
this.sst = sst;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if ("c".equals(qName)) {
currentCellRef = attributes.getValue("r"); // 如 A1, B2
inCell = true;
} else if (inCell && "v".equals(qName)) {
inValue = true;
}
}
@Override
public void characters(char[] ch, int start, int length) {
if (inValue) {
contents.append(ch, start, length);
}
}
@Override
public void endElement(String uri, String localName, String qName) {
if ("v".equals(qName)) {
inValue = false;
String value = contents.toString().trim();
// 若單元格類型為 's',表示引用共享字符串表
String cellType = attributes.getValue("t");
if ("s".equals(cellType) && !value.isEmpty()) {
int idx = Integer.parseInt(value);
value = sst.getItemAt(idx).getString();
}
System.out.printf("%s: %s%n", currentCellRef, value);
contents.setLength(0);
} else if ("c".equals(qName)) {
inCell = false;
}
}
}
這種方式的優(yōu)勢顯而易見:
? 內(nèi)存恒定 :不管文件多大,內(nèi)存占用基本不變
? 速度快 :無需構(gòu)建完整對象樹,解析效率提升 3–5 倍
? 可實時輸出 :邊解析邊寫庫,無需等待
不過也有局限:不能隨機訪問某一行,也不適合做寫操作。如果既要高性能又要可寫能力怎么辦?那就輪到 SXSSF 登場了。
終極方案:SXSSF —— 大規(guī)模導(dǎo)出的秘密武器
如果你需要生成一個包含百萬行數(shù)據(jù)的 Excel 報告,傳統(tǒng)的 XSSFWorkbook 會讓你的服務(wù)器癱瘓。而 SXSSFWorkbook 就是為此類場景量身打造的解決方案。
它是 XSSF 的流式變體,采用“滑動窗口”機制:只將最近 N 行保留在內(nèi)存中,其余溢出至磁盤臨時文件。當(dāng)最終寫入時,再合并輸出。
// 創(chuàng)建基于 XSSF 的流式工作簿,保留 100 行在內(nèi)存
XSSFWorkbook xwb = new XSSFWorkbook();
SXSSFWorkbook sxwb = new SXSSFWorkbook(xwb, 100);
SXSSFSheet sheet = sxwb.createSheet("數(shù)據(jù)導(dǎo)出");
for (int i = 0; i < 1_000_000; i++) {
Row row = sheet.createRow(i);
for (int j = 0; j < 10; j++) {
Cell cell = row.createCell(j);
cell.setCellValue("Row" + i + "-Col" + j);
}
}
try (FileOutputStream out = new FileOutputStream("output.xlsx")) {
sxwb.write(out);
} finally {
sxwb.dispose(); // 刪除臨時文件
}
關(guān)鍵參數(shù)說明:
new SXSSFWorkbook(xwb, 100):前 100 行常駐內(nèi)存,后續(xù)行寫入臨時文件;sxwb.dispose():務(wù)必調(diào)用,否則臨時文件不會自動清理;- 支持設(shè)置壓縮、臨時目錄等高級選項;
注意: poi-3.7.jar 原生不包含 SXSSF(首次出現(xiàn)在 3.8-beta),若無法升級主版本,可嘗試單獨引入較新的 poi-ooxml 模塊以獲得支持。
底層支撐:那些你忽略卻至關(guān)重要的輔助依賴包
很多人以為 POI 只是一個 poi.jar ,實際上它是一套精密協(xié)作的生態(tài)系統(tǒng)。特別是當(dāng)你處理 .xlsx 文件時,以下三個 JAR 包缺一不可:
poi-ooxml-schemas-3.7.jar:XML Schema 的 Java 映射
Open XML 規(guī)范由數(shù)百個 XSD 文件定義,涵蓋 <workbook> 、 <worksheet> 、 <table> 等所有元素。POI 團隊使用 Apache XmlBeans 工具鏈將這些 XSD 預(yù)編譯為 Java 類,打包進 poi-ooxml-schemas-3.7.jar 。
例如, CTWorkbook.java 對應(yīng) <workbook> 元素:
public interface CTWorkbook extends XmlObject {
List<CTSheet> getSheetsList();
CTSheets addNewSheets();
boolean isSetSheets();
}
這些類以 CT 開頭(意為 Complex Type),實現(xiàn)了強類型的 XML 操作。相比原始 DOM,它們提供了更好的 IDE 提示和編譯期檢查。
xmlbeans-2.3.0.jar:運行時綁定引擎
有了生成的類還不夠,還需要一個運行時引擎來完成 XML ↔ Java 對象的序列化/反序列化。這就是 xmlbeans 的職責(zé)。
當(dāng)你執(zhí)行:
CTWorksheet ct = sheet.getCTWorksheet(); ct.addNewSheetViews().addNewSheetView().setRightToLeft(true);
XmlBeans 會在幕后將這些調(diào)用轉(zhuǎn)化為對應(yīng)的 XML 修改,并最終持久化到 .xlsx 文件中。
然而,這也帶來了潛在風(fēng)險:由于 poi-ooxml-schemas 包體積巨大(約 20MB),且與其他庫可能存在版本沖突(如舊版 Spring Boot 自帶 xmlbeans),很容易引發(fā) LinkageError 或 NoClassDefFoundError 。
推薦排除策略(Maven):
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.17</version>
<exclusions>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 單獨引入受控版本 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>2.6.0</version>
</dependency>
這樣可以確保類路徑唯一,避免重復(fù)加載。
dom4j-1.6.1.jar:靈活補充非標(biāo)準(zhǔn)內(nèi)容解析
盡管 POI 主力使用 XmlBeans,但在面對 customXML、VBA 宏或 Visio 圖形元數(shù)據(jù)時,其原生支持有限。此時,輕量級的 dom4j 成為理想補充。
例如,讀取嵌入式業(yè)務(wù)數(shù)據(jù):
ZipFile zip = new ZipFile("report.xlsx");
ZipEntry entry = zip.getEntry("customXml/item1.xml");
if (entry != null) {
InputStream is = zip.getInputStream(entry);
SAXReader reader = new SAXReader();
Document doc = reader.read(is);
Element root = doc.getRootElement();
String bizId = root.elementText("businessId");
String status = root.element("metadata").attributeValue("status");
log.info("Found biz context: id={}, status={}", bizId, status);
}
還可以結(jié)合 XPath 實現(xiàn)復(fù)雜查詢:
Map<String, String> ns = Map.of("x", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
reader.getDocumentFactory().setXPathNamespaceContext(new SimpleNamespaceContext(ns));
List<Node> redCells = doc.selectNodes("http://x:c[x:v/@color='red']");
合理劃分職責(zé)邊界,才能兼顧性能與靈活性:
| 場景 | 推薦工具 |
|---|---|
| 標(biāo)準(zhǔn) Sheet/Cell 操作 | POI (XSSF/HSSF) |
| SharedStrings 處理 | POI + XmlBeans |
| 自定義 XML 數(shù)據(jù)讀取 | dom4j |
| 圖表結(jié)構(gòu)分析 | dom4j + XPath |
| 大文件流式解析 | POI EventModel |
生產(chǎn)級實踐:Spring Boot 中的安全集成模式
在真實項目中,我們不僅要考慮功能實現(xiàn),更要關(guān)注資源管理、異常恢復(fù)和可觀測性。以下是一個基于 Spring Boot 的通用 Excel 導(dǎo)入服務(wù)模板:
@Service
@Slf4j
public class ExcelImportService {
public List<Map<String, Object>> importFromStream(InputStream inputStream, int sheetIndex) {
List<Map<String, Object>> result = new ArrayList<>();
try (OPCPackage pkg = OPCPackage.open(inputStream);
XSSFReader reader = new XSSFReader(pkg)) {
ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(pkg);
XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) reader.getSheetsData();
int currentIndex = 0;
while (iter.hasNext()) {
if (currentIndex++ == sheetIndex) {
try (InputStream sheetStream = iter.next()) {
parseSheetStream(sheetStream, strings, result);
break;
}
} else {
iter.next().close(); // 跳過未選中的 sheet
}
}
} catch (NotOfficeXmlFileException e) {
throw new IllegalArgumentException("無效的 .xlsx 文件", e);
} catch (OutOfMemoryError e) {
log.error("內(nèi)存不足,建議啟用流式解析", e);
throw new RuntimeException("文件過大,無法處理");
} catch (Exception e) {
log.error("Excel 解析失敗", e);
throw new RuntimeException("解析異常:" + e.getMessage(), e);
}
return result;
}
private void parseSheetStream(InputStream stream,
ReadOnlySharedStringsTable strings,
List<Map<String, Object>> result) throws Exception {
XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader xr = factory.createXMLStreamReader(stream);
Map<String, Object> currentRow = null;
StringBuilder cellValue = new StringBuilder();
String cellRef = "";
String cellType = "";
while (xr.hasNext()) {
int event = xr.next();
switch (event) {
case START_ELEMENT:
String tagName = xr.getLocalName();
if ("row".equals(tagName)) {
currentRow = new LinkedHashMap<>();
} else if ("c".equals(tagName)) {
cellRef = xr.getAttributeValue(null, "r");
cellType = xr.getAttributeValue(null, "t");
cellValue.setLength(0);
} else if ("v".equals(tagName) || "t".equals(tagName)) {
// 準(zhǔn)備接收字符數(shù)據(jù)
}
break;
case CHARACTERS:
cellValue.append(xr.getText());
break;
case END_ELEMENT:
tagName = xr.getLocalName();
if ("v".equals(tagName) || "t".equals(tagName)) {
String value = cellValue.toString().trim();
if (!value.isEmpty() && "s".equals(cellType)) {
try {
int idx = Integer.parseInt(value);
value = strings.getEntryAt(idx).getString();
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
}
if (currentRow != null && cellRef.matches("[A-Z]+\\d+")) {
currentRow.put(extractColumnName(cellRef), value);
}
} else if ("row".equals(tagName) && currentRow != null) {
result.add(currentRow);
}
break;
}
}
}
private String extractColumnName(String ref) {
return ref.replaceAll("\\d+", "");
}
}
亮點解析:
自動資源管理 : try-with-resources 確保 OPCPackage 和流被及時關(guān)閉
異常分級處理 :區(qū)分文件格式錯誤、內(nèi)存溢出、解析失敗等不同級別異常
列名提取 :將 A1 , B2 轉(zhuǎn)換為 A , B 作為鍵存儲
跳過無關(guān) sheet :避免加載不需要的工作表,節(jié)省內(nèi)存
配合單元測試驗證邊界情況:
@Test
void should_parse_small_excel_correctly() throws Exception {
try (InputStream is = getClass().getResourceAsStream("/test-data.xlsx")) {
List<Map<String, Object>> data = service.importFromStream(is, 0);
assertThat(data).hasSize(3);
assertThat(data.get(0)).containsKey("姓名");
}
}
構(gòu)建工具最佳配置:Maven & Gradle 實戰(zhàn)指南
最后,別忘了依賴管理的規(guī)范化。強烈建議統(tǒng)一版本,避免混合引入 poi-3.7.jar 和 poi-ooxml-3.5-beta6 這種嚴(yán)重錯配的情況。
Maven 推薦配置
<properties>
<poi.version>3.17</poi.version> <!-- 或 5.2.3 最新版 -->
</properties>
<dependencies>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
</dependencies>
Gradle 推薦配置
ext {
poiVersion = '3.17'
}
dependencies {
implementation "org.apache.poi:poi:${poiVersion}"
implementation "org.apache.poi:poi-ooxml:${poiVersion}"
implementation "org.apache.poi:poi-ooxml-schemas:${poiVersion}"
implementation 'dom4j:dom4j:1.6.1'
}
并通過以下命令檢查依賴樹:
mvn dependency:tree | grep poi
確保所有 POI 模塊來自同一版本系列,杜絕類沖突隱患。
結(jié)語:讓 Excel 自動化真正服務(wù)于業(yè)務(wù)
Java POI 不只是一個工具庫,它是一整套關(guān)于 結(jié)構(gòu)化數(shù)據(jù)流動 的設(shè)計哲學(xué)。從最初的簡單讀寫,到如今的大規(guī)模流式處理,每一次演進都在回應(yīng)現(xiàn)實世界的復(fù)雜挑戰(zhàn)。
掌握它的關(guān)鍵,從來不只是記住幾個 API,而是理解:
- 什么時候該用 UserModel,什么時候必須切換 EventModel?
- 如何平衡開發(fā)效率與系統(tǒng)穩(wěn)定性?
- 當(dāng)出現(xiàn)
OutOfMemoryError時,是調(diào)大堆內(nèi)存,還是重構(gòu)解析邏輯? - 第三方依賴沖突了,你是盲目排除,還是深入分析類加載機制?
這才是資深工程師與初級開發(fā)者之間的真正差距所在。
希望這篇長達七千字的深度剖析,能幫你建立起一套完整的 POI 認(rèn)知框架。下次當(dāng)你面對那個“又大又慢”的 Excel 文件時,心里會多一份從容與底氣。
畢竟,真正的自動化,不是讓機器干活,而是讓我們掌控機器的方式變得更聰明。
以上就是Java POI讀取Excel所需全部依賴包的實戰(zhàn)指南的詳細內(nèi)容,更多關(guān)于Java POI讀取Excel所需依賴包的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot中EasyPoi實現(xiàn)自動新增序號的方法
本文主要介紹了EasyPoi實現(xiàn)自動新增序號,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09
SpringBoot項目發(fā)送釘釘消息功能實現(xiàn)
在工作中的一些告警需要發(fā)送釘釘通知,有的是發(fā)給個人,有的要發(fā)到群里,這時項目就需要接入釘釘,實現(xiàn)發(fā)消息的功能,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2024-02-02
SpringBoot整合redis+Aop防止重復(fù)提交的實現(xiàn)
Spring Boot通過AOP可以實現(xiàn)防止表單重復(fù)提交,本文主要介紹了SpringBoot整合redis+Aop防止重復(fù)提交的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
為什么 Java 8 中不需要 StringBuilder 拼接字符串
java8中,編輯器對“+”進行了優(yōu)化,默認(rèn)使用StringBuilder進行拼接,所以不用顯示的使用StringBuilder了,直接用“+”就可以了。下面我們來詳細了解一下2019-05-05
Springboot配置管理Externalized?Configuration深入探究
這篇文章主要介紹了Springboot配置管Externalized?Configuration深入探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-01-01
使用 Spring Boot 實現(xiàn) WebSocket實時通信
本篇文章主要介紹了使用 Spring Boot 實現(xiàn) WebSocket實時通信,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10
Java使用connectTo方法提高代碼可續(xù)性詳解
這篇文章主要介紹了Java使用connectTo方法提高代碼可續(xù)性,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08

