当前位置: 首页 > news >正文

springboot集成PDF导出


内容目录
知识准备
什么是itext
itext的历史版本和License问题
标准的itextpdf导出的步骤
实现案例
Pom依赖
导出PDF
添加页眉页脚和水印
进一步理解
遇到license问题怎么办
为何添加页眉页脚和水印是通过PdfPageEvent来完成

除了处理word, excel等文件外,最为常见的就是PDF的导出了。在java技术栈中,PDF创建和操作最为常用的itext了,但是使用itext一定要了解其版本历史和License问题,在早前版本使用的是MPL和LGPL双许可协议,在5.x以上版本中使用的是AGPLv3(这个协议意味着,只有个人用途和开源的项目才能使用itext这个库,否则是需要收费的)。本文主要介绍通过SpringBoot集成itextpdf实现PDF导出功能。@pdai

知识准备
需要了解itext,以及itext历史版本变迁,以及license的问题。

什么是itext
来源于百度百科:iText是著名的开放源码的站点sourceforge一个项目(由Bruno Lowagie编写),是一个用Java和.NET语言写的库,用来创建和修改PDF文件。通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为PDF文件。 iText的安装非常方便,下载iText.jar文件后,只需要在系统的CLASSPATH中加入iText.jar的路径,在程序中就可以使用iText类库了。

iText提供除了基本的创建、修改PDF文件外的其他高级的PDF特性,例如基于PKI的签名,40位和128位加密,颜色校正,带标签的PDF,PDF表单(AcroForms),PDF/X,通过ICC配置文件和条形码进行颜色管理。这些特性被一些产品和服务中使用,包括Eclipse BIRT,Jasper Reports,JBoss Seam,Windward Reports和pdftk。

一般情况下,iText使用在有以下一个要求的项目中:

内容无法提前利用:取决于用户的输入或实时的数据库信息。
由于内容,页面过多,PDF文档不能手动生成。
文档需在无人参与,批处理模式下自动创建。
内容被定制或个性化;例如,终端客户的名字需要标记在大量的页面上。
itext的历史版本和License问题
使用itext一定要了解其版本历史,和License问题,在早前版本使用的是MPL和LGPL双许可协议,在5.x以上版本中使用的是AGPLv3(这个协议意味着,只有个人用途和开源的项目才能使用itext这个库,否则是需要收费的)

iText 0.x-2.x/iTextSharp 3.x-4.x
更新时间是2000-2009
使用的是MPL和LGPL双许可协议
最近的更新是2009年,版本号是iText 2.1.7/iTextSharp 4.1.6.0
此时引入包的GAV版本如下:
<dependency>
  <groupId>com.lowagie</groupId>
  <artifactId>itext</artifactId>
  <version>2.1.7</version>
</dependency>
iText 5.x和iTextSharp 5.x
更新时间是2009-2016, 公司化运作,并标准化和提高性能
开始使用AGPLv3协议
只有个人用途和开源的项目才能使用itext这个库,否则是需要收费的
iTextSharp被设计成iText库的.NET版本,并且与iText版本号同步,iText 5.0.0和iTextSharp5.0.0同时发布
新功能不在这里面增加,但是官方会修复重要的bug
此时引入包的GAV版本如下:
<dependency>
  <groupId>com.itextpdf</groupId>
  <artifactId>itextpdf</artifactId>
  <version>5.5.13.3</version>
</dependency>
iText 7.x
更新时间是2016到现在
AGPLv3协议
完全重写,重点关注可扩展性和模块化
不适用iTextSharp这个名称,都统称为iText,有Java和.Net版本
JDK 1.7+
此时引入包的GAV版本如下:
<dependency>
  <groupId>com.itextpdf</groupId>
  <artifactId>itext7-core</artifactId>
  <version>7.2.2</version>
  <type>pom</type>
</dependency>
注:iText变化后,GitHub上有团队基于4.x版本(MPL和LGPL双许可协议)fork了一个分支成为OpenPDF,并继续维护该项目。

标准的itextpdf导出的步骤
itextpdf导出pdf主要包含如下几步:

@Override
public Document generateItextPdfDocument(OutputStream os) throws Exception {
    // 1. 创建文档
    Document document = new Document(PageSize.A4);

    // 2. 绑定输出流(通过pdfwriter)
    PdfWriter.getInstance(document, os);

    // 3. 打开文档
    document.open();

    // 4. 往文档中添加内容
    document.add(xxx);

    // 5. 关闭文档
    document.close();
    return document;
}
document中添加的Element有哪些呢?

