Master Efficient Excel Imports in Java with EasyExcel and Custom Listeners
This article explains how to use Alibaba's EasyExcel library in Java to perform fast, flexible Excel data imports, covering custom listeners, validation, handling complex sheet structures, and providing reusable utility classes for robust backend data processing.
In today's data‑driven world, efficiently managing large volumes of data is crucial for enterprises, especially those that rely on Excel for data exchange and reporting. Traditional import methods often suffer from performance bottlenecks, format incompatibility, and lack of flexibility.
This article introduces a Java‑based solution that leverages Alibaba's open‑source EasyExcel library to achieve high‑performance, flexible Excel data import. It demonstrates how to handle complex Excel structures with custom listeners and utility classes, and how to meet various data validation requirements.
1. Special Excel Sheet Example
2. Usage Examples
2.1 Excel Import Listener
<code>public interface ExcelListener<T> extends ReadListener<T> {
ExcelResult<T> getExcelResult();
/**
* Set pre‑header data
*/
void setPreHeaderData(Map<String, String> preHeaderData);
}</code>2.2 Default Listener
<code>@Slf4j
@NoArgsConstructor
public class DefaultExcelListener<T> extends AnalysisEventListener<T> implements ExcelListener<T> {
private Boolean isValidate = Boolean.TRUE;
private Map<Integer, String> headMap;
private ExcelResult<T> excelResult;
public DefaultExcelListener(boolean isValidate) {
this.excelResult = new DefaultExcelResult<>();
this.isValidate = isValidate;
}
@Override
public void onException(Exception exception, AnalysisContext context) throws Exception {
String errMsg = null;
if (exception instanceof ExcelDataConvertException e) {
Integer rowIndex = e.getRowIndex();
Integer columnIndex = e.getColumnIndex();
errMsg = StrUtil.format("Row {}‑Column {}‑Header {}: parsing error", rowIndex + 1, columnIndex + 1, headMap.get(columnIndex));
if (log.isDebugEnabled()) {
log.error(errMsg);
}
}
if (exception instanceof ConstraintViolationException e) {
String msgs = StreamUtils.join(e.getConstraintViolations(), ConstraintViolation::getMessage, ", ");
errMsg = StrUtil.format("Row {} data validation error: {}", context.readRowHolder().getRowIndex() + 1, msgs);
if (log.isDebugEnabled()) {
log.error(errMsg);
}
}
excelResult.getErrorList().add(errMsg);
throw new ExcelAnalysisException(errMsg);
}
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
this.headMap = headMap;
log.debug("Parsed header: {}", JsonUtils.toJsonString(headMap));
}
@Override
public void invoke(T data, AnalysisContext context) {
if (isValidate) {
ValidatorUtils.validate(data);
}
excelResult.getList().add(data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
log.debug("All data parsed!");
}
@Override
public ExcelResult<T> getExcelResult() {
return excelResult;
}
@Override
public void setPreHeaderData(Map<String, String> preHeaderData) {
// optional implementation
}
}</code>2.3 Excel Result Interface
<code>public interface ExcelResult<T> {
/** Object list */
List<T> getList();
/** Error list */
List<String> getErrorList();
/** Import analysis summary */
String getAnalysis();
}</code>2.4 Default Excel Result Implementation
<code>public class DefaultExcelResult<T> implements ExcelResult<T> {
@Setter private List<T> list;
@Setter private List<String> errorList;
public DefaultExcelResult() {
this.list = new ArrayList<>();
this.errorList = new ArrayList<>();
}
@Override
public List<T> getList() { return list; }
@Override
public List<String> getErrorList() { return errorList; }
@Override
public String getAnalysis() {
int successCount = list.size();
int errorCount = errorList.size();
if (successCount == 0) {
return "Read failed, no data parsed";
} else if (errorCount == 0) {
return StrUtil.format("All rows read successfully! Total {}", successCount);
} else {
return ""; // customize as needed
}
}
}</code>3. Special Excel Features
3.1 Cell Position Class
<code>/**
* Cell position class for representing Excel cell coordinates
*/
@Data
public class CellPosition {
private final int row; // zero‑based row index
private final int column; // zero‑based column index
public CellPosition(int row, String columnLetter) {
this.row = row - 1;
this.column = getColumnIndex(columnLetter);
}
private static int getColumnIndex(String columnLetter) {
int columnIndex = 0;
for (int i = 0; i < columnLetter.length(); i++) {
columnIndex = columnIndex * 26 + (columnLetter.charAt(i) - 'A' + 1);
}
return columnIndex - 1;
}
}</code>3.2 Pre‑Header Listener
<code>@Slf4j
@Getter
public class PreHeaderListener extends AnalysisEventListener<Map<Integer, String>> {
private final Set<CellPosition> targetCells;
private final Map<String, String> preHeaderData = new HashMap<>();
public PreHeaderListener(Set<CellPosition> targetCells) {
this.targetCells = targetCells;
}
private static String getColumnLetter(int columnIndex) {
StringBuilder sb = new StringBuilder();
while (columnIndex >= 0) {
sb.insert(0, (char)('A' + columnIndex % 26));
columnIndex = columnIndex / 26 - 1;
}
return sb.toString();
}
@Override
public void invoke(Map<Integer, String> map, AnalysisContext context) {
int rowIndex = context.readRowHolder().getRowIndex();
for (CellPosition position : targetCells) {
if (position.getRow() == rowIndex) {
int colIdx = position.getColumn();
if (map.containsKey(colIdx)) {
String cellValue = map.get(colIdx);
preHeaderData.put("Row" + (rowIndex + 1) + "Column" + getColumnLetter(colIdx), cellValue);
}
}
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
// optional post‑processing
}
}</code>4. Implementation Overview
The utility class
ExcelUtilprovides static methods for synchronous import, import with validation, and import with custom listeners. It handles pre‑header data extraction, stream reuse, and returns an
ExcelResultcontaining parsed objects and error messages.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.