当前位置: 代码网 > it编程>编程语言>Java > SpringBoot使用JasperReports报表引擎的原理与使用实践

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

2026年07月26日 Java 我要评论
一、jasperreports 是什么1.1 定位jasperreports 是 java 生态中最流行的开源报表引擎,专门用于生成格式化的文档(pdf、excel、word、html、csv 等)。

一、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报表引擎的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com