Backend Development 12 min read

Implementing a Fixed‑Size FIFO Export Queue in a Java Backend with Spring and EasyExcel

This article explains how to design and implement a fixed-size FIFO export queue in a Java backend using Spring components and EasyExcel, covering business requirements, class definitions, code examples, testing results, and considerations for further enhancements such as Redis queues and high‑concurrency scenarios.

Architecture Digest
Architecture Digest
Architecture Digest
Implementing a Fixed‑Size FIFO Export Queue in a Java Backend with Spring and EasyExcel

The business requirement is to limit simultaneous full‑data exports from a growing MySQL database, because concurrent export operations cause heavy I/O and can degrade server performance. To mitigate this, a fixed‑size FIFO queue is introduced so that export requests are processed sequentially.

Three main entities are defined: ExportQueue – a bounded queue that stores pending export users and provides methods to add users and retrieve the next user; User information – represents the user who initiates an export; and Export class – encapsulates the export logic, supports asynchronous execution, and allows users to download the generated files.

package com.example.system.config; import com.example.system.api.domain.ExportUser; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import java.util.LinkedList; @Slf4j @Component public class ExportQueue { private final int MAX_CAPACITY = 10; // queue capacity private LinkedList<ExportUser> queue; public ExportQueue() { this.queue = new LinkedList<>(); } public synchronized LinkedList<ExportUser> add(ExportUser sysUser) { while (queue.size() >= MAX_CAPACITY) { try { log.info("Queue full, waiting..."); wait(); } catch (InterruptedException e) { e.getMessage(); } } queue.add(sysUser); log.info("Current queue size: " + queue.size()); notifyAll(); return queue; } public synchronized ExportUser getNextSysUser() { while (queue.isEmpty()) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } ExportUser sysUser = queue.remove(); notifyAll(); return sysUser; } }

The abstract export class leverages EasyExcel to write large data sets to Excel files in a paginated manner, handling pagination, file naming, and response streaming.

package com.example.system.config; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.util.PageUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.write.metadata.WriteSheet; import com.example.system.api.domain.ExportUser; import lombok.extern.slf4j.Slf4j; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URLEncoder; import java.util.List; @Slf4j public abstract class AbstractExport<T, K> { public abstract void export(ExportUser sysUser) throws InterruptedException; public void export(HttpServletResponse response, int pageSize, T t, Class<K> k, String fileName) throws Exception { ExcelWriter writer = null; try { writer = getExcelWriter(response, fileName); int total = this.countExport(t); int loopCount = PageUtil.totalPage(total, pageSize); BeanUtil.setProperty(t, "pageSize", pageSize); for (int i = 0; i < loopCount; i++) { BeanUtil.setProperty(t, "pageNum", PageUtil.getStart(i + 1, pageSize)); List<K> kList = this.getExportDetail(t); WriteSheet writeSheet = EasyExcel.writerSheet(fileName).head(k).build(); writer.write(kList, writeSheet); } } catch (Exception e) { String msg = "Export " + fileName + " error"; log.error(msg, e); throw new Exception(msg + e); } finally { if (writer != null) writer.finish(); } } public ExcelWriter getExcelWriter(HttpServletResponse response, String fileName) throws IOException { response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); String fileNameUtf = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileNameUtf + ".xlsx"); return EasyExcel.write(response.getOutputStream()).build(); } public abstract void complexFillWithTable(T t, String fileName, HttpServletResponse response); public abstract int countExport(T t); public abstract List<K> getExportDetail(T t); }

The concrete implementation ExportImpl uses the queue to control export execution, simulates processing time, and logs queue status.

package com.example.system.service.impl; import com.alibaba.excel.ExcelWriter; import com.example.system.api.domain.ExportUser; import com.example.system.config.AbstractExport; import com.example.system.config.ExportQueue; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.servlet.http.HttpServletResponse; import java.util.LinkedList; import java.util.List; @Service @Slf4j public class ExportImpl extends AbstractExport { @Autowired private ExportQueue exportQueue; @Override public void export(ExportUser sysUser) throws InterruptedException { LinkedList<ExportUser> queue = exportQueue.add(sysUser); log.info("Export queue: " + queue); Thread.sleep(20000); // simulate export time ExportUser nextSysUser = exportQueue.getNextSysUser(); log.info("Next user after removal: " + nextSysUser.getUserName()); } // other overridden methods delegate to super }

A simple controller launches export tasks in separate threads to demonstrate concurrent request handling.

package com.example.system.controller; import com.example.system.api.domain.ExportUser; import com.example.system.service.impl.ExportImpl; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/export") @Slf4j public class ExportController { @Autowired private ExportImpl export; @PostMapping("/exportFile") public void exportFile() { new Thread(() -> { @SneakyThrows public void run() { ExportUser sysUser = new ExportUser(); sysUser.setUserName(Thread.currentThread().getName()); export.export(sysUser); } }).start(); } }

Testing shows that the queue limits concurrent exports to ten; when the limit is reached, additional requests wait until a slot frees up. Screenshots (omitted here) illustrate the queue size before and after processing.

The article concludes that while the presented FIFO queue works, other solutions such as a Redis‑backed queue could handle higher concurrency and persistence, and further work is needed for file storage, OSS upload, and download features.

Additionally, the post contains promotional material offering a free book download and links to other technical articles, but the core tutorial remains a practical guide for backend export queue implementation.

backendJavaSpringEasyExcelQueueExport
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

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.