Spring Boot iText 教程

在本教程中,我们将展示如何使用 iText 和 Spring Boot 创建 PDF 报告。 数据是从 H2 内存数据库中的表加载的。

iText 是一个开放源代码库,用于在 Java 中创建和处理 PDF 文件。

Spring 是用于开发 Java 企业应用的 Java 应用框架。 它还有助于集成各种企业组件。 Spring Boot 使创建具有 Spring 动力的生产级应用和服务变得很容易,而对安装的要求却最低。

H2 是完全用 Java 实现的开源关系数据库管理系统。 它可以嵌入 Java 应用中或以客户端-服务器模式运行。 它占地面积小,易于部署和安装。 它包含一个基于浏览器的控制台应用,用于查看和编辑数据库表。

JdbcTemplate 是一个 Spring 库,可以帮助程序员创建与关系数据库和 JDBC 一起使用的应用。 它会处理许多繁琐且容易出错的底层细节,例如处理事务,清理资源以及正确处理异常。 JdbcTemplate 在 Spring 的spring-jdbc模块中提供。

应用

以下 Spring Boot 应用从数据库表中加载数据,并使用 iText 库从中生成 PDF 报告。 该应用与嵌入式 Tomcat 服务器一起运行。

├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── zetcode
    │   │           ├── Application.java
    │   │           ├── bean
    │   │           │   └── Car.java
    │   │           ├── conf
    │   │           │   └── AppConfig.java
    │   │           ├── controller
    │   │           │   └── MyController.java
    │   │           ├── service
    │   │           │   ├── CarService.java
    │   │           │   └── ICarService.java
    │   │           └── view
    │   │               ├── AbstractPdfView.java
    │   │               └── MyPdfView.java
    │   └── resources
    │       ├── application.yml
    │       ├── data-h2.sql
    │       ├── schema-h2.sql
    │       └── static
    │           └── index.html
    └── test
        └── java

这是项目结构。

pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.zetcode</groupId> <artifactId>SpringBootItext</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> </parent> <dependencies> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.lowagie</groupId> <artifactId>itext</artifactId> <version>4.2.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

Maven pom.xml文件包含 iText 库,H2 驱动程序和 Spring 框架的依赖项。

Car.java

package com.zetcode.bean;

public class Car {

    private Long id;
    private String name;
    private int price;

    public Car() {}

    public Car(Long id, String name, int price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" + "id=" + id + ", name=" + name + ", price=" + price + '}';
    }
}

这是Car bean 类。 它包含商品 ID,名称和价格。

application.yml

datasource:
    url: jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE
    username: sa
    password: 
    driverClassName: org.h2.Driver

spring: 
    datasource:
        platform: h2
    h2: 
        console:
            enabled: true
            path: /console/        

application.yml是主要的 Spring Boot 配置文件。 它包含数据源和 MVC 设置。 我们选择了 H2 作为数据库系统。 数据库在内存中运行。 我们启用基于浏览器的控制台应用。

url: jdbc:h2:mem:testdb;DB_CLOSE_ON_EXIT=FALSE

数据库名称为testdb,并在内存中创建。 (当 Spring Boot 在 Maven POM 文件中发现 H2 时,它会自动创建一个内存数据库,但我们将展示如何显式地执行该操作。)

spring: 
    datasource:
        platform: h2

该平台值用在 SQL 初始化脚本中:schema-${platform}.sqldata-${platform}.sql

h2: 
    console:
        enabled: true
        path: /console/  

H2 Web 控制台应用已启用; 在localhost:8080/console/路径中可用。

schema-h2.sql

CREATE TABLE Cars(ID BIGINT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(30), PRICE INT);

该 SQL 脚本创建Cars表。

data-h2.sql

