商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > SpringBoot使用JasperReports报表引擎的原理与使用实践

SpringBoot使用JasperReports报表引擎的原理与使用实践

  发布于2026-07-26 阅读(0)

扫一扫,手机访问

在Ja va报表领域,JasperReports是一个绕不开的名字。下面从几个方面来深入解析,看看它到底是什么、怎么用,以及如何在实际项目中落地。

一、JasperReports 是什么

1.1 定位

JasperReports 是 Ja va 生态中最流行的开源报表引擎,专门用于生成格式化的文档(PDF、Excel、Word、HTML、CSV 等)。它的核心价值是将数据模板分离,通过模板定义报表样式,运行时填充数据后生成最终文档。

SpringBoot使用JasperReports报表引擎的原理与使用实践

1.2 类比理解

Word 邮件合并:
  模板(.docx)+ 数据(Excel联系人) → 批量生成信件

JasperReports:
  模板(.jrxml/.jasper)+ 数据(数据库/Ja va对象) → 批量生成 PDF/Excel/HTML

1.3 典型应用场景

场景示例
发货单打印选中订单 → 生成 PDF 发货单(含条码/表格/签章)
财务报表月度销售汇总 → 导出 Excel
物流面单快递单/运单 → 生成固定格式 PDF 批量打印
库存盘点表仓库商品清单 → 导出打印
对账单供应商对账 → 生成 PDF 发送邮件

二、核心概念

2.1 关键术语

概念说明文件格式
JRXML报表模板源文件,XML 格式,定义报表布局和样式.jrxml
Jasper编译后的模板文件(二进制),运行时直接加载.jasper
JasperPrint填充数据后的内存报表对象,可导出为各种格式内存对象
DataSource数据源,为报表提供数据(JDBC/Ja va集合/JSON等)-
Parameter报表参数,从外部传入的变量(如标题、日期、Logo等)-
Field数据字段,对应数据源中每条记录的列-
Band报表区域/带区(页眉/列头/明细/页脚/汇总等)-

2.2 报表结构

┌─────────────────────────────────────────┐
│          Title Band(标题区)             │  ← 整个报表只出现一次
├─────────────────────────────────────────┤
│        Page Header(页眉)               │  ← 每页顶部
├─────────────────────────────────────────┤
│       Column Header(列标题)            │  ← 表格列名
├─────────────────────────────────────────┤
│         Detail Band(明细区)            │  ← 每条数据重复一次
│    ┌────┬──────┬────┬──────┬─────┐     │
│    │序号│ 商品名 │数量│  单价  │ 金额 │     │
│    ├────┼──────┼────┼──────┼─────┤     │
│    │ 1  │冰箱   │ 2  │3999  │7998 │     │
│    │ 2  │洗衣机 │ 1  │2999  │2999 │     │
│    │... │...   │... │...   │...  │     │
│    └────┴──────┴────┴──────┴─────┘     │
├─────────────────────────────────────────┤
│        Column Footer(列汇总)           │  ← 合计行
├─────────────────────────────────────────┤
│        Page Footer(页脚)               │  ← 每页底部(页码等)
├─────────────────────────────────────────┤
│         Summary(总汇总)                │  ← 整个报表最后
└─────────────────────────────────────────┘

三、工作流程

3.1 完整生命周期

设计阶段(开发时):
  Jaspersoft Studio 设计模板 → 保存为 .jrxml 文件
       │
       ▼
  编译模板:JasperCompileManager.compileReport() → .jasper 文件
       │
       ▼(部署到项目 resources 目录)

运行阶段(运行时):
  加载 .jasper 模板
       │
       ▼
  填充数据:JasperFillManager.fillReport(模板, 参数, 数据源) → JasperPrint
       │
       ▼
  导出文档:JasperExportManager.exportReportToPdf(jasperPrint) → PDF/Excel/HTML
       │
       ▼
  返回给前端下载/预览/打印

3.2 运行时数据流

Controller 接收请求(如:打印发货单)
  │
  ├─→ Service 查询数据库获取发货单数据
  │     └── List 数据集
  │
  ├─→ 组装参数 Map(标题、公司名、打印日期等)
  │
  ├─→ 创建数据源 JRBeanCollectionDataSource(数据集)
  │
  ├─→ 加载模板 .jasper(从 classpath 或数据库)
  │
  ├─→ JasperFillManager.fillReport(模板, 参数, 数据源)
  │     └── → JasperPrint(内存中的完整报表)
  │
  ├─→ 导出为目标格式
  │     ├── PDF:JasperExportManager.exportReportToPdfStream()
  │     ├── Excel:JRXlsxExporter
  │     ├── Word:JRDocxExporter
  │     └── HTML:HtmlExporter
  │
  └─→ 写入 HttpServletResponse 输出流
       └── 前端收到文件下载/预览

