BIRT 与 Spring Boot 集成
1. 简介
在本教程中,我们将集成 BIRT(商业智能和报告工具)与 Spring Boot MVC,以提供 HTML 和 PDF 格式的静态和动态报告。
2. 什么是BIRT?
BIRT 是一个开源引擎,用于创建可集成到 Java Web 应用程序中的数据可视化。
它是 Eclipse Foundation 中的一个顶级软件项目,并利用了 IBM 和 Innovent Solutions 的贡献。它于 2004 年底由 Actuate 发起并赞助。
该框架允许创建与各种数据源集成的报告。
3. Maven依赖
BIRT 有两个主要组件:一个用于创建报表设计文件的可视化报表设计器,以及一个用于解释和呈现这些设计的运行时组件。
在我们的示例 Web 应用程序中,我们将在 Spring Boot 之上使用这两者。
3.1. BIRT 框架依赖
由于我们习惯于从依赖管理的角度进行思考,因此首选是在 Maven Central 中查找 BIRT。
但是,可用的最新核心库官方版本在这里 ,而在Eclipse 下载页面上 ,我们可以找到至少两个较新版本的链接。
如果我们选择使用官方构建,让代码启动并运行的最简单方法是下载BIRT Report Engine 包,它是一个完整的 Web 应用程序,对学习也很有用。然后我们需要将它的lib文件夹复制到我们的项目中(大约 68MB 大小)并告诉 IDE 将所有 jar 包含在其中。
不用说,使用这种方法,我们只能通过 IDE 进行编译,因为除非我们在本地 repo 中手动配置和安装它们(超过 100 个文件!),否则 Maven 不会找到这些 jar。
幸运的是,Innovent Solutions 已经决定自己动手,并在 Maven Central 上发布了它自己构建的最新 BIRT 依赖项,这很棒,因为它为我们管理了所有需要的依赖项。
阅读在线论坛中的评论,尚不清楚这些工件是否可用于生产,但 Innovent Solutions 从一开始就在 Eclipse 团队旁边的项目上工作,因此我们的项目依赖于它们。
包括 BIRT 现在很容易:
<dependency>
<groupId>com.innoventsolutions.birt.runtime</groupId>
<artifactId>org.eclipse.birt.runtime_4.8.0-20180626</artifactId>
<version>4.8.0</version>
</dependency>
3.2. Spring Boot 依赖项
现在 BIRT 已导入到我们的项目中,我们只需在 pom 文件中添加标准 Spring Boot 依赖项。
但是有一个陷阱,因为 BIRT jar 包含它自己的Slf4J实现,它不能很好地与Logback配合使用,并且在启动期间会引发冲突异常。
由于我们无法从 jar 中删除它,为了解决这个问题,我们需要排除 Logback:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
</exclusions>
</dependency>
现在我们终于准备好开始了!
4. BIRT 报告
在 BIRT 框架中,报告是一个长 XML 配置文件,由扩展名rptdesign标识。
它告诉引擎要绘制什么以及在哪里绘制,从标题的样式到连接到数据源所需的属性。
对于一个基本的动态报表,我们需要配置三件事:
- 数据源(在我们的示例中,我们使用本地 CSV 文件,但它很容易成为数据库表)
- 我们要显示的元素(图表、表格等)
- 页面设计
报告的结构类似于 HTML 页面,包含页眉、正文、页脚、脚本和样式。
该框架提供了一系列开箱即用的组件可供选择,包括与主流数据源、布局、图表和表格的集成。而且,我们可以扩展它来添加我们自己的!
生成报告文件有两种方法:可视化或程序化。
5. Eclipse 报表设计器
为了简化报告的创建, Eclipse 团队为其流行的 IDE构建了一个报告设计工具插件。
该工具具有从左侧*Palette *中**轻松拖放的界面,它会自动打开我们添加到页面的新组件的设置窗口。*我们还可以通过单击页面上的每个组件,然后单击“Property Editor”*按钮(在下图中突出显示)来查看每个组件可用的所有自定义项。
要在树视图中可视化整个页面结构,我们只需单击*Outline *按钮。
Data Explorer选项卡还包含为我们的报告定义的数据源:
图片中显示的示例报告可在路径project_root/reports/csv_data_report.rptdesign中找到
使用视觉设计师的另一个优势是在线文档,它更多地关注这个工具而不是程序化方法。
如果我们已经在使用 Eclipse,我们只需要安装 BIRT Report Design 插件,它包括一个预定义的透视图和可视化编辑器。
对于那些当前没有使用 Eclipse并且不想切换的开发人员,有一个Eclipse Report Designer 包,它包含一个预装了 BIRT 插件的便携式 Eclipse 安装。
报告文件完成后,我们可以将其保存在我们的项目中,然后在我们首选的环境中返回编码。
6. 程序化方法
我们也可以只使用代码来设计报告,但是由于可用的文档很差,这种方法要困难得多,因此请准备好深入研究源代码和在线论坛。
另外值得考虑的是,所有繁琐的设计细节,如尺寸、长度和网格位置**,使用设计师都更容易处理**。
为了证明这一点,下面是一个如何定义一个带有图像和文本的简单静态页面的示例:
DesignElementHandle element = factory.newSimpleMasterPage("Page Master");
design.getMasterPages().add(element);
GridHandle grid = factory.newGridItem(null, 2, 1);
design.getBody().add(grid);
grid.setWidth("100%");
RowHandle row0 = (RowHandle) grid.getRows().get(0);
ImageHandle image = factory.newImage(null);
CellHandle cell = (CellHandle) row0.getCells().get(0);
cell.getContent().add(image);
image.setURL("https://www.itcodingman.com/logo.jpg");
LabelHandle label = factory.newLabel(null);
cell = (CellHandle) row0.getCells().get(1);
cell.getContent().add(label);
label.setText("Hello, Blogdemo world!");
此代码将生成一个简单(且丑陋)的报告:
上图中显示的示例报告可在以下路径中找到:project_root/reports/static_report.rptdesign。
一旦我们编写了报告的外观和应该显示的数据,我们就可以通过运行我们的ReportDesignApplication类来生成 XML 文件。
7. 附加数据源
我们之前提到过 BIRT 支持许多不同的数据源。
对于我们的示例项目,我们使用了一个包含三个条目的简单 CSV 文件。它可以在*reports *文件夹中找到,由三行简单的数据和标题组成:
Student, Math, Geography, History
Bill, 10,3,8
Tom, 5,6,5
Anne, 7, 4,9
7.1.配置数据源
要让 BIRT 使用我们的文件(或任何其他类型的源),我们必须配置一个Data Source。
对于我们的文件,我们使用报表设计器创建了一个Flat File Data Source,只需几个步骤:
- 打开设计师视角,看右边的*outline *。
- 右键单击Data Sources图标。
- 选择所需的源类型(在我们的例子中是平面文件源)。
- 我们现在可以选择加载整个文件夹或仅加载一个文件。我们使用了第二个选项(如果我们的数据文件是 CSV 格式,我们要确保使用第一行作为列名指示符)。
- 测试连接以确保路径正确。
我们附上了一些图片来展示每个步骤:
7.2. 数据集
数据源已经准备好,但我们仍然需要定义我们的Data Set,也就是我们报告中显示的实际数据:
- 打开设计师视角,看 右边的*outline *。
- 右键单击Data Sets图标。
- 选择所需的Data Sources和类型(在我们的例子中只有一种类型)。
- 下一个屏幕取决于我们选择的数据源和数据集的类型:在我们的例子中,我们看到一个页面,我们可以在其中选择要包含的列。
- 设置完成后,我们可以随时通过双击我们的数据集打开配置。
- 在Output Columns中,我们可以设置显示数据的正确类型。
- 然后我们可以通过单击Preview Results来查看预览。
同样,一些图片来阐明这些步骤:
7.3. 其他数据源类型
如Data Sets配置的第 4 步所述,可用选项可能会根据引用的Data Sources而改变。
对于我们的 CSV 文件,BIRT 提供了与显示哪些列、数据类型以及是否要加载整个文件相关的选项。另一方面,如果我们有 JDBC 数据源,我们可能必须编写 SQL 查询或存储过程。
从 Data Sets菜单中,我们还可以将两个或多个数据集连接到一个新数据集中。
8. 呈现报告
报告文件准备好后,我们必须将其传递给引擎进行渲染。要做到这一点,有一些事情要实施。
8.1. 初始化引擎
解释设计文件并生成最终结果的ReportEngine类是 BIRT 运行时库的一部分。
它使用一堆助手和任务来完成这项工作,这使得它非常耗费资源:
与创建引擎实例相关的成本很高,主要是由于加载扩展的成本。因此,我们应该只创建一个ReportEngine实例并使用它来运行多个报表。
报表引擎是通过Platform提供的工厂创建的。在创建引擎之前,我们必须启动Platform,它将加载适当的插件:
@PostConstruct
protected void initialize() throws BirtException {
EngineConfig config = new EngineConfig();
config.getAppContext().put("spring", this.context);
Platform.startup(config);
IReportEngineFactory factory = (IReportEngineFactory) Platform
.createFactoryObject(IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY);
birtEngine = factory.createReportEngine(config);
imageFolder = System.getProperty("user.dir") + File.separatorChar + reportsPath + imagesPath;
loadReports();
}
当我们不再需要它时,我们可以销毁它:
@Override
public void destroy() {
birtEngine.destroy();
Platform.shutdown();
}
8.2. 实现输出格式
BIRT 已经支持多种输出格式:HTML、PDF、PPT 和 ODT等等。
对于示例项目,我们使用generatePDFReport和generateHTMLReport方法实现了其中的两个。
它们略有不同,具体取决于所需的特定属性,例如输出格式和图像处理程序。
事实上,PDF 将图像与文本一起嵌入,而 HTML 报告需要生成它们和/或链接它们。
因此,PDF 渲染功能非常简单:
private void generatePDFReport(IReportRunnable report, HttpServletResponse response,
HttpServletRequest request) {
IRunAndRenderTask runAndRenderTask = birtEngine.createRunAndRenderTask(report);
response.setContentType(birtEngine.getMIMEType("pdf"));
IRenderOption options = new RenderOption();
PDFRenderOption pdfRenderOption = new PDFRenderOption(options);
pdfRenderOption.setOutputFormat("pdf");
runAndRenderTask.setRenderOption(pdfRenderOption);
runAndRenderTask.getAppContext().put(EngineConstants.APPCONTEXT_PDF_RENDER_CONTEXT, request);
try {
pdfRenderOption.setOutputStream(response.getOutputStream());
runAndRenderTask.run();
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
runAndRenderTask.close();
}
}
而 HTML 渲染功能需要更多的设置:
private void generateHTMLReport(IReportRunnable report, HttpServletResponse response,
HttpServletRequest request) {
IRunAndRenderTask runAndRenderTask = birtEngine.createRunAndRenderTask(report);
response.setContentType(birtEngine.getMIMEType("html"));
IRenderOption options = new RenderOption();
HTMLRenderOption htmlOptions = new HTMLRenderOption(options);
htmlOptions.setOutputFormat("html");
htmlOptions.setBaseImageURL("/" + reportsPath + imagesPath);
htmlOptions.setImageDirectory(imageFolder);
htmlOptions.setImageHandler(htmlImageHandler);
runAndRenderTask.setRenderOption(htmlOptions);
runAndRenderTask.getAppContext().put(
EngineConstants.APPCONTEXT_BIRT_VIEWER_HTTPSERVET_REQUEST, request);
try {
htmlOptions.setOutputStream(response.getOutputStream());
runAndRenderTask.run();
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
runAndRenderTask.close();
}
}
最值得注意的是,我们设置了HTMLServerImageHandler而不是保留默认处理程序。这个微小的差异对生成的img标签有很大的影响:
- 默认处理程序将img标记链接到文件系统路径,许多浏览器出于安全考虑而阻止了该路径。
- HTMLServerImageHandler*链接到服务器 URL
使用setImageDirectory方法,我们指定引擎将保存生成的图像文件的位置。
默认情况下,处理程序会在每次请求时生成一个新文件,因此我们可以添加缓存层或删除策略。
8.3. 发布图像
在 HTML 报告案例中,图像文件是外部的,因此它们需要在服务器路径上可访问。
在上面的代码中,通过setBaseImageURL方法,我们告诉引擎应该在img标签链接中使用什么相对路径,所以我们需要确保该路径实际上是可访问的!
出于这个原因,在我们的ReportEngineApplication中,我们将 Spring 配置为发布*images *文件夹:
@SpringBootApplication
@EnableWebMvc
public class ReportEngineApplication implements WebMvcConfigurer {
@Value("${reports.relative.path}")
private String reportsPath;
@Value("${images.relative.path}")
private String imagesPath;
...
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler(reportsPath + imagesPath + "/**")
.addResourceLocations("file:///" + System.getProperty("user.dir") + "/"
+ reportsPath + imagesPath);
}
}
无论我们选择什么路径,我们都必须确保这里和之前代码片段的 htmlOptions中使用相同的路径,否则我们的报告将无法显示图像。
9. 显示报告
准备好我们的应用程序所需的最后一个组件是一个*Controller *来返回渲染结果:
@RequestMapping(method = RequestMethod.GET, value = "/report/{name}")
@ResponseBody
public void generateFullReport(HttpServletResponse response, HttpServletRequest request,
@PathVariable("name") String name, @RequestParam("output") String output)
throws EngineException, IOException {
OutputType format = OutputType.from(output);
reportService.generateMainReport(name, format, response, request);
}
使用*output *参数,我们可以让用户选择所需的格式——HTML 或 PDF。
10. 测试报告
我们可以通过运行ReportEngineApplication类来启动应用程序。
在启动期间,BirtReportService类将加载在project_root/reports文件夹中找到的所有报告。
要查看我们的报告,我们只需将浏览器指向:
- /report/csv_data_report?output=pdf
- /report/csv_data_report?output=html
- /report/static_report?output=pdf
- /report/static_report?output=html
以下是 csv_data_report报告的外观:
要在更改设计文件后重新加载报告,我们只需将浏览器指向*/report/reload*。