# Spring Boot iText 教程

> 原文： [http://zetcode.com/articles/springbootitext/](http://zetcode.com/articles/springbootitext/)

在本教程中，我们将展示如何使用 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 服务器一起运行。

```java
├── 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`

```java

<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`

```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`

```java
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 作为数据库系统。 数据库在内存中运行。 我们启用基于浏览器的控制台应用。

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

```

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

```java
spring: 
    datasource:
        platform: h2

```

该平台值用在 SQL 初始化脚本中：`schema-${platform}.sql`和`data-${platform}.sql`。

```java
h2: 
    console:
        enabled: true
        path: /console/  

```

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

`schema-h2.sql`

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

```

该 SQL 脚本创建`Cars`表。

`data-h2.sql`

```java
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`

```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`

```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`

```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 文件。

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

```

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

```java
PdfPTable table = new PdfPTable(3);

```

我们将数据放在表格中； 为此，我们有`PdfPTable`类。 该表包含三列：ID，名称和价格。

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

```

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

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

```

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

```java
document.add(table);

```

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

`MyController.java`

```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`中，我们有一个映射。

```java
@Autowired
private ICarService carService;

```

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

```java
@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`

```java
package com.zetcode.service;

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

public interface ICarService {

    public List<Car> findAll();
}

```

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

`CarService.java`

```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()`方法的实现。 我们借助`JdbcTemplate`从`Cars`表中检索所有汽车。

```java
@Autowired
private JdbcTemplate jtm;

```

注入`JdbcTemplate`。

```java
String sql = "SELECT * FROM Cars";

```

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

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

```

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

`index.html`

```java
<!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`

```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 应用。

```java
$ 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 环境中运行。 您可能也对这些相关教程感兴趣： [Spring Boot H2 教程](/articles/springbooth2/)， [Spring Boot JasperReports Web 集成](/articles/jasperspringbootweb/)， [Spring Web 应用简介](/articles/springwebfirst/)， [Spring Boot 第一个 Web 应用](/articles/springbootwebfirst/)和 [Java 教程](/lang/java/)。