四、涉及的技术知识点

4.1 模板设计

知识点说明
Jaspersoft StudioEclipse 插件形式的可视化模板设计器(拖拽式)
JRXML 语法XML 格式的模板描述语言
表达式语言$F{fieldName}(字段)、$P{paramName}(参数)、$V{varName}(变量)
子报表(Subreport)报表嵌套,主报表中嵌入子报表
条件样式根据数据值动态改变颜色/字体/可见性
条码/二维码内置 Barcode4J 支持各种条码格式
图表内置 JFreeChart 支持柱状图/折线图/饼图

4.2 数据源类型

数据源场景
Ja va Bean 集合JRBeanCollectionDataSource最常用,传入 List
JDBC 直连JRResultSetDataSource直接执行 SQL
空数据源JREmptyDataSource只有参数没有明细数据
Map 集合JRMapCollectionDataSourceList数据
JSONJsonDataSourceJSON 字符串/文件

4.3 导出格式

格式导出器类用途
PDFJasperExportManager打印、归档
Excel (xlsx)JRXlsxExporter数据分析
Word (docx)JRDocxExporter文档编辑
HTMLHtmlExporter在线预览
CSVJRCsvExporter数据交换
图片 (PNG)JRGraphics2DExporter缩略图

4.4 设计模式

模式应用
模板方法编译 → 填充 → 导出的固定流程
策略模式不同 Exporter 实现不同格式导出
建造者模式ExporterInput/OutputItem 的构建
工厂模式JasperCompileManager/JasperFillManager 工厂方法

五、通用示例代码

5.1 pom.xml 依赖


    
    
        net.sf.jasperreports
        jasperreports
        6.20.0
    
    
    
        net.sf.jasperreports
        jasperreports-fonts
        6.20.0
    
    
    
        org.apache.poi
        poi-ooxml
        5.2.3
    

5.2 JRXML 模板示例(发货单)



    
    
    
    
    
    
    
    
    
    
    
    
    
        
            
        
    
    
    
        <band height="80">
            <staticText>
                <reportElement x="0" y="0" width="555" height="30"/>
                <textElement textAlignment="Center">
                    <font size="18" isBold="true" fontName="华文宋体"/>
                </textElement>
                <text><![CDATA[发 货 单]]></text>
            </staticText>
            <textField>
                <reportElement x="0" y="40" width="200" height="20"/>
                <textFieldExpression><![CDATA["单号:" + $P{deliveryCode}]]></textFieldExpression>
            </textField>
            <textField>
                <reportElement x="355" y="40" width="200" height="20"/>
                <textFieldExpression><![CDATA["日期:" + $P{printDate}]]></textFieldExpression>
            </textField>
        </band>
    
    
    
        
            
                
                
            
            
                
                
            
            
                
                
            
            
                
                
            
        
    
    
    
        
            
                
                
            
            
                
                
            
            
                
                
            
            
                
                
            
        
    
    
    
        
            
                
                
                    
                
                
            
        
    

5.3 JasperUtil 工具类封装

package com.example.utils;

import ja va.io.ByteArrayOutputStream;
import ja va.io.InputStream;
import ja va.util.List;
import ja va.util.Map;
import ja vax.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.export.ooxml.JRDocxExporter;
import net.sf.jasperreports.engine.export.ooxml.JRXlsxExporter;
import net.sf.jasperreports.export.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * JasperReports 报表工具类.
 * 封装编译、填充、导出的完整流程.
 */
public class JasperUtil {

    private static final Logger log = LoggerFactory.getLogger(JasperUtil.class);

    /**
     * 导出类型枚举.
     */
    public enum DocType {
        PDF, EXCEL, WORD, HTML
    }

    /**
     * 生成报表并写入 HTTP 响应(文件下载).
     *
     * @param templatePath 模板路径(classpath 下的 .jasper 文件)
     * @param params       报表参数
     * @param dataList     数据集合(对应模板中的 Field)
     * @param fileName     下载文件名
     * @param docType      导出格式
     * @param response     HTTP 响应
     */
    public static  void exportToResponse(
            String templatePath,
            Map params,
            List dataList,
            String fileName,
            DocType docType,
            HttpServletResponse response) {

        try {
            byte[] bytes = generateReport(templatePath, params, dataList, docType);

            // 设置响应头
            String contentType;
            String extension;
            switch (docType) {
                case PDF:
                    contentType = "application/pdf";
                    extension = ".pdf";
                    break;
                case EXCEL:
                    contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                    extension = ".xlsx";
                    break;
                case WORD:
                    contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
                    extension = ".docx";
                    break;
                default:
                    contentType = "text/html";
                    extension = ".html";
            }

            response.setContentType(contentType);
            response.setHeader("Content-Disposition",
                "attachment; filename=" + ja va.net.URLEncoder.encode(fileName + extension, "UTF-8"));
            response.setContentLength(bytes.length);
            response.getOutputStream().write(bytes);
            response.getOutputStream().flush();

        } catch (Exception e) {
            log.error("报表导出失败", e);
            throw new RuntimeException("报表导出失败", e);
        }
    }

