一、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 studio | eclipse 插件形式的可视化模板设计器(拖拽式) |
| jrxml 语法 | xml 格式的模板描述语言 |
| 表达式语言 | $f{fieldname}(字段)、$p{paramname}(参数)、$v{varname}(变量) |
| 子报表(subreport) | 报表嵌套,主报表中嵌入子报表 |
| 条件样式 | 根据数据值动态改变颜色/字体/可见性 |
| 条码/二维码 | 内置 barcode4j 支持各种条码格式 |
| 图表 | 内置 jfreechart 支持柱状图/折线图/饼图 |
4.2 数据源类型
| 数据源 | 类 | 场景 |
|---|
| java bean 集合 | jrbeancollectiondatasource | 最常用,传入 list |
| jdbc 直连 | jrresultsetdatasource | 直接执行 sql |
| 空数据源 | jremptydatasource | 只有参数没有明细数据 |
| map 集合 | jrmapcollectiondatasource | list数据 |
| json | jsondatasource | json 字符串/文件 |
4.3 导出格式
| 格式 | 导出器类 | 用途 |
|---|
| pdf | jasperexportmanager | 打印、归档 |
| excel (xlsx) | jrxlsxexporter | 数据分析 |
| word (docx) | jrdocxexporter | 文档编辑 |
| html | htmlexporter | 在线预览 |
| csv | jrcsvexporter | 数据交换 |
| 图片 (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报表引擎的资料请关注代码网其它相关文章!
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论