INSERT INTO Cars(Name, Price) VALUES('Audi', 52642);
INSERT INTO Cars(Name, Price) VALUES('Mercedes', 57127);
INSERT INTO Cars(Name, Price) VALUES('Skoda', 9000);
INSERT INTO Cars(Name, Price) VALUES('Volvo', 29000);
INSERT INTO Cars(Name, Price) VALUES('Bentley', 350000);
INSERT INTO Cars(Name, Price) VALUES('Citroen', 21000);
INSERT INTO Cars(Name, Price) VALUES('Hummer', 41400);
INSERT INTO Cars(Name, Price) VALUES('Volkswagen', 21600);

该脚本用数据填充表。 这两个脚本都位于类路径的根目录中。

AppConfig.java

package com.zetcode.conf;

import javax.sql.DataSource;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

@Configuration
public class AppConfig {

    @Bean
    @Primary
    @ConfigurationProperties(prefix = "datasource")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }
}

AppConfig是 Java 配置类。 它从application.yml配置文件创建数据源 bean。

AbstractPdfView.java

package com.zetcode.view;

import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.ByteArrayOutputStream;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.view.AbstractView;

public abstract class AbstractPdfView extends AbstractView {

    public AbstractPdfView() {

        initView();
    }

    private void initView() {

        setContentType("application/pdf");
    }

    @Override
    protected boolean generatesDownloadContent() {
        return true;
    }

    @Override
    protected final void renderMergedOutputModel(Map<String, Object> model, 
            HttpServletRequest request, HttpServletResponse response) throws Exception {

        ByteArrayOutputStream baos = createTemporaryOutputStream();

        Document document = new Document(PageSize.A4);
        PdfWriter writer = PdfWriter.getInstance(document, baos);
        prepareWriter(model, writer, request);
        buildPdfMetadata(model, document, request);

        document.open();
        buildPdfDocument(model, document, writer, request, response);
        document.close();

        writeToResponse(response, baos);
    }

    protected void prepareWriter(Map<String, Object> model, PdfWriter writer, 
            HttpServletRequest request) throws DocumentException {
        writer.setViewerPreferences(getViewerPreferences());
    }

    protected int getViewerPreferences() {
        return PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage;
    }

    protected void buildPdfMetadata(Map<String, Object> model, Document document, 
            HttpServletRequest request) {
    }

    protected abstract void buildPdfDocument(Map<String, Object> model, 
            Document document, PdfWriter writer, HttpServletRequest request, 
            HttpServletResponse response) throws Exception;
}

Spring 的AbstractPdfView基于旧的iText库。 因此,我们需要创建自己的抽象类,该抽象类基本上是具有更新的导入的副本。

MyPdfView.java

package com.zetcode.view;

import com.itextpdf.text.Document;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.FontFactory;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.zetcode.bean.Car;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyPdfView extends AbstractPdfView {

    @Override
    protected void buildPdfDocument(Map<String, Object> model,
            Document document, PdfWriter writer, HttpServletRequest request,
            HttpServletResponse response) throws Exception {

        List<Car> cars = (List<Car>) model.get("cars");

        PdfPTable table = new PdfPTable(3);
        table.setWidthPercentage(60);
        table.setWidths(new int[] {1, 3, 3});

        Font headFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD);

        PdfPCell hcell;
        hcell = new PdfPCell(new Phrase("Id", headFont));
        hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
        table.addCell(hcell);

        hcell = new PdfPCell(new Phrase("Name", headFont));
        hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
        table.addCell(hcell);

        hcell = new PdfPCell(new Phrase("Price", headFont));
        hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
        table.addCell(hcell);

        for (Car car : cars) {

            PdfPCell cell;

            cell = new PdfPCell(new Phrase(car.getId().toString()));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);
            table.addCell(cell);

            cell = new PdfPCell(new Phrase(car.getName()));
            cell.setPaddingLeft(5);
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_LEFT);
            table.addCell(cell);

            cell = new PdfPCell(new Phrase(String.valueOf(car.getPrice())));
            cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
            cell.setHorizontalAlignment(Element.ALIGN_RIGHT);
            cell.setPaddingRight(5);
            table.addCell(cell);
        }

        document.add(table);
    }
}

