html2canvas + jspdf 纯前端HTML导出PDF的实现与问题
前言
这几天接到一个需求,富文本编辑器的内容不仅要展示出来,还要实现展示的内容导出pdf文件。一开始导出pdf的功能是由后端来做的,然后发现对于宽度太大的图片,导出的pdf文件里部分图片内容被遮盖了,但在前端是正常显示的,只因为class样式后端无法解析。然后,后端开发人员就嫌麻烦,让前端来实现导出pdf的功能。。。
html2canvas(V1.4.1)
html2canvas 用于将 html 元素渲染成图像,可以将整个页面或特定区域以图像形式进行捕获。这对于将复杂的 html 结构转换为 PDF 格式非常有用,因为它可以捕获 html 中的样式、布局和图像等细节。
官网:https://html2canvas.hertzen.com/
引用
npm install html2canvas@1.4.1 --save
或
yarn add html2canvas@1.4.1
或
<script src="https://html2canvas.hertzen.com/dist/html2canvas.js"></script>
示例
<template><div id="pdfBody" style="margin: 30px;"><input class="test_input" type="text" autocomplete="off" placeholder="输入框111"/><input class="test_btn" id="testBtn" type="button" value="按钮"/></div>
</template><script>import html2canvas from "@/utils/htmlToPdf/html2canvas.min";export default {name: "Test",mounted() {document.getElementById('testBtn').addEventListener('click', () => {this.canvasTest();});},methods: {createCanvas(dom) {html2canvas(dom, {useCORS: true, //允许跨域scale: 2, //按比例增加分辨率dpi: 200, //将分辨率提高到特定的DPI(每英寸点数)ignoreElements: (e) => {//过滤head、body等无用的标签元素return !(e.contains(dom) || dom.contains(e) || e.tagName === 'STYLE' || e.tagName === 'LINK');},onclone: (doc) => {//Scoped CSS无法直接应用,需要手动处理let inputDocArr = doc.getElementsByClassName('test_input');[].forEach.call(inputDocArr, inputDoc => {inputDoc.style.height = '36px';inputDoc.style.lineheight = '36px';inputDoc.style.width = '200px';inputDoc.style.margin = '20px';inputDoc.style.borderRadius = '5px';inputDoc.style.border = '1px solid #DCDFE6';});let btnDocArr = doc.getElementsByClassName('test_btn');[].forEach.call(btnDocArr, btnDoc => {btnDoc.style.color = '#FFFFFF';btnDoc.style.backgroundColor = '#1890ff';btnDoc.style.border = '1px solid #1890ff';btnDoc.style.height = '36px';btnDoc.style.width = '80px';btnDoc.style.borderRadius = '20px';});}}).then(canvas => {dom.appendChild(canvas);}).finally(() => {console.log('create canvas finish!');});},canvasTest() {let pdfBody = document.getElementById('pdfBody');this.createCanvas(pdfBody);}}}
</script><style scoped>.test_input {height: 36px;line-height: 36px;width: 200px;margin: 20px;border-radius: 5px;border: 1px solid #DCDFE6;}.test_input:focus {border: 1px solid #1890ff;outline: none;}.test_btn {color: #FFFFFF;background-color: #1890ff;border: 1px solid #1890ff;height: 36px;width: 80px;border-radius: 20px;cursor: pointer;}
</style>
配置项
如果想隐藏某个元素不让其显示出来,参考以下几种方案:
1、元素标签添加 data-html2canvas-ignore 属性,示例如下:
2、配置项中添加 ignoreElements 属性,手动过滤某个元素,示例如上代码;
3、配置项中添加 onclone 属性,手动处理某个元素的显示与隐藏,即:element.style.display = 'hidden'。
jsPDF(V1.5.3)
jsPDF 是一个 PDF 生成库,它允许你通过 JavaScript 代码创建和编辑 PDF 文档。
官网:https://github.com/parallax/jsPDF
引用
npm install jsPDF@1.5.3 --save
或
yarn add jsPDF@1.5.3
或
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js"></script>
示例
<template><div id="pdfBody" style="margin: 30px;"><h3>测试创建pdf文件!</h3><input id="testPdf" type="button" value="pdf测试"/></div>
</template><script>import jsPDF from "@/utils/htmlToPdf/jspdf.min";export default {name: "Test",mounted() {document.getElementById('testPdf').addEventListener('click', () => {this.pdfTest();});},methods: {pdfTest() {let doc = new jsPDF({orientation: 'p',unit: 'mm',format: 'a4'});doc.text("Hello world!", 10, 10);doc.save("测试.pdf");}}}
</script>
设置中文字体
设置英文内容是正常显示的,假若内容包含中文,会发现pdf文件里的内容显示乱码,如下图所示:
let doc = new jsPDF({orientation: 'p', unit: 'mm',format: 'a4'
});
doc.text("pdf文件测试!", 10, 10);
doc.save("测试.pdf");
解决这一问题,可以使用 jsPDF 提供的 setFont(font) 方法,具体操作如下:
1、网上下载一个支持中文的字体tff文件,或者拷贝一份本地 window/font/ 路径下的字体文件(ps:有的字体不支持);
2、从GitHub上下载jsPDF源码,然后打开fontconverter目录下的fontconverter.html文件;
3、选择本地的tff文件,点击“Create”按钮,会生成一个js文件;
4、一般情况下,生成的js文件直接引入页面使用会报错,需要手动处理生成新的js文件;
默认生成的js文件内容如下:
(function (jsPDFAPI) {var font = "XXXXXX";var callAddFont = function () {this.addFileToVFS("simfang.ttf", font);this.addFont("simfang.ttf", "simfang", "normal");
};
jsPDFAPI.events.push(['addFonts', callAddFont])})(jsPDF.API);
然后复制一份js文件,编辑内容如下格式:
export function addfont(pdf) {let font = 'XXXXXX';pdf.addFileToVFS('simfang.ttf', font);pdf.addFont('simfang.ttf', 'simfang', 'normal');
}
5、将处理好的新的js文件放入项目中,然后在页面中引用,并设置字体。
<template><div id="pdfBody" style="margin: 30px;"><h3>测试创建pdf文件!</h3><input id="testPdf" type="button" value="pdf测试"/></div>
</template><script>import jsPDF from "@/utils/htmlToPdf/jspdf.min";import {addfont} from '@/utils/htmlToPdf/simfang';export default {name: "Test",mounted() {document.getElementById('testPdf').addEventListener('click', () => {this.pdfTest();});},methods: {pdfTest() {let doc = new jsPDF({orientation: 'p',unit: 'mm',format: 'a4'});//设置中文字体addfont(doc);doc.setFont('simfang');doc.text("pdf文件测试撒大大大飒飒热通过如果还有就要让他阿松大尔特瑞特好几年,是否然后他又就很尴尬发射任务给他人用不上,二条天皇已经不是v堵塞惹我实在是肉体和set一年德国必须色让他管很多人把!", 10, 10);doc.save("测试.pdf");}}}
</script>
解决jsPDF文本不自动换行问题
中文乱码问题解决了,然后新的问题又出现了,文本不自动换行。解决方案参考如下:
1、使用 splitTextToSize() 方法:
let doc = new jsPDF({orientation: 'p',unit: 'mm',format: 'a4'
});
//设置中文字体
addfont(doc);
doc.setFont('simfang');
let con = "pdf文件测试撒大大大飒飒热通过如果还有就要让他阿松大尔特瑞特好几年,是否然后他又就很尴尬发射任务给他人用不上,二条天皇已经不是v堵塞惹我实在是肉体和set一年德国必须色让他管很多人把!";
let splitCon = doc.splitTextToSize(con, 190);
console.log('splitCon',splitCon)
doc.text(splitCon, 10, 10);
doc.save("测试.pdf");
2、text() 方法参数添加选项 maxWidth :
let doc = new jsPDF({orientation: 'p',unit: 'mm',format: 'a4'
});
//设置中文字体
addfont(doc);
doc.setFont('simfang');
let con = "pdf文件测试撒大大大飒飒热通过如果还有就要让他阿松大尔特瑞特好几年,是否然后他又就很尴尬发射任务给他人用不上,二条天皇已经不是v堵塞惹我实在是肉体和set一年德国必须色让他管很多人把!";
doc.text(con, 10, 10, {maxWidth: 190
});
doc.save("测试.pdf");
jsPDF 库 API
文档地址:https://artskydj.github.io/jsPDF/docs/index.html
以下是常用API,仅供参考:
方法 | 说明 |
---|---|
var doc = new jsPDF(orientation, unit, format, compress); | 创建新文档: orientation:"l"(横向)、"p"(纵向); unit:"pt"、"mm"(默认)、"cm"、"m"、"in" or "px"; format:"a3"、"a4(默认)"、"a5"、"letter"、"legal"等 |
doc.addPage() | 添加一个空白页 |
doc.setPage(pageNumber) | 切换到第几个页面操作 |
doc.internal.getNumberOfPages() | 获取总页面数 |
doc.text(text, x, y, options); | 页面中添加文本: text:文本内容; x:距离页面左边的距离; y:距离页面上边的距离; options:可选参数配置 |
doc.setFont(fontName, fontStyle) | 页面文本设置字体: fontName:字体名称; fontStyle:字体风格,如"加粗" |
doc.setFontSize(size); | 设置字体大小 |
doc.setTextColor(ch1, ch2, ch3, ch4); | 设置文本颜色,可以是颜色码或rgb值 |
doc.addImage(imageData,format, x, y, width, height) | 在 PDF 文件中添加一个图像: imageData:图像的数据; format:图像的类型,如"JPEG"; x、y:分别表示图像左上角的坐标; width:图像的宽度; height:图像的高度 |
doc.save(filename) | 生成指定文件名的PDF文件 |
html2canvas + jsPDF
jsPDF 与 html2canvas 结合使用,可以将 html 元素渲染成图像,然后将图像插入到 jsPDF 创建的 PDF 文档中。
示例
import html2canvas from "@/utils/htmlToPdf/html2canvas.min";
import jsPDF from "@/utils/htmlToPdf/jspdf.min";
import {addfont} from '@/utils/htmlToPdf/simfang';
import {Loading} from "element-ui";export function downloadPdf(title, dom) {let reqLoading = Loading.service({fullscreen: true,text: '正在生成PDF文件......',spinner: 'el-icon-loading',background: 'rgba(0,0,0,0.5)'});html2canvas(dom, {useCORS: true, //允许跨域scale: 2, //按比例增加分辨率dpi: 200, //将分辨率提高到特定的DPI(每英寸点数)ignoreElements: (e) => {return !(e.contains(dom) || dom.contains(e) || e.tagName === 'STYLE' || e.tagName === 'LINK');}}).then(canvas => {// dom.appendChild(canvas);// 新建JsPDF对象let PDF = new jsPDF({orientation: 'p', //参数: l:横向 p:纵向unit: 'mm', //参数:测量单位("pt","mm", "cm", "m", "in" or "px")format: 'a4', //A4纸});let ctx = canvas.getContext('2d');let a4w = 190;let a4h = 272; //A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277,底部留5mm的页码显示位置,所以高度为272mm。let imgHeight = Math.floor(a4h * canvas.width / a4w) - 2; //按A4显示比例换算一页图像的像素高度let renderedHeight = 0;//计算总页数let pageCount = Math.ceil(canvas.height / imgHeight);let page = document.createElement("canvas");page.width = canvas.width;//设置中文字体addfont(PDF);PDF.setFont('simfang');while (renderedHeight < canvas.height) {page.height = Math.min(imgHeight, canvas.height - renderedHeight); //可能内容不足一页//用getImageData剪裁指定区域,并画到前面创建的canvas对象中page.getContext('2d').putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0);//canvas转图片数据保留10mm边距PDF.addImage(page.toDataURL('image/jpeg', 1), 'JPEG', 10, 10, a4w, Math.min(a4h, a4w * page.height / page.width));renderedHeight += imgHeight;//计算当前页及文字let pageNumberText = '第' + (renderedHeight / imgHeight) + '页/共' + pageCount + '页';//设置文字大小PDF.setFontSize(4);//设置文字颜色PDF.setTextColor('#999');//在页脚添加页码PDF.text(pageNumberText, ((a4w + 8) / 2), (a4h + 16));//判断是否分页,如果后面还有内容,添加一个空页if (renderedHeight < canvas.height) {PDF.addPage();}}PDF.save(title + ".pdf");}).finally(() => {if (reqLoading) {reqLoading.close();}});
}
常见问题
截取的图片显示空白
1、跨域问题
html2canvas 默认是不支持跨域图片的,对于跨域的图片默认是无法截取进去的,需要开启跨域配置:
- 将图片转成 base64 形式;
- 设置配置项 allowTaint: true 或 useCORS: true;
- img 标签中添加 crossOrigin="anonymous";
- 图片服务器配置 Access-Control-Allow-Origin: *
2、图片未加载完成
若图片未加载完成,此时截取的也是空白,解决方法就是设置延时一定事件后处理,或图片添加 load 事件,等图片加载完在进行截图。
3、需要截图的dom元素太多
若出现前面内容都正常截取,后面内容出现空白,大概率就是dom元素太多了。本人就碰到这种情况,即使延时10秒后截取,后面的部分还是空白,没办法,最后只能后端实现导出pdf功能了。。。
截图后部分css效果未正常显示
1、部分css样式 html2canvas 不支持
html2canvas 无法支持全部的css样式,关于支持哪些css样式或不支持哪些css样式,可以参考文档:https://html2canvas.hertzen.com/features
2、scoped 作用域的css无法应用
在vue中,为了避免css样式的交叉污染,都会使用 scoped 作用域的css样式,但是该作用域下的css样式无法在 html2canvas 中使用,解决方案可以在 onclone 配置项中过滤元素手动处理样式(详情可参考上述示例)
其他的问题可以参考博文:https://www.cnblogs.com/padding1015/p/9225517.html
总结
使用 html2canvas + jsPDF 纯前端导出pdf的方式还是有很多问题的,一般情况下都是后端进行文件的导出,前端配合解决样式问题。
另外我看还有使用 dom-to-image 或 modern-screenshot 的,不知道效果怎么样,有时间可以试一下。
相关文章:

html2canvas + jspdf 纯前端HTML导出PDF的实现与问题
前言 这几天接到一个需求,富文本编辑器的内容不仅要展示出来,还要实现展示的内容导出pdf文件。一开始导出pdf的功能是由后端来做的,然后发现对于宽度太大的图片,导出的pdf文件里部分图片内容被遮盖了,但在前端是正常显…...

【JVM】JVM调优练习-随笔
JVM实战笔记-随笔 前言字节码如何查看字节码文件jclasslibJavapArthasArthurs监控面板Arthus查看字节码信息 内存调优内存溢出的常见场景解决内存溢出发现问题Top命令VisualVMArthas使用案例 Prometheus Grafana案例 堆内存情况对比内存泄漏的原因:代码中的内存泄漏并发请求问…...
如何解决 CentOS 7 官方 yum 仓库无法使用
一、背景介绍 编译基于 CentOS 7.6.1810 镜像的 Dockerfile 过程中,执行 yum install 指令时,遇到了错误:Could not resolve host: mirrorlist.centos.org; Unknown error。 二、原因分析 官方停止维护 CentOS 7。该系统内置的 yum.repo 所使用的域名 mirrorlist.centos.o…...
分布式唯一id的7种方案
背景 为什么需要使用分布式唯一id? 如果我们的系统是单体的,数据库是单库,那无所谓,怎么搞都行。 但是如果系统是多系统,如果id是和业务相关,由各个系统生成的情况下,那每个主机生成的主键id就…...

嵌入式物联网在医疗行业中的应用——案例分析
作者主页: 知孤云出岫 目录 嵌入式物联网在医疗行业中的应用——案例分析引言1. 智能病房监控1.1 实时患者监控系统 2. 智能医疗设备管理2.1 设备使用跟踪与维护 3. 智能药物管理3.1 药物分配与跟踪 4. 智能远程医疗4.1 远程患者监控与诊断 总结 嵌入式物联网在医疗行业中的应…...

C语言 底层逻辑详细阐述指针(一)万字讲解 #指针是什么? #指针和指针类型 #指针的解引用 #野指针 #指针的运算 #指针和数组 #二级指针 #指针数组
文章目录 前言 序1:什么是内存? 序2:地址是怎么产生的? 一、指针是什么 1、指针变量的创建及其意义: 2、指针变量的大小 二、指针的解引用 三、指针类型存在的意义 四、野指针 1、什么是野指针 2、野指针的成因 a、指…...

【人工智能大模型】文心一言介绍以及基本使用指令
目录 一、产品背景与技术基础 二、主要功能与特点 基本用法 指令的使用 注意事项 文心一言(ERNIE Bot)是百度基于其文心大模型技术推出的生成式AI产品。以下是对文心一言的详细介绍: 一、产品背景与技术基础 技术背景:百度…...

AI绘画入门实践|Midjourney 的模型版本
模型分类 Midjourney 的模型主要分为2大类: 默认模型:目前包括:V1, V2, V3, V4, V5.0, V5.1, V5.2, V6 NIJI模型:目前包括:NIJI V4, NIJI V5, NIJI V6 模型切换 你在服务器输入框中输入 /settings: 回车后…...

Web3时代的教育技术革新:智能合约在学习管理中的应用
随着区块链技术的发展和普及,Web3时代正在为教育技术带来前所未有的革新和机遇。智能合约作为区块链技术的核心应用之一,不仅在金融和供应链管理等领域展示了其巨大的潜力,也在教育领域中逐渐探索和应用。本文将探讨智能合约在学习管理中的具…...

云计算实训室的核心功能有哪些?
在当今数字化转型浪潮中,云计算技术作为推动行业变革的关键力量,其重要性不言而喻。唯众,作为教育实训解决方案的领先者,深刻洞察到市场对云计算技能人才的迫切需求,精心打造了云计算实训室。这一实训平台不仅集成了先…...

芯科科技第五届物联网开发者大会走进世界各地,巡回开启注册
中国,北京 – 2024年7月18日 – 致力于以安全、智能无线连接技术,建立更互联世界的全球领导厂商Silicon Labs(亦称“芯科科技”,NASDAQ:SLAB)今日宣布,其2024年Works With开发者大会现正开放注册…...

Python创建Excel表和读取Excel表的基础操作
下载openpyxl第三方库 winr打开命令行输入cmd 这个如果不行可以试试其他方法,在运行Python代码的软件里也有直接下载的地方,可以上网搜索 创建Excel表 示例代码:最后要记得保存,可以加一句提示语句。 import openpyxl lst[100,…...

JVM(day2)经典垃圾收集器
经典垃圾收集器 Serial收集 使用一个处理器或一条收集线程去完成垃圾收集工作,更重要的是强调在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束。 ParNew收集器 ParNew 收集器除了支持多线程并行收集之外,其他与 …...

华为od机试真题 — 分披萨(Python)
题目描述 “吃货”和“馋嘴”两人到披萨店点了一份铁盘(圆形)披萨,并嘱咐店员将披萨按放射状切成大小相同的偶数个小块。 但是粗心服务员将披萨切成了每块大小都完全不同奇数块,且肉眼能分辨出大小。 由于两人都想吃到最多的披萨,他们商量…...
ubuntu22.04 安装boost
下载boost压缩包,我这里上传了一份1_81_0版本tar -xzvf boost_1_81_0.tar.gzcd boost_1_81_0/sudo apt install build-essential g autotools-dev libicu-dev libbz2-dev -ysudo ./bootstrap.sh --prefix/usr/./b2sudo ./b2 install 上述7步完成后,相关…...

基于JAVA+SpringBoot+uniapp的心理小程序(小程序版本)
✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、SpringCloud、Layui、Echarts图表、Nodejs、爬…...

C语言 ——— 输入两个正整数,求出最小公倍数
目录 何为最小公倍数 题目要求 代码实现 方法一:暴力求解法(不推荐) 方法二:递乘试摸法(推荐) 何为最小公倍数 最小公倍数是指两个或者多个正整数(除了0以外)的最小的公共倍数…...
Langchain 对pdf,word,txt等不同文件的加载解析
项目中遇到各种数据资源想要加载近langchain构建本地知识ai系统,怎么加载对应的文件格式呢,一起研究下 引入Langchain from langchain.document_loaders import UnstructuredWordDocumentLoader,PyPDFium2Loader,DirectoryLoader,PyPDFLoader,TextLoad…...

BL201分布式I/O耦合器连接Profinet网络
钡铼技术的BL201分布式I/O耦合器是一个用于Profinet网络的设备,用于连接远程输入/输出(I/O)设备到控制系统,如可编程逻辑控制器(PLC),能够实现分布式的I/O连接和通信。 它支持标准Profinet IO …...

Pycharm 报错 Environment location directory is not empty 解
删除项目中ven文件夹(已存在的),然后再添加新的ven虚拟环境就可以了...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...

MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...

Spring AOP代理对象生成原理
代理对象生成的关键类是【AnnotationAwareAspectJAutoProxyCreator】,这个类继承了【BeanPostProcessor】是一个后置处理器 在bean对象生命周期中初始化时执行【org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization】方法时…...