Backend Development 10 min read

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.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Master Efficient Excel Imports in Java with EasyExcel and Custom Listeners

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

Special Excel Example
Special Excel 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

ExcelUtil

provides static methods for synchronous import, import with validation, and import with custom listeners. It handles pre‑header data extraction, stream reuse, and returns an

ExcelResult

containing parsed objects and error messages.

backendJavaeasyexcelExcel importdata validationcustom-listenerexcel-utility
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.