SpringBoot集成JasperReports报表引擎原理与实践

作者:技术拾光

时间:2026-07-27

浏览:403

JasperReports是Java生态中流行的开源报表引擎,支持PDF、Excel、Word等格式输出。通过JRXML模板实现数据与样式分离,编译后运行时填充数据生成报表。在SpringBoot中集成,利用JasperFillManager填充数据源,JasperExportManager导出文档,实现高效报表生成。

文章目录

在Java报表开发领域,JasperReports报表引擎是开发者绕不开的核心工具。本文将从多个维度深入解析JasperReports的功能定位、使用方法以及在实际项目中的落地实践,帮助Java开发者快速掌握这一开源报表工具。

一、JasperReports报表引擎概述

1.1 JasperReports定位

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

SpringBoot集成JasperReports报表引擎原理与实践

1.2 JasperReports类比理解

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

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

1.3 JasperReports典型应用场景

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

二、JasperReports核心概念

2.1 JasperReports关键术语

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

2.2 JasperReports报表结构

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

三、JasperReports工作流程

3.1 JasperReports完整生命周期

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

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

3.2 JasperReports运行时数据流

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

四、JasperReports涉及的技术知识点

4.1 JasperReports模板设计

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

4.2 JasperReports数据源类型

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

4.3 JasperReports导出格式

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

4.4 JasperReports设计模式

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

五、JasperReports通用示例代码

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 模板示例(发货单)



    
    
    
    
    
    
    
    
    
    
    
    
    
        
            <![CDATA[$F{price}.multiply(new java.math.BigDecimal($F{quantity}))]]>
        
    
    
    
        <band height="80">
            <statictext>
                <reportelement x="0" y="0" width="555" height="30"></reportelement>
                <textelement textalignment="Center">
                    <font size="18" isbold="true" fontname="华文宋体"></font>
                </textelement>
                <text><![CDATA[发 货 单]]></text>
            </statictext>
            <textfield>
                <reportelement x="0" y="40" width="200" height="20"></reportelement>
                <textfieldexpression><![CDATA["单号:" + $P{deliveryCode}]]></textfieldexpression>
            </textfield>
            <textfield>
                <reportelement x="355" y="40" width="200" height="20"></reportelement>
                <textfieldexpression><![CDATA["日期:" + $P{printDate}]]></textfieldexpression>
            </textfield>
        </band>
    
    
    
        
            
                
                <![CDATA[商品编码]]>
            
            
                
                <![CDATA[商品名称]]>
            
            
                
                <![CDATA[数量]]>
            
            
                
                <![CDATA[单价]]>
            
        
    
    
    
        
            
                
                <![CDATA[$F{productCode}]]>
            
            
                
                <![CDATA[$F{productName}]]>
            
            
                
                <![CDATA[$F{quantity}]]>
            
            
                
                <![CDATA[$F{price}]]>
            
        
    
    
    
        
            
                
                
                    
                
                <![CDATA["合计金额:" + $V{totalAmount}]]>
            
        
    

5.3 JasperUtil 工具类封装

package com.example.utils;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import javax.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=" + java.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}
}

六、JasperReports开发流程

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 配置

七、JasperReports关键设计总结

设计要点实现方式收益
数据与模板分离.jasper 模板 + List 数据修改样式不改代码,修改逻辑不改模板
预编译模板.jrxml → .jasper(开发时编译)运行时直接加载,无需编译开销
多格式输出同一模板导出 PDF/Excel/Word/HTML一次设计,多种输出
批量合并多个 JasperPrint 合并为一个 PDF批量打印只需下载一个文件
中文支持嵌入字体文件到项目中避免服务器字体缺失导致中文乱码
参数化$P{paramName} 动态传入同一模板适配不同场景(换标题/Logo/签章)
子报表嵌套Subreport 组件主从报表(如订单主信息 + 商品明细)
表达式计算$V{variable} + calculation=Sum自动计算合计/平均/计数

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章