0
# Event System
1
2
Event-driven processing system with listeners for handling large datasets efficiently. EasyExcel uses a listener pattern for memory-efficient processing of Excel files, allowing row-by-row processing without loading entire datasets into memory.
3
4
## Capabilities
5
6
### Core Listener Interface
7
8
Primary interface for handling read events during Excel processing.
9
10
```java { .api }
11
/**
12
* Main interface for handling read events
13
* @param <T> Type of data object for each row
14
*/
15
public interface ReadListener<T> extends Listener {
16
/**
17
* Process each row of data
18
* @param data Parsed data object for current row
19
* @param context Analysis context with metadata
20
*/
21
void invoke(T data, AnalysisContext context);
22
23
/**
24
* Called after all data has been analyzed
25
* @param context Analysis context with final metadata
26
*/
27
void doAfterAllAnalysed(AnalysisContext context);
28
29
/**
30
* Handle exceptions during reading
31
* @param exception Exception that occurred
32
* @param context Analysis context when exception occurred
33
* @throws Exception Can re-throw or handle the exception
34
*/
35
default void onException(Exception exception, AnalysisContext context) throws Exception {
36
throw exception;
37
}
38
39
/**
40
* Process header row data
41
* @param headMap Map of column index to header cell data
42
* @param context Analysis context
43
*/
44
default void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
45
// Default implementation does nothing
46
}
47
48
/**
49
* Process extra information (comments, hyperlinks, merged regions)
50
* @param extra Extra cell information
51
* @param context Analysis context
52
*/
53
default void extra(CellExtra extra, AnalysisContext context) {
54
// Default implementation does nothing
55
}
56
57
/**
58
* Control whether to continue reading
59
* @param context Analysis context
60
* @return true to continue reading, false to stop
61
*/
62
default boolean hasNext(AnalysisContext context) {
63
return true;
64
}
65
}
66
```
67
68
### Analysis Context
69
70
Context interface providing metadata and state information during reading.
71
72
```java { .api }
73
/**
74
* Provides context information during read operations
75
*/
76
public interface AnalysisContext {
77
/**
78
* Get the current row's analysis result
79
* @return Current row data object
80
*/
81
Object getCurrentRowAnalysisResult();
82
83
/**
84
* Get current row number (0-based)
85
* @return Row number
86
*/
87
Integer getCurrentRowNum();
88
89
/**
90
* Get current sheet information
91
* @return ReadSheet configuration
92
*/
93
ReadSheet getCurrentSheet();
94
95
/**
96
* Get Excel file type
97
* @return ExcelTypeEnum (XLS, XLSX, CSV)
98
*/
99
ExcelTypeEnum getExcelType();
100
101
/**
102
* Get read holder with configuration
103
* @return ReadHolder with settings
104
*/
105
ReadHolder currentReadHolder();
106
107
/**
108
* Get global configuration
109
* @return GlobalConfiguration settings
110
*/
111
GlobalConfiguration getGlobalConfiguration();
112
113
/**
114
* Get current workbook holder
115
* @return ReadWorkbook configuration
116
*/
117
ReadWorkbookHolder getReadWorkbookHolder();
118
119
/**
120
* Get current sheet holder
121
* @return ReadSheetHolder configuration
122
*/
123
ReadSheetHolder getReadSheetHolder();
124
125
/**
126
* Interrupt the reading process
127
*/
128
void interrupt();
129
}
130
```
131
132
### Abstract Base Listener
133
134
Abstract base implementation providing common functionality.
135
136
```java { .api }
137
/**
138
* Abstract base implementation of ReadListener
139
* @param <T> Type of data object for each row
140
*/
141
public abstract class AnalysisEventListener<T> implements ReadListener<T> {
142
/**
143
* Process each row of data (must be implemented)
144
* @param data Parsed data object for current row
145
* @param context Analysis context with metadata
146
*/
147
public abstract void invoke(T data, AnalysisContext context);
148
149
/**
150
* Called after all data has been analyzed (must be implemented)
151
* @param context Analysis context with final metadata
152
*/
153
public abstract void doAfterAllAnalysed(AnalysisContext context);
154
155
/**
156
* Handle exceptions during reading
157
* @param exception Exception that occurred
158
* @param context Analysis context when exception occurred
159
*/
160
@Override
161
public void onException(Exception exception, AnalysisContext context) throws Exception {
162
throw exception;
163
}
164
165
/**
166
* Process header row data
167
* @param headMap Map of column index to header cell data
168
* @param context Analysis context
169
*/
170
@Override
171
public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
172
// Default implementation does nothing
173
}
174
}
175
```
176
177
### Built-in Listener Implementations
178
179
Pre-built listeners for common scenarios.
180
181
```java { .api }
182
/**
183
* Synchronous listener that collects all data in a List
184
* @param <T> Type of data object
185
*/
186
public class SyncReadListener<T> implements ReadListener<T> {
187
private final List<T> list = new ArrayList<>();
188
189
@Override
190
public void invoke(T data, AnalysisContext context) {
191
list.add(data);
192
}
193
194
@Override
195
public void doAfterAllAnalysed(AnalysisContext context) {
196
// Analysis completed
197
}
198
199
/**
200
* Get all collected data
201
* @return List of all data objects
202
*/
203
public List<T> getList() {
204
return list;
205
}
206
}
207
208
/**
209
* Paginated listener for processing large datasets in batches
210
* @param <T> Type of data object
211
*/
212
public abstract class PageReadListener<T> extends AnalysisEventListener<T> {
213
private final int batchCount;
214
private List<T> cachedDataList = new ArrayList<>();
215
216
/**
217
* Create paginated listener with batch size
218
* @param batchCount Number of records per batch
219
*/
220
public PageReadListener(int batchCount) {
221
this.batchCount = batchCount;
222
}
223
224
@Override
225
public void invoke(T data, AnalysisContext context) {
226
cachedDataList.add(data);
227
if (cachedDataList.size() >= batchCount) {
228
invokeAfterBatch(cachedDataList, context);
229
cachedDataList = new ArrayList<>();
230
}
231
}
232
233
@Override
234
public void doAfterAllAnalysed(AnalysisContext context) {
235
if (!cachedDataList.isEmpty()) {
236
invokeAfterBatch(cachedDataList, context);
237
}
238
doAfterAllPageRead(context);
239
}
240
241
/**
242
* Process a batch of data
243
* @param dataList Batch of data objects
244
* @param context Analysis context
245
*/
246
public abstract void invokeAfterBatch(List<T> dataList, AnalysisContext context);
247
248
/**
249
* Called after all pages have been processed
250
* @param context Analysis context
251
*/
252
public void doAfterAllPageRead(AnalysisContext context) {
253
// Default implementation does nothing
254
}
255
}
256
257
/**
258
* Listener that ignores exceptions and continues reading
259
* @param <T> Type of data object
260
*/
261
public class IgnoreExceptionReadListener<T> implements ReadListener<T> {
262
private final ReadListener<T> delegate;
263
264
/**
265
* Create listener that wraps another listener and ignores exceptions
266
* @param delegate Listener to delegate to
267
*/
268
public IgnoreExceptionReadListener(ReadListener<T> delegate) {
269
this.delegate = delegate;
270
}
271
272
@Override
273
public void invoke(T data, AnalysisContext context) {
274
try {
275
delegate.invoke(data, context);
276
} catch (Exception e) {
277
// Log and ignore the exception
278
System.err.println("Exception ignored: " + e.getMessage());
279
}
280
}
281
282
@Override
283
public void doAfterAllAnalysed(AnalysisContext context) {
284
delegate.doAfterAllAnalysed(context);
285
}
286
287
@Override
288
public void onException(Exception exception, AnalysisContext context) {
289
// Ignore exceptions
290
System.err.println("Exception ignored: " + exception.getMessage());
291
}
292
}
293
```
294
295
### Extra Information Classes
296
297
Classes for handling additional cell information.
298
299
```java { .api }
300
/**
301
* Extra cell information (comments, hyperlinks, merged regions)
302
*/
303
public class CellExtra {
304
/**
305
* Type of extra information
306
*/
307
private CellExtraTypeEnum type;
308
309
/**
310
* Text content (for comments and hyperlinks)
311
*/
312
private String text;
313
314
/**
315
* First row index (0-based)
316
*/
317
private Integer firstRowIndex;
318
319
/**
320
* Last row index (0-based)
321
*/
322
private Integer lastRowIndex;
323
324
/**
325
* First column index (0-based)
326
*/
327
private Integer firstColumnIndex;
328
329
/**
330
* Last column index (0-based)
331
*/
332
private Integer lastColumnIndex;
333
334
// Getters and setters...
335
}
336
337
/**
338
* Types of extra cell information
339
*/
340
public enum CellExtraTypeEnum {
341
/**
342
* Cell comments
343
*/
344
COMMENT,
345
346
/**
347
* Hyperlinks
348
*/
349
HYPERLINK,
350
351
/**
352
* Merged cell regions
353
*/
354
MERGE
355
}
356
```
357
358
### Supporting Interfaces
359
360
Base interfaces for the listener system.
361
362
```java { .api }
363
/**
364
* Base marker interface for all listeners
365
*/
366
public interface Listener {
367
}
368
369
/**
370
* Base marker interface for event handlers
371
*/
372
public interface Handler {
373
}
374
375
/**
376
* Interface for controlling execution order
377
*/
378
public interface Order {
379
/**
380
* Get execution order (lower values execute first)
381
* @return Order value
382
*/
383
int order();
384
}
385
```
386
387
## Usage Examples
388
389
### Basic Custom Listener
390
```java
391
import com.alibaba.excel.context.AnalysisContext;
392
import com.alibaba.excel.event.AnalysisEventListener;
393
394
public class UserDataListener extends AnalysisEventListener<UserData> {
395
private final List<UserData> users = new ArrayList<>();
396
397
@Override
398
public void invoke(UserData user, AnalysisContext context) {
399
// Validate data
400
if (user.getName() != null && user.getEmail() != null) {
401
users.add(user);
402
System.out.println("Processed user: " + user.getName());
403
} else {
404
System.err.println("Invalid user data at row: " + context.getCurrentRowNum());
405
}
406
}
407
408
@Override
409
public void doAfterAllAnalysed(AnalysisContext context) {
410
System.out.println("Analysis completed. Total valid users: " + users.size());
411
412
// Process all users (save to database, etc.)
413
saveUsers(users);
414
}
415
416
@Override
417
public void onException(Exception exception, AnalysisContext context) {
418
System.err.println("Error at row " + context.getCurrentRowNum() +
419
": " + exception.getMessage());
420
// Continue processing instead of stopping
421
}
422
423
private void saveUsers(List<UserData> users) {
424
// Implementation for saving users
425
}
426
}
427
428
// Usage
429
EasyExcel.read("users.xlsx", UserData.class, new UserDataListener())
430
.sheet()
431
.doRead();
432
```
433
434
### Paginated Processing for Large Files
435
```java
436
import com.alibaba.excel.read.listener.PageReadListener;
437
import com.alibaba.excel.context.AnalysisContext;
438
439
public class UserBatchProcessor extends PageReadListener<UserData> {
440
private final UserService userService;
441
442
public UserBatchProcessor(UserService userService) {
443
super(1000); // Process in batches of 1000
444
this.userService = userService;
445
}
446
447
@Override
448
public void invokeAfterBatch(List<UserData> userBatch, AnalysisContext context) {
449
System.out.println("Processing batch of " + userBatch.size() + " users");
450
451
// Process batch (validate, transform, save)
452
List<UserData> validUsers = userBatch.stream()
453
.filter(this::isValid)
454
.collect(Collectors.toList());
455
456
userService.saveBatch(validUsers);
457
458
System.out.println("Saved " + validUsers.size() + " valid users");
459
}
460
461
@Override
462
public void doAfterAllPageRead(AnalysisContext context) {
463
System.out.println("Completed processing all data");
464
userService.finalizeImport();
465
}
466
467
private boolean isValid(UserData user) {
468
return user.getName() != null &&
469
user.getEmail() != null &&
470
user.getEmail().contains("@");
471
}
472
}
473
474
// Usage
475
EasyExcel.read("large-users.xlsx", UserData.class, new UserBatchProcessor(userService))
476
.sheet()
477
.doRead();
478
```
479
480
### Handling Headers and Extra Information
481
```java
482
import com.alibaba.excel.context.AnalysisContext;
483
import com.alibaba.excel.event.AnalysisEventListener;
484
import com.alibaba.excel.metadata.CellExtra;
485
import com.alibaba.excel.metadata.data.ReadCellData;
486
487
public class DetailedDataListener extends AnalysisEventListener<UserData> {
488
private Map<Integer, String> headers = new HashMap<>();
489
private List<String> comments = new ArrayList<>();
490
491
@Override
492
public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
493
System.out.println("Processing headers:");
494
headMap.forEach((index, cellData) -> {
495
String headerName = cellData.getStringValue();
496
headers.put(index, headerName);
497
System.out.println("Column " + index + ": " + headerName);
498
});
499
}
500
501
@Override
502
public void invoke(UserData data, AnalysisContext context) {
503
System.out.println("Row " + context.getCurrentRowNum() + ": " + data);
504
}
505
506
@Override
507
public void extra(CellExtra extra, AnalysisContext context) {
508
switch (extra.getType()) {
509
case COMMENT:
510
comments.add("Comment at (" + extra.getFirstRowIndex() +
511
"," + extra.getFirstColumnIndex() + "): " + extra.getText());
512
break;
513
case HYPERLINK:
514
System.out.println("Hyperlink found: " + extra.getText());
515
break;
516
case MERGE:
517
System.out.println("Merged region: " +
518
extra.getFirstRowIndex() + "-" + extra.getLastRowIndex() +
519
", " + extra.getFirstColumnIndex() + "-" + extra.getLastColumnIndex());
520
break;
521
}
522
}
523
524
@Override
525
public void doAfterAllAnalysed(AnalysisContext context) {
526
System.out.println("Headers processed: " + headers.size());
527
System.out.println("Comments found: " + comments.size());
528
comments.forEach(System.out::println);
529
}
530
}
531
532
// Usage with extra information reading
533
EasyExcel.read("data-with-extras.xlsx", UserData.class, new DetailedDataListener())
534
.extraRead(CellExtraTypeEnum.COMMENT)
535
.extraRead(CellExtraTypeEnum.HYPERLINK)
536
.extraRead(CellExtraTypeEnum.MERGE)
537
.sheet()
538
.doRead();
539
```
540
541
### Error-Tolerant Processing
542
```java
543
import com.alibaba.excel.context.AnalysisContext;
544
import com.alibaba.excel.event.AnalysisEventListener;
545
546
public class RobustDataListener extends AnalysisEventListener<UserData> {
547
private int successCount = 0;
548
private int errorCount = 0;
549
private List<String> errors = new ArrayList<>();
550
551
@Override
552
public void invoke(UserData data, AnalysisContext context) {
553
try {
554
// Process data with potential validation
555
validateAndProcess(data);
556
successCount++;
557
} catch (Exception e) {
558
errorCount++;
559
String error = "Row " + context.getCurrentRowNum() + ": " + e.getMessage();
560
errors.add(error);
561
System.err.println(error);
562
}
563
}
564
565
@Override
566
public void onException(Exception exception, AnalysisContext context) {
567
errorCount++;
568
String error = "Parse error at row " + context.getCurrentRowNum() +
569
": " + exception.getMessage();
570
errors.add(error);
571
System.err.println(error);
572
573
// Continue processing instead of stopping
574
}
575
576
@Override
577
public void doAfterAllAnalysed(AnalysisContext context) {
578
System.out.println("Processing completed:");
579
System.out.println("- Successful rows: " + successCount);
580
System.out.println("- Error rows: " + errorCount);
581
582
if (!errors.isEmpty()) {
583
System.out.println("Errors encountered:");
584
errors.forEach(System.out::println);
585
}
586
}
587
588
private void validateAndProcess(UserData data) {
589
if (data.getName() == null || data.getName().trim().isEmpty()) {
590
throw new IllegalArgumentException("Name is required");
591
}
592
if (data.getEmail() == null || !data.getEmail().contains("@")) {
593
throw new IllegalArgumentException("Valid email is required");
594
}
595
596
// Process valid data
597
saveUser(data);
598
}
599
600
private void saveUser(UserData user) {
601
// Implementation for saving user
602
}
603
}
604
```
605
606
### Conditional Reading Control
607
```java
608
import com.alibaba.excel.context.AnalysisContext;
609
import com.alibaba.excel.event.AnalysisEventListener;
610
611
public class ConditionalReadListener extends AnalysisEventListener<UserData> {
612
private int maxRows = 1000;
613
private int processedRows = 0;
614
private boolean stopReading = false;
615
616
@Override
617
public void invoke(UserData data, AnalysisContext context) {
618
processedRows++;
619
620
// Process data
621
System.out.println("Processing row " + processedRows + ": " + data.getName());
622
623
// Check stopping condition
624
if (processedRows >= maxRows) {
625
System.out.println("Reached maximum rows limit: " + maxRows);
626
stopReading = true;
627
}
628
629
// Could also stop based on data content
630
if ("STOP".equals(data.getName())) {
631
System.out.println("Found stop marker, ending processing");
632
stopReading = true;
633
}
634
}
635
636
@Override
637
public boolean hasNext(AnalysisContext context) {
638
return !stopReading;
639
}
640
641
@Override
642
public void doAfterAllAnalysed(AnalysisContext context) {
643
System.out.println("Processing ended. Total rows processed: " + processedRows);
644
}
645
}
646
```