    /**
     * 生成报表字节数组.
     */
    public static  byte[] generateReport(
            String templatePath,
            Map params,
            List dataList,
            DocType docType) throws Exception {

        // 1. 加载编译好的模板
        InputStream templateStream = JasperUtil.class.getClassLoader()
            .getResourceAsStream(templatePath);
        JasperReport jasperReport = (JasperReport) JRLoader.loadObject(templateStream);

        // 2. 创建数据源
        JRDataSource dataSource;
        if (dataList != null && !dataList.isEmpty()) {
            dataSource = new JRBeanCollectionDataSource(dataList);
        } else {
            dataSource = new JREmptyDataSource();
        }

        // 3. 填充数据 → 生成 JasperPrint
        JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, params, dataSource);

        // 4. 导出为目标格式
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        switch (docType) {
            case PDF:
                JasperExportManager.exportReportToPdfStream(jasperPrint, outputStream);
                break;
            case EXCEL:
                exportToExcel(jasperPrint, outputStream);
                break;
            case WORD:
                exportToWord(jasperPrint, outputStream);
                break;
            default:
                JasperExportManager.exportReportToHtmlFile(jasperPrint, outputStream.toString());
        }
        return outputStream.toByteArray();
    }

    private static void exportToExcel(JasperPrint jasperPrint, ByteArrayOutputStream out) throws Exception {
        JRXlsxExporter exporter = new JRXlsxExporter();
        exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
        exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(out));
        SimpleXlsxReportConfiguration config = new SimpleXlsxReportConfiguration();
        config.setOnePagePerSheet(false);
        config.setDetectCellType(true);
        exporter.setConfiguration(config);
        exporter.exportReport();
    }

    private static void exportToWord(JasperPrint jasperPrint, ByteArrayOutputStream out) throws Exception {
        JRDocxExporter exporter = new JRDocxExporter();
        exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
        exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(out));
        exporter.exportReport();
    }
}

5.4 业务代码使用示例

@RestController
public class DeliveryPrintController {

    @Resource
    private DeliveryService deliveryService;

    /**
     * 打印发货单(导出PDF).
     */
    @GetMapping("/api/delivery/print")
    public void printDeliveryOrder(
            @RequestParam Integer deliveryId,
            HttpServletResponse response) {

        // 1. 查询发货单数据
        DeliveryOrderDto order = deliveryService.getDeliveryOrder(deliveryId);
        List items = deliveryService.getDeliveryItems(deliveryId);

        // 2. 组装报表参数
        Map params = new HashMap<>();
        params.put("companyName", "xxx科技");
        params.put("deliveryCode", order.getDeliveryCode());
        params.put("printDate", DateUtil.formatStandardDate(new Date()));
        params.put("customerName", order.getCustomerName());
        params.put("address", order.getShipToAddress());

        // 3. 导出 PDF
        JasperUtil.exportToResponse(
            "print/delivery_order.jasper",  // classpath 下的模板
            params,
            items,                           // 明细数据
            "发货单_" + order.getDeliveryCode(),
            JasperUtil.DocType.PDF,
            response);
    }

    /**
     * 批量打印(多个发货单合并为一个PDF).
     */
    @PostMapping("/api/delivery/batch-print")
    public void batchPrint(
            @RequestBody List deliveryIds,
            HttpServletResponse response) {

        List prints = new ArrayList<>();
        for (Integer id : deliveryIds) {
            DeliveryOrderDto order = deliveryService.getDeliveryOrder(id);
            List items = deliveryService.getDeliveryItems(id);

            Map params = new HashMap<>();
            params.put("deliveryCode", order.getDeliveryCode());
            // ... 其他参数

            // 生成每个发货单的 JasperPrint
            InputStream template = getClass().getClassLoader()
                .getResourceAsStream("print/delivery_order.jasper");
            JasperReport report = (JasperReport) JRLoader.loadObject(template);
            JasperPrint print = JasperFillManager.fillReport(report, params,
                new JRBeanCollectionDataSource(items));
            prints.add(print);
        }

        // 合并导出为一个 PDF
        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", "attachment; filename=batch_delivery.pdf");
        JRPdfExporter exporter = new JRPdfExporter();
        exporter.setExporterInput(SimpleExporterInput.getInstance(prints));
        exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(response.getOutputStream()));
        exporter.exportReport();
    }
}

5.5 模板 DTO 示例

/**
 * 发货单明细DTO(字段名需与JRXML中的Field名称一致).
 */