需要说明下如下概念之前的差别:

Chunk:文档的文本的最小块单位
Phrase:一系列以特定间距(两行之间的距离)作为参数的块
Paragraph:段落是一系列块和(或)短句。同短句一样,段落有确定的间距。用户还可以指定缩排;在边和(或)右边保留一定空白,段落可以左对齐、右对齐和居中对齐。添加到文档中的每一个段落将自动另起一行。
(其它从字面上就可以看出,所以这里具体就不做解释了)

实现案例
这里展示SpringBoot集成itext5导出PDF的例子。

Pom依赖
引入poi的依赖包

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.13.3</version>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext-asian</artifactId>
    <version>5.2.0</version>
</dependency>
导出PDF
UserController中导出的方法

package tech.pdai.springboot.file.word.poi.controller;


import java.io.OutputStream;

import javax.servlet.http.HttpServletResponse;

import io.swagger.annotations.ApiOperation;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import tech.pdai.springboot.file.word.poi.service.IUserService;

/**
 * @author pdai
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private IUserService userService;

    @ApiOperation("Download Word")
    @GetMapping("/word/download")
    public void download(HttpServletResponse response) {
        try {
            XWPFDocument document = userService.generateWordXWPFDocument();
            response.reset();
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-disposition",
                    "attachment;filename=user_world_" + System.currentTimeMillis() + ".docx");
            OutputStream os = response.getOutputStream();
            document.write(os);
            os.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

UserServiceImple中导出PDF方法

@Override
public Document generateItextPdfDocument(OutputStream os) throws Exception {
    // document
    Document document = new Document(PageSize.A4);
    PdfWriter.getInstance(document, os);

    // open
    document.open();

    // add content - pdf meta information
    document.addAuthor("pdai");
    document.addCreationDate();
    document.addTitle("pdai-pdf-itextpdf");
    document.addKeywords("pdf-pdai-keyword");
    document.addCreator("pdai");

    // add content -  page content

    // Title
    document.add(createTitle("Java 全栈知识体系"));

    // Chapter 1
    document.add(createChapterH1("1. 知识准备"));
    document.add(createChapterH2("1.1 什么是POI"));
    document.add(createParagraph("Apache POI 是创建和维护操作各种符合Office Open XML(OOXML)标准和微软的OLE 2复合文档格式(OLE2)的Java API。用它可以使用Java读取和创建,修改MS Excel文件.而且,还可以使用Java读取和创建MS Word和MSPowerPoint文件。更多请参考[官方文档](https://poi.apache.org/index.html)"));
    document.add(createChapterH2("1.2 POI中基础概念"));
    document.add(createParagraph("生成xls和xlsx有什么区别?POI对Excel中的对象的封装对应关系?"));

    // Chapter 2
    document.add(createChapterH1("2. 实现案例"));
    document.add(createChapterH2("2.1 用户列表示例"));
    document.add(createParagraph("以导出用户列表为例"));

    // 表格
    List<User> userList = getUserList();
    PdfPTable table = new PdfPTable(new float[]{20, 40, 50, 40, 40});
    table.setTotalWidth(500);
    table.setLockedWidth(true);
    table.setHorizontalAlignment(Element.ALIGN_CENTER);
    table.getDefaultCell().setBorder(1);

    for (int i = 0; i < userList.size(); i++) {
        table.addCell(createCell(userList.get(i).getId() + ""));
        table.addCell(createCell(userList.get(i).getUserName()));
        table.addCell(createCell(userList.get(i).getEmail()));
        table.addCell(createCell(userList.get(i).getPhoneNumber() + ""));
        table.addCell(createCell(userList.get(i).getDescription()));
    }
    document.add(table);

    document.add(createChapterH2("2.2 图片导出示例"));
    document.add(createParagraph("以导出图片为例"));
    // 图片
    Resource resource = new ClassPathResource("pdai-guli.png");
    Image image = Image.getInstance(resource.getURL());
    // Image image = Image.getInstance("/Users/pdai/pdai/www/tech-pdai-spring-demos/481-springboot-demo-file-pdf-itextpdf/src/main/resources/pdai-guli.png");
    image.setAlignment(Element.ALIGN_CENTER);
    image.scalePercent(60); // 缩放
    document.add(image);

    // close
    document.close();
    return document;
}

private List<User> getUserList() {
    List<User> userList = new ArrayList<>();
    for (int i = 0; i < 5; i++) {
        userList.add(User.builder()
                .id(Long.parseLong(i + "")).userName("pdai" + i).email("pdai@pdai.tech" + i).phoneNumber(121231231231L)
                .description("hello world" + i)
                .build());
    }
    return userList;
}
在实现时可以将如下创建文档内容的方法封装到Util工具类中


private Paragraph createTitle(String content) throws IOException, DocumentException {
    Font font = new Font(getBaseFont(), 24, Font.BOLD);
    Paragraph paragraph = new Paragraph(content, font);
    paragraph.setAlignment(Element.ALIGN_CENTER);
    return paragraph;
}


private Paragraph createChapterH1(String content) throws IOException, DocumentException {
    Font font = new Font(getBaseFont(), 22, Font.BOLD);
    Paragraph paragraph = new Paragraph(content, font);
    paragraph.setAlignment(Element.ALIGN_LEFT);
    return paragraph;
}

private Paragraph createChapterH2(String content) throws IOException, DocumentException {
    Font font = new Font(getBaseFont(), 18, Font.BOLD);
    Paragraph paragraph = new Paragraph(content, font);
    paragraph.setAlignment(Element.ALIGN_LEFT);
    return paragraph;
}

private Paragraph createParagraph(String content) throws IOException, DocumentException {
    Font font = new Font(getBaseFont(), 12, Font.NORMAL);
    Paragraph paragraph = new Paragraph(content, font);
    paragraph.setAlignment(Element.ALIGN_LEFT);
    paragraph.setIndentationLeft(12); //设置左缩进
    paragraph.setIndentationRight(12); //设置右缩进
    paragraph.setFirstLineIndent(24); //设置首行缩进
    paragraph.setLeading(20f); //行间距
    paragraph.setSpacingBefore(5f); //设置段落上空白
    paragraph.setSpacingAfter(10f); //设置段落下空白
    return paragraph;
}

public PdfPCell createCell(String content) throws IOException, DocumentException {
    PdfPCell cell = new PdfPCell();
    cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
    cell.setHorizontalAlignment(Element.ALIGN_CENTER);
    Font font = new Font(getBaseFont(), 12, Font.NORMAL);
    cell.setPhrase(new Phrase(content, font));
    return cell;
}

private BaseFont getBaseFont() throws IOException, DocumentException {
    return BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
}
导出后的PDF

添加页眉页脚和水印
在itextpdf 5.x 中可以利用PdfPageEvent来完成页眉页脚和水印。

package tech.pdai.springboot.file.pdf.itextpdf.pdf;

import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.Element;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.ColumnText;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfGState;
import com.itextpdf.text.pdf.PdfPageEventHelper;
import com.itextpdf.text.pdf.PdfTemplate;
import com.itextpdf.text.pdf.PdfWriter;

/**
 * @author pdai
 */
