Generating Dynamic Excel Files in Spring Boot with Apache POI

Introduction

Implementing data export functionality is a common requirement in enterprise Java applications. By leveraging the Apache POI library, developers can generate Excel spreadsheets programmatically. This approach allows for dynamic column mapping using Java annotations and reflection, ensuring that the spreadsheet structure remains synchronized with the underlying data models.

Maven Dependencies

To begin, include the Apache POI dependency in your pom.xml file. This library provides the necessary APIs to manipulate Microsoft Office documents.

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
</dependency>

Custom Annotation for Column Headers

We define a custom annotation to mark fields within our POJOs (Plain Old Java Objects). This allows the utility class to retrieve the display name for each column automatically during runtime.

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ColumnHeader {
    String name();
}

Excel Generation Utility

The following utility class handles the workbook creation. It uses variable arguments to accept multiple lists of data, supporting the export of different data types into a single sheet if necessary. The implementation utilizes reflection to extract values from the object fields.

import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

public class SpreadsheetBuilder {

    /**
     * Generates an HSSFWorkbook based on provided headers and data collections.
     *
     * @param tabName   The name of the Excel sheet.
     * @param headers   List of column headers.
     * @param dataLists Variable arguments containing lists of data objects.
     * @return A configured HSSFWorkbook.
     */
    public static HSSFWorkbook buildWorkbook(String tabName, List<String> headers, List<?>... dataLists) 
            throws IllegalAccessException {
        
        HSSFWorkbook workbook = new HSSFWorkbook();
        HSSFSheet sheet = workbook.createSheet(tabName);
        HSSFRow titleRow = sheet.createRow(0);

        // Write Column Headers
        for (int i = 0; i < headers.size(); i++) {
            titleRow.createCell(i).setCellValue(headers.get(i));
        }

        int rowIndex = 1;
        
        // Iterate through all provided data lists
        for (List<?> currentList : dataLists) {
            for (Object entity : currentList) {
                HSSFRow dataRow = sheet.createRow(rowIndex++);
                Class<?> clazz = entity.getClass();
                Field[] fields = clazz.getDeclaredFields();

                for (int i = 0; i < fields.length; i++) {
                    fields[i].setAccessible(true);
                    Object value = fields[i].get(entity);
                    
                    if (value != null) {
                        if (value instanceof Date) {
                            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                            dataRow.createCell(i).setCellValue(dateFormat.format(value));
                        } else {
                            dataRow.createCell(i).setCellValue(String.valueOf(value));
                        }
                    }
                }
            }
        }
        return workbook;
    }
}

Data Model Definition

The entity class below demonstrates the usage of the @ColumnHeader annotation. The fields represent the data columns to be exported.

public class UserRecord {
    @ColumnHeader(name = "User ID")
    private Long userId;

    @ColumnHeader(name = "Full Name")
    private String fullName;

    @ColumnHeader(name = "Comments")
    private String notes;

    // Constructors, Getters, and Setters
    public UserRecord() {}

    public Long getUserId() { return userId; }
    public void setUserId(Long userId) { this.userId = userId; }

    public String getFullName() { return fullName; }
    public void setFullName(String fullName) { this.fullName = fullName; }

    public String getNotes() { return notes; }
    public void setNotes(String notes) { this.notes = notes; }
}

Controller Endpoint Implementation

The Spring Boot controller method below handles the HTTP request. It sets the appropriate response headers to trigger a file download in the browser. It also dynamically extracts the header values from the UserRecord class annotations before generating the file.

import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

@RestController
public class DataExportController {

    @GetMapping("/export-users")
    public void exportUserList(HttpServletResponse response) {
        try {
            OutputStream outputStream = response.getOutputStream();
            String filename = "User_List_Report";
            
            // Configure response headers
            response.reset();
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-disposition", 
                    "attachment; filename=" + new String(filename.getBytes("GB2312"), "ISO8859-1") + ".xls");

            // 1. Dynamically extract headers using Reflection
            List<String> headers = new ArrayList<>();
            Field[] fields = UserRecord.class.getDeclaredFields();
            for (Field field : fields) {
                ColumnHeader annotation = field.getAnnotation(ColumnHeader.class);
                if (annotation != null) {
                    headers.add(annotation.name());
                }
            }

            // 2. Prepare Mock Data
            List<UserRecord> primaryData = new ArrayList<>();
            UserRecord userOne = new UserRecord();
            userOne.setUserId(1001L);
            userOne.setFullName("Alice Smith");
            userOne.setNotes("Administrator");
            primaryData.add(userOne);

            UserRecord userTwo = new UserRecord();
            userTwo.setUserId(1002L);
            userTwo.setFullName("Bob Jones");
            userTwo.setNotes("Standard User");
            primaryData.add(userTwo);

            // 3. Generate and Write Workbook
            HSSFWorkbook workbook = SpreadsheetBuilder.buildWorkbook("UserData", headers, primaryData);
            workbook.write(outputStream);
            
            outputStream.close();
            workbook.close();

        } catch (IOException e) {
            // Handle logging appropriately
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

Tags: java spring-boot apache-poi excel-export reflection

Posted on Tue, 28 Jul 2026 17:14:55 +0000 by Darkmatter5