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

 更新时间:2026年07月26日 13:47:40   作者:霸道流氓气质  
还在为Java报表生成头疼,本文带你快速掌握JasperReports核心功能,从JRXML模板设计到数据填充,轻松实现PDF、Excel等多格式导出,解决中文乱码、批量打印等常见难题,附完整示例代码,需要的朋友可以参考下

一、JasperReports 是什么

1.1 定位

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

1.2 类比理解

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

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

1.3 典型应用场景

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

二、核心概念

2.1 关键术语

概念说明文件格式
JRXML报表模板源文件,XML 格式,定义报表布局和样式.jrxml
Jasper编译后的模板文件(二进制),运行时直接加载.jasper
JasperPrint填充数据后的内存报表对象,可导出为各种格式内存对象
DataSource数据源,为报表提供数据(JDBC/Java集合/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<DeliveryOrderDto> 数据集
  │
  ├─→ 组装参数 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 数据源类型

数据源场景
Java 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 依赖

<dependencies>
    <!-- JasperReports 核心 -->
    <dependency>
        <groupId>net.sf.jasperreports</groupId>
        <artifactId>jasperreports</artifactId>
        <version>6.20.0</version>
    </dependency>
    <!-- 中文字体支持 -->
    <dependency>
        <groupId>net.sf.jasperreports</groupId>
        <artifactId>jasperreports-fonts</artifactId>
        <version>6.20.0</version>
    </dependency>
    <!-- Excel 导出 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>5.2.3</version>
    </dependency>
</dependencies>

5.2 JRXML 模板示例(发货单)

<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports
              http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
              name="delivery_order" pageWidth="595" pageHeight="842"
              columnWidth="555" leftMargin="20" rightMargin="20"
              topMargin="20" bottomMargin="20">
    <!-- 参数定义 -->
    <parameter name="companyName" class="java.lang.String"/>
    <parameter name="deliveryCode" class="java.lang.String"/>
    <parameter name="printDate" class="java.lang.String"/>
    <parameter name="customerName" class="java.lang.String"/>
    <parameter name="address" class="java.lang.String"/>
    <!-- 字段定义(对应 Java Bean 属性) -->
    <field name="productCode" class="java.lang.String"/>
    <field name="productName" class="java.lang.String"/>
    <field name="quantity" class="java.lang.Integer"/>
    <field name="price" class="java.math.BigDecimal"/>
    <!-- 变量定义(自动计算) -->
    <variable name="totalAmount" class="java.math.BigDecimal" calculation="Sum">
        <variableExpression>
            <![CDATA[$F{price}.multiply(new java.math.BigDecimal($F{quantity}))]]>
        </variableExpression>
    </variable>
    <!-- 标题区 -->
    <title>
        <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>
    </title>
    <!-- 列标题 -->
    <columnHeader>
        <band height="25">
            <staticText>
                <reportElement x="0" y="0" width="100" height="25" mode="Opaque" backcolor="#CCCCCC"/>
                <text><![CDATA[商品编码]]></text>
            </staticText>
            <staticText>
                <reportElement x="100" y="0" width="200" height="25" mode="Opaque" backcolor="#CCCCCC"/>
                <text><![CDATA[商品名称]]></text>
            </staticText>
            <staticText>
                <reportElement x="300" y="0" width="80" height="25" mode="Opaque" backcolor="#CCCCCC"/>
                <text><![CDATA[数量]]></text>
            </staticText>
            <staticText>
                <reportElement x="380" y="0" width="80" height="25" mode="Opaque" backcolor="#CCCCCC"/>
                <text><![CDATA[单价]]></text>
            </staticText>
        </band>
    </columnHeader>
    <!-- 明细区(每条数据重复) -->
    <detail>
        <band height="20">
            <textField>
                <reportElement x="0" y="0" width="100" height="20"/>
                <textFieldExpression><![CDATA[$F{productCode}]]></textFieldExpression>
            </textField>
            <textField>
                <reportElement x="100" y="0" width="200" height="20"/>
                <textFieldExpression><![CDATA[$F{productName}]]></textFieldExpression>
            </textField>
            <textField>
                <reportElement x="300" y="0" width="80" height="20"/>
                <textFieldExpression><![CDATA[$F{quantity}]]></textFieldExpression>
            </textField>
            <textField>
                <reportElement x="380" y="0" width="80" height="20"/>
                <textFieldExpression><![CDATA[$F{price}]]></textFieldExpression>
            </textField>
        </band>
    </detail>
    <!-- 汇总区 -->
    <summary>
        <band height="30">
            <textField>
                <reportElement x="300" y="5" width="180" height="20"/>
                <textElement textAlignment="Right">
                    <font isBold="true"/>
                </textElement>
                <textFieldExpression><![CDATA["合计金额:" + $V{totalAmount}]]></textFieldExpression>
            </textField>
        </band>
    </summary>
</jasperReport>

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 <T> void exportToResponse(
            String templatePath,
            Map<String, Object> params,
            List<T> 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 <T> byte[] generateReport(
            String templatePath,
            Map<String, Object> params,
            List<T> 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<DeliveryItemDto> items = deliveryService.getDeliveryItems(deliveryId);

        // 2. 组装报表参数
        Map<String, Object> 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<Integer> deliveryIds,
            HttpServletResponse response) {

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

            Map<String, Object> 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自动计算合计/平均/计数

以上就是SpringBoot使用JasperReports报表引擎的原理与使用实践的详细内容,更多关于SpringBoot使用JasperReports报表引擎的资料请关注脚本之家其它相关文章!

相关文章

  • 深入解析HashMap的put方法

    深入解析HashMap的put方法

    在Java集合中,HashMap的重要性不言而喻,作为一种存储键值对的数据结构,它在日常开发中有着非常多的应用场景,也是面试中的高频考点,本篇文章就来分析一下HashMap集合中的put方法
    2022-01-01
  • json转换成java对象示例

    json转换成java对象示例

    这篇文章主要介绍了json转换成java对象示例,需要的朋友可以参考下
    2014-04-04
  • Java 异常详解

    Java 异常详解

    本文主要介绍了异常与错误的区别,异常的体现分类,异常的处理机制,如何自定义异常等,具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • springmvc集成shiro登录失败处理操作

    springmvc集成shiro登录失败处理操作

    这篇文章主要介绍了springmvc集成shiro登录失败处理操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-09-09
  • 使用SpringBoot和JPA实现批量处理新增、修改

    使用SpringBoot和JPA实现批量处理新增、修改

    最近项目需要在JPA中使用ID进行批量更新,所以下面这篇文章主要给大家介绍了关于使用SpringBoot和JPA实现批量处理新增、修改的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • Simple Java Mail邮件发送实现过程解析

    Simple Java Mail邮件发送实现过程解析

    这篇文章主要介绍了Simple Java Mail邮件发送实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • JavaGUI菜单栏与文本和密码及文本域组件使用详解

    JavaGUI菜单栏与文本和密码及文本域组件使用详解

    这篇文章主要介绍了JavaGUI菜单栏与文本和密码及文本域组件使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-03-03
  • Java快速开发平台若依、芋道、Jeesite、JeecgBoot详细对比

    Java快速开发平台若依、芋道、Jeesite、JeecgBoot详细对比

    Java快速开发平台是现代企业级Java应用研发过程中提升效率、降低重复劳动、加速交付周期的关键技术支撑体系,这篇文章主要介绍了Java快速开发平台若依、芋道、Jeesite、JeecgBoot详细对比的相关资料,需要的朋友可以参考下
    2026-05-05
  • Java后台Controller实现文件下载操作

    Java后台Controller实现文件下载操作

    这篇文章主要介绍了Java后台Controller实现文件下载操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-10-10
  • Spring事务管理之如何处理删除操作与事务回滚

    Spring事务管理之如何处理删除操作与事务回滚

    在实际开发中,事务管理是保证数据一致性的核心机制之一,本文将通过一个实际案例,详细分析Spring事务中的删除操作与回滚机制,并提供优化方案,需要的可以参考下
    2025-04-04

最新评论