public class MyHeaderFooterPageEventHelper extends PdfPageEventHelper {

    private String headLeftTitle;

    private String headRightTitle;

    private String footerLeft;

    private String waterMark;

    private PdfTemplate total;

    public MyHeaderFooterPageEventHelper(String headLeftTitle, String headRightTitle, String footerLeft, String waterMark) {
        this.headLeftTitle = headLeftTitle;
        this.headRightTitle = headRightTitle;
        this.footerLeft = footerLeft;
        this.waterMark = waterMark;
    }

    @Override
    public void onOpenDocument(PdfWriter writer, Document document) {
        total = writer.getDirectContent().createTemplate(30, 16);
    }

    @Override
    public void onEndPage(PdfWriter writer, Document document) {
        BaseFont bf = null;
        try {
            bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // page header and footer
        addPageHeaderAndFooter(writer, document, bf);

        // watermark
        if (waterMark!=null) {
            addWaterMark(writer, document, bf);
        }
    }

    private void addPageHeaderAndFooter(PdfWriter writer, Document document, BaseFont bf) {
        PdfContentByte cb = writer.getDirectContent();
        cb.saveState();

        cb.beginText();

        cb.setColorFill(BaseColor.GRAY);
        cb.setFontAndSize(bf, 10);


        // header
        float x = document.top(-10);
        cb.showTextAligned(PdfContentByte.ALIGN_LEFT,
                headLeftTitle,
                document.left(), x, 0);
        cb.showTextAligned(PdfContentByte.ALIGN_RIGHT,
                headRightTitle,
                document.right(), x, 0);

        // footer
        float y = document.bottom(-10);
        cb.showTextAligned(PdfContentByte.ALIGN_LEFT,
                footerLeft,
                document.left(), y, 0);
        cb.showTextAligned(PdfContentByte.ALIGN_CENTER,
                String.format("- %d -", writer.getPageNumber()),
                (document.right() + document.left()) / 2,
                y, 0);

        cb.endText();

        cb.restoreState();
    }

    private void addWaterMark(PdfWriter writer, Document document, BaseFont bf) {
        for (int i = 1; i < 7; i++) {
            for (int j = 1; j < 10; j++) {
                PdfContentByte cb = writer.getDirectContent();
                cb.saveState();
                cb.beginText();
                cb.setColorFill(BaseColor.GRAY);
                PdfGState gs = new PdfGState();
                gs.setFillOpacity(0.1f);
                cb.setGState(gs);
                cb.setFontAndSize(bf, 12);
                cb.showTextAligned(Element.ALIGN_MIDDLE, waterMark, 75 * i,
                        80 * j, 30);
                cb.endText();
                cb.restoreState();
            }
        }
    }

    @Override
    public void onCloseDocument(PdfWriter writer, Document document) {
        ColumnText.showTextAligned(total, Element.ALIGN_LEFT, new Phrase(String.valueOf(writer.getPageNumber() - 1)), 2,
                2, 0);
    }
}
添加水印后导出后的PDF

进一步理解
通过如下几个问题进一步理解itextpdf。

遇到license问题怎么办
如前文所述,使用itext一定要了解其版本历史和License问题,在早前版本使用的是MPL和LGPL双许可协议,在5.x以上版本中使用的是AGPLv3。 有两种选择:

使用2.1.7版本
<dependency>
  <groupId>com.lowagie</groupId>
  <artifactId>itext</artifactId>
  <version>2.1.7</version>
</dependency>
使用OpenPDF
GitHub上有团队基于itext 4.x版本(MPL和LGPL双许可协议)fork了一个分支成为OpenPDF,并继续维护该项目。

为何添加页眉页脚和水印是通过PdfPageEvent来完成
为何添加页眉页脚和水印是通过PdfPageEvent来完成?

举个例子,如果我们在上述例子中需要在页脚中显示 “Page 1 of 3", 即总页数怎么办呢?而itext是流模式的写入内容,只有写到最后,才能知道有多少页,那么显示总页数必须在内容写完之后(或者关闭之前)确定;这就是为什么在onEndPage方法时才会写每页的页眉页脚。

iText仅在调用释放模板方法后才将PdfTemplate写入到OutputStream中,否则对象将一直保存在内存中,直到关闭文档。所以我们可以在最后关闭文档前,使用PdfTemplate写入总页码。可以理解成先写个占位符,然后统一替换。

相关文章:

springboot集成PDF导出

内容目录 知识准备 什么是itext itext的历史版本和License问题 标准的itextpdf导出的步骤 实现案例 Pom依赖 导出PDF 添加页眉页脚和水印 进一步理解 遇到license问题怎么办 为何添加页眉页脚和水印是通过PdfPageEvent来完成 除了处理word, excel等文件外&#xff0c;最为常见的…...

Podman 创建持久 MySQL 数据库容器

使用正确的 SELinux 上下文和权限创建目录/home/student/local/mysql。 创建/home/student/local/mysql目录。 [studentworkstation ~]$ mkdir -vp /home/student/local/mysql mkdir: 创建的目录/home/student/local mkdir: 创建的目录/home/student/local/mysql/home/studen…...

Java-反射

反射概述 Java反射机制&#xff1a; 是指在运行时去获取一个类的变量和方法信息。然后通过获取的信息来创建对象&#xff0c;调用方法的一种机制。由于这种<动态性>&#xff0c;可以极大的增强程序的灵活性&#xff0c;程序不用在编译期就完成确定&#xff0c;在运行期仍…...

构造agent类型的内存马(内存马系列篇十三)

写在前面 前面我们对JAVA中的Agent技术进行了简单的学习&#xff0c;学习前面的Agent技术是为了给这篇Agent内存马的实现做出铺垫&#xff0c;接下来我们就来看看Agent内存马的实现。 这是内存马系列篇的第十三篇了。 环境搭建 我这里就使用Springboot来搭建一个简单的漏洞…...

JavaEE简单示例——<select>中的查询参数传递和结果集封装自动映射关系

简单介绍&#xff1a; 在之前我们在讲SQL映射文件中的映射查询语句的<select>标签的时候&#xff0c;对其中的四个常用属性的讲解并不是那么的透彻&#xff0c;今天就来详细的解释<select>的四个常用属性的具体含义以及<select>标签在进行查询的时候查询参数…...

信息安全圈都在谈论CISP,CISSP,这两者有什么区别呢?

CISP 和 CISSP 都是信息安全认证资格考试&#xff0c;但是它们之间有一些区别。 CISP&#xff08;Certified Information Security Professional&#xff09;认证考试是由国际信息系统安全认证联盟&#xff08;ISC)所开发和管理的&#xff0c;主要考核信息安全专业人员在保障企…...

浅谈Redisson实现分布式锁的原理

1.Redisson简介 Redis 是最流行的 NoSQL 数据库解决方案之一&#xff0c;而 Java 是世界上最流行&#xff08;注意&#xff0c;我没有说“最好”&#xff09;的编程语言之一。虽然两者看起来很自然地在一起“工作”&#xff0c;但是要知道&#xff0c;Redis 其实并没有对 Java…...

UVM实战(张强)-- UVM中的寄存器模型

目录一.整体的设计结构图二.各个组件代码详解2.1 DUT2.2 bus_driver2.3 bus_sequencer2.4 bus_monitor2.5 bus_agent2.6 bus_transaction2.7 bus_if2.8 my_if2.9 my_transaction2.10 my_sequencer2.11 my_driver2.12 my_monitor2.13 my_agent2.14 my_scoreboard2.15 my_env2.16…...

什么是 CSAT?这份客户满意度流程指南请查收

什么是 CSAT&#xff1f;如何计算我的客户满意度分数&#xff1f;大中型公司应该熟悉这些术语。以下文章旨在教您有关客户满意度流程的所有内容 - 基本的CSAT概念、创建CSAT调查的好处、如何创建CSAT调查。配图来源&#xff1a; SaleSmartly(ss客服) 一、什么是 CSAT&#xff1…...

AD域备份和恢复工具

Microsoft的本地Active Directory备份和恢复功能不适用于对象级备份和属性级还原。使用RecoveryManager Plus&#xff0c;您不仅可以备份和还原所有AD对象&#xff0c;还可以备份和还原其他基本AD元素&#xff0c;例如架构属性&#xff0c;组成员身份信息和Exchange属性。此外&…...

老学长的浙大MPA现场复试经验分享

作为一名在浙大MPA项目已经毕业的考生来说&#xff0c;很荣幸受到杭州达立易考周老师的邀请&#xff0c;给大家分享下我的复试经验&#xff0c;因为听周老师说是这几年浙大MPA因疫情情况&#xff0c;已经连续几年都是线上个人复试了&#xff0c;而今年疫情社会面较为平稳的情况…...

制作证书链并进行验证

生成自签名的根证书: openssl req -x509 -newkey rsa -outform PEM -out tls-rootca.pem -keyform PEM -keyout tls-rootca.key.pem -days 35600 -nodes -subj “/C=cn/O=mycomp/OU=mygroup/CN=rootca” 生成中间证书 1.生成csr和key文件 openssl req -newkey rsa:2048 -outf…...

基于python多光谱遥感数据处理、图像分类、定量评估及机器学习方法应用

基于卫星或无人机平台的多光谱数据在地质、土壤调查和农业等应用领域发挥了重要作用&#xff0c;在地质应用方面&#xff0c;综合Aster的短波红外波段、landsat热红外波段等多光谱数据&#xff0c;可以通过不同的多光谱数据组合&#xff0c;协同用于矿物信息有效提取。此外&…...

初识 git--本地仓库

目录&#xff1a;一&#xff0c;基础步骤&#xff1a;1&#xff0c;安装2&#xff0c;配置3&#xff0c;检查配置4&#xff0c;创建仓库 - repository5&#xff0c;查看工作区的文件状态6&#xff0c;如果显示乱码的解决方式git status 显示乱码终端乱码7&#xff0c;添加工作区…...

Redis学习之持久化(六)

这里写目录标题一、持久化简介1.1 持久化1.2 Redis持久化的两种形式二、RDB2.1 RDB概念2.2 save指令手动执行一次保存配置相关参数2.3 bgsave指令2.4 save配置自动执行2.5 RDB三种启动方式对比三、AOF3.1 AOF概念3.2 AOF执行策略3.3 AOF重写四、RDB和AOF区别2.1 RDB与AOF对比&a…...

C++11 之 auto decltype

文章目录autodecltypesauto 和 decltype 的配合—返回值类型后置关于 c11 新特性&#xff0c;最先提到的肯定是类型推导&#xff0c;c11 引入了 auto 和 decltype 关键字&#xff0c;使用他们可以在编译期就推导出变量或者表达式的类型&#xff0c;方便开发者编码也简化了代码。…...

论文笔记:How transferable are features in deep neural networks? 2014年NIP文章

文章目录一、背景介绍二、方法介绍三、实验论证四、结论五、感想参考文献一、背景介绍 1.问题介绍&#xff1a; 许多在自然图像上训练的深度神经网络都表现出一个奇怪的共同现象&#xff1a;在第一层&#xff0c;它们学习类似于Gabor过滤器和color blobs的特征。这样的第一层特…...

python基于flask共享单车系统vue

可定制框架:ssm/Springboot/vue/python/PHP/小程序/安卓均可开发 目录 1 绪论 1 1.1课题背景 1 1.2课题研究现状 1 1.3初步设计方法与实施方案 2 1.4本文研究内容 2 2 系统开发环境 4 2. 3 系统分析 6 3.1系统可行性分析 6 3.1.1经济可行性 6 3.1.2技术可行性 6 3.1.3运行可行…...

C++11 之模板改进

模板的右尖括号 在 c98/03 的泛型编程中&#xff0c;模板实例化有一个很烦琐的地方&#xff0c;那就是连续两个右尖括号&#xff08;>>&#xff09;会被编译器解释成右移操作符&#xff0c;而不是模板参数表的结束&#xff0c;所以需要中间加个空格进行分割&#xff0c;…...

Linux - POSIX信号量,基于环形队列的生产者消费者模型

信号量在Linux下&#xff0c;POSIX信号量是一种线程同步机制&#xff0c;用于控制多个线程之间的访问顺序。POSIX信号量可以用于实现线程之间的互斥或者同步。在之前的阻塞队列生产者消费者模型中&#xff0c;阻塞队列是一个共享资源&#xff0c;不管是生产者还是消费者&#x…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

Linux链表操作全解析

Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表&#xff1f;1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...

redis和redission的区别

Redis 和 Redisson 是两个密切相关但又本质不同的技术&#xff0c;它们扮演着完全不同的角色&#xff1a; Redis: 内存数据库/数据结构存储 本质&#xff1a; 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能&#xff1a; 提供丰…...

实战设计模式之模板方法模式

概述 模板方法模式定义了一个操作中的算法骨架&#xff0c;并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下&#xff0c;重新定义算法中的某些步骤。简单来说&#xff0c;就是在一个方法中定义了要执行的步骤顺序或算法框架&#xff0c;但允许子类…...

解析“道作为序位生成器”的核心原理

解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制&#xff0c;重点解析"道作为序位生成器"的核心原理与实现框架&#xff1a; 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…...

手动给中文分词和 直接用神经网络RNN做有什么区别

手动分词和基于神经网络&#xff08;如 RNN&#xff09;的自动分词在原理、实现方式和效果上有显著差异&#xff0c;以下是核心对比&#xff1a; 1. 实现原理对比 对比维度手动分词&#xff08;规则 / 词典驱动&#xff09;神经网络 RNN 分词&#xff08;数据驱动&#xff09…...

Docker、Wsl 打包迁移环境

电脑需要开启wsl2 可以使用wsl -v 查看当前的版本 wsl -v WSL 版本&#xff1a; 2.2.4.0 内核版本&#xff1a; 5.15.153.1-2 WSLg 版本&#xff1a; 1.0.61 MSRDC 版本&#xff1a; 1.2.5326 Direct3D 版本&#xff1a; 1.611.1-81528511 DXCore 版本&#xff1a; 10.0.2609…...