MyPdfView继承自定制的AbstractPdfView。 在buildPdfDocument()方法中,我们生成 PDF 文件。

List<Car> cars = (List<Car>) model.get("cars");

首先,我们从模型中获取数据。

PdfPTable table = new PdfPTable(3);

我们将数据放在表格中; 为此,我们有PdfPTable类。 该表包含三列:Id,名称和价格。

Font headFont = FontFactory.getFont(FontFactory.HELVETICA_BOLD);

对于表头,我们使用粗体的 Helvetica 字体。

PdfPCell hcell;
hcell = new PdfPCell(new Phrase("Id", headFont));
hcell.setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(hcell);

数据放置在表单元格内,由PdfPCell表示。 使用setHorizontalAlignment()方法将文本水平对齐。

document.add(table);

最后,表格被插入到 PDF 文档中。

MyController.java

package com.zetcode.controller;

import com.zetcode.bean.Car;
import com.zetcode.service.ICarService;
import com.zetcode.view.MyPdfView;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MyController {

    @Autowired
    private ICarService carService;

    @RequestMapping(path = "/report", method = RequestMethod.GET)
    public ModelAndView report() {

        Map<String, Object> model = new HashMap<>();

        List<Car> cars = carService.findAll();
        model.put("cars", cars);

        return new ModelAndView(new MyPdfView(), model);
    }
}

MyController中,我们有一个映射。

@Autowired
private ICarService carService;

我们将CarService对象注入到属性中。 服务对象用于从数据库检索数据。

@RequestMapping(path = "/report", method = RequestMethod.GET)
public ModelAndView report() {

    Map<String, Object> model = new HashMap<>();

    List<Car> cars = carService.findAll();
    model.put("cars", cars);

    return new ModelAndView(new MyPdfView(), model);
}

report()方法中,我们使用findAll()方法找到所有汽车。 我们将自定义MyPdfView返回给客户端。

ICarService.java

package com.zetcode.service;

import com.zetcode.bean.Car;
import java.util.List;

public interface ICarService {

    public List<Car> findAll();
}

ICarService提供了一种从数据源获取所有汽车的合同方法。

CarService.java

package com.zetcode.service;

import com.zetcode.bean.Car;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

@Service
public class CarService implements ICarService {

    @Autowired
    private JdbcTemplate jtm;

    @Override
    public List<Car> findAll() {

        String sql = "SELECT * FROM Cars";

        List<Car> cars = jtm.query(sql, new BeanPropertyRowMapper(Car.class));

        return cars;
    }
}

CarService包含findAll()方法的实现。 我们借助JdbcTemplateCars表中检索所有汽车。

@Autowired
private JdbcTemplate jtm;

注入JdbcTemplate

String sql = "SELECT * FROM Cars";

这是要执行的 SQL。 我们从Cars表中选择所有汽车。

List<Car> cars = jtm.query(sql, new BeanPropertyRowMapper(Car.class));

BeanPropertyRowMapper将一行转换为指定映射目标类的新实例。

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Home Page</title>
    </head>
    <body>
        <a href="/report.html">Generate report</a>
    </body>
</html>

index.html文件包含一个生成 PDF 报告的链接。 静态文件从预定义目录提供; 其中之一是static,位于src/main/resources中。

Application.java

package com.zetcode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Application设置 Spring Boot 应用。

$ mvn spring-boot:run

我们启动 Spring Boot 应用。

导航至http://localhost:8080/以测试应用。 H2 控制台应用可从http://localhost:8080/console/获得。 控制台应用的 JDBC URL 为jdbc:h2:mem:testdb。 密码为空。

在本教程中,我们从 H2 数据库的数据库表中使用 iText 创建了 PDF 报告。 该应用使用 Spring Boot 框架,并在 Web 环境中运行。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程