@Data
public class DeliveryItemDto {
    private String productCode;    // 对应 $F{productCode}
    private String productName;    // 对应 $F{productName}
    private Integer quantity;      // 对应 $F{quantity}
    private BigDecimal price;      // 对应 $F{price}
}

六、开发流程

6.1 模板设计(使用 Jaspersoft Studio)

1. 下载安装 Jaspersoft Studio(免费,基于 Eclipse)
2. 新建 Jasper Report → 选择模板尺寸(A4/自定义)
3. 拖拽组件到 Band 中:
   - Static Text:固定文本
   - Text Field:动态字段 $F{xxx}
   - Image:图片/Logo
   - Barcode:条码
   - Line/Rectangle:线条/边框
4. 定义 Parameters(外部传入的参数)
5. 定义 Fields(对应数据源的字段)
6. 预览效果 → 保存为 .jrxml
7. 编译为 .jasper → 放到项目 resources/print/ 目录

6.2 项目中的文件组织

src/main/resources/
├── print/
│   ├── delivery_order.jrxml      ← 模板源文件(用于修改)
│   ├── delivery_order.jasper     ← 编译后模板(运行时加载)
│   ├── outbound_order.jasper     ← 出库单模板
│   └── stock_report.jasper       ← 库存报表模板
├── font/
│   └── simsun.ttf                ← 中文字体文件
└── jasperreports.properties       ← JasperReports 配置

七、关键设计总结

设计要点实现方式收益
数据与模板分离.jasper 模板 + List 数据修改样式不改代码,修改逻辑不改模板
预编译模板.jrxml → .jasper(开发时编译)运行时直接加载,无需编译开销
多格式输出同一模板导出 PDF/Excel/Word/HTML一次设计,多种输出
批量合并多个 JasperPrint 合并为一个 PDF批量打印只需下载一个文件
中文支持嵌入字体文件到项目中避免服务器字体缺失导致中文乱码
参数化$P{paramName} 动态传入同一模板适配不同场景(换标题/Logo/签章)
子报表嵌套Subreport 组件主从报表(如订单主信息 + 商品明细)
表达式计算$V{variable} + calculation=Sum自动计算合计/平均/计数
本文转载于:https://www.jb51.net/program/368075tgt.htm 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。
  • using namespace 使用中遇到的问题怎么解决 正版软件
    using namespace 使用中遇到的问题怎么解决
    命名空间的基本概念与常见引入问题在C++等编程语言中,命名空间(namespace)是一种将代码标识符(如变量、函数、类名)封装在特定名称下的机制,其主要目的是避免命名冲突,尤其是在大型项目或使用多个第三方库时。使用“using namespace”指令可以将指定命名空间中的所有名称引入当前作用域,
    8天前 0
  • c语言函数递归 实操经验总结:这些技巧很实用 正版软件
    c语言函数递归 实操经验总结:这些技巧很实用
    理解递归的基本原理在C语言中,递归是一种函数调用自身的编程技术。要掌握它,首先需要理解其核心思想:将一个复杂的大问题,分解为一个或几个与原问题相似但规模更小的子问题,直到子问题足够简单,可以直接求解。这个过程通常包含两个关键部分:递归出口和递归体。递归出口定义了问题何时不再继续分解,即最简单、可直接
    8天前 0
  • c语言函数递归 怎么选?常见方案对比分析 正版软件
    c语言函数递归 怎么选?常见方案对比分析
    递归函数的基本概念与适用场景在C语言编程中,递归是一种函数调用自身的编程技巧。它并非适用于所有问题,但在处理某些具有自相似结构的问题时,能提供极其清晰和优雅的解决方案。递归的核心思想是将一个大规模问题分解为一个或多个同类型但规模更小的子问题,直到子问题简单到可以直接求解。典型的适用场景包括树形结构的
    8天前 0
  • Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解 正版软件
    Objective-C 内存管理入门:从 alloc 到 dealloc 的生命周期详解
    理解内存管理的基石在Objective-C的编程世界中,内存管理是开发者必须掌握的核心技能之一。它直接关系到应用的性能、稳定性与资源利用效率。与一些采用自动垃圾回收机制的语言不同,Objective-C在很长一段时间里,依赖一套基于引用计数的、需要开发者部分介入的管理规则。这套规则的核心思想是明确的
    8天前 0
  • 如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏 正版软件
    如何正确使用 dealloc 以避免 iOS 应用中的内存泄漏
    理解 dealloc 的角色与时机在 iOS 应用开发中,内存管理是保障应用性能与稳定性的基石。dealloc 方法是 Objective-C 中对象生命周期结束时的关键回调,它标志着对象即将被系统回收内存。正确理解其触发时机至关重要:当一个对象的引用计数降为零时,运行时系统会自动调用该对象的 de
    8天前 0