云锦 - java
type: channel
link: https://blog.flyecho.fun/index.php/tag/java/
items: Spring Security 整合 JWT ：实现身份认证
items: p6spy 组件打印完整的 SQL 语句、执行耗时
items: Springboot 整合持久层框架 Mybatis Plus
items: Spring Boot 实现 JSR 380优雅的参数校验
items: Spring Boot 自定义注解，AOP切面实现 API 请求日志记录
items: Spring Boot 整合 Logback 日志替代 log4j
items: 用宝塔轻松部署java springboot的前后端分离项目
items: springboot项目配置Lombok 避免编写那些冗余的 Java 样板式代码
items: JAVA 使用aspose-words 实现 word转pdf

Spring Security 整合 JWT ：实现身份认证
type: item
date: 2024-11-20T11:04:00+08:00
description: 1. 什么是 JWT？JWT（JSON Web Token）是一种用于在不同应用之间安全传输信息的开放标准（RFC 7519）。它是一种基于 JSON 的轻量级令牌，由三部分组成：头部（Header）、载荷（Payload）和签名（Signature）。JWT 被广泛用于实现身份验证和授权，特别适用于前后端分离的应用程序。令牌类似下面这一大长串：eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJxdWFueGlhb2hhIiwiaXNzIjoicXVhbnhpYW9oYSIsImlhdCI6MTY5Mjk1OTY2MSwiZXhwIjoxNjkyOTYzMjYxfQ.wbqbn23C9vAe5sQZRCBzrIM4SiN1eNl55NIONmHoiPHPHSSu0QJGgPGUin80hA4XgMHEqN1Wm5KJlmKKucUyGQ可以看到，由 header.payload.signature 三部分组成，你可以在此网站: https://jwt.io/ 上获得解析结果：2. 为什么要使用 JWT？JWT 提供了一种在客户端和服务器之间传输安全信息的简单方法，具有以下优点：无状态性（Stateless）：JWT 本身包含了所有必要的信息，无需在服务器端存储会话信息，每个请求都可以独立验证。灵活性：JWT 可以存储任意格式的数据，使其成为传递用户信息、权限、角色等的理想选择。安全性：JWT 使用签名进行验证，确保信息在传输过程中不被篡改。跨平台和跨语言：由于 JWT 使用 JSON 格式，它在不同的编程语言和平台之间都可以轻松传递。3. 开始动手3.1 添加 JWT 依赖这里我们选择 Java JWT : JSON Web Token for Java and Android (简称 JJWT) 库。首先，在父模块中的 pom.xml 中声明版本号： <properties> // 省略... <jjwt.version>0.11.2</jjwt.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>${jjwt.version}</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>${jjwt.version}</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>${jjwt.version}</version> </dependency> </dependencies> </dependencyManagement> 然后，在jwt模块中的 pom.xml 文件中，引入该依赖，添加内容如下： <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> </dependency>3.2 JwtTokenHelper 工具类@Component public class JwtTokenHelper implements InitializingBean { /** * 签发人 */ @Value("${jwt.issuer}") private String issuer; /** * 秘钥 */ private Key key; /** * JWT 解析 */ private JwtParser jwtParser; /** * 解码配置文件中配置的 Base 64 编码 key 为秘钥 * @param base64Key */ @Value("${jwt.secret}") public void setBas…

p6spy 组件打印完整的 SQL 语句、执行耗时
type: item
date: 2024-11-20T10:38:00+08:00
description: 1. 前言配置SQL打印一方面可以了解到每个操作都具体执行的什么 SQL 语句， 另一方面通过打印执行耗时，也可以提前发现一些慢 SQL，提前做好优化， 省得 DBA 公开处刑。注意，生产环境不推荐打印执行 SQL，会有数据泄漏风险，仅推荐本地开发使用。2. 添加依赖在父项目的 pom.xml 文件中，声明 p6spy 依赖的版本号： <properties> // 省略... <p6spy.version>3.9.1</p6spy.version> </properties> <dependencyManagement> <dependencies> // 省略... <dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> <version>${p6spy.version}</version> </dependency> </dependencies> </dependencyManagement> 然后在具体的业务模块中的 pom.xml 文件中，引入该依赖： <dependency> <groupId>p6spy</groupId> <artifactId>p6spy</artifactId> </dependency> 3. 添加配置3.1 修改 application-dev.yml 配置文件spring: datasource: driver-class-name: com.p6spy.engine.spy.P6SpyDriver url: jdbc:p6spy:mysql://127.0.0.1:3306/weblog?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull ... 注意：driver-class-name : 修改为 p6spy 提供的驱动类;url ： 修改为前缀为 jdbc:p6spy 跟着冒号，后面对应数据库连接地址；3.2 添加 p6spy 配置文件然后在模块的 resources 目录下添加 spy.properties 配置文件：配置文件内容如下：#3.2.1以上使用 modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory #3.2.1以下使用或者不配置 #modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory # 自定义日志打印 logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger #日志输出到控制台 appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger # 使用日志系统记录 sql #appender=com.p6spy.engine.spy.appender.Slf4JLogger # 设置 p6spy driver 代理 deregisterdrivers=true # 取消JDBC URL前缀 useprefix=true # 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset. excludecategories=info,debug,result,commit,resultset # 日期格式 dateformat=yyyy-MM-dd HH:mm:ss # 实际驱动可多个 #driverlist=org.h2.Driver # 是否开启慢SQL记录 outagedetection=true # 慢SQL记录标准 2 秒 outagedetectioninterval=2 4. 生产环境不要启用 p6spyp6spy 组件请勿在生产环境使用，因为有性能损耗，推荐仅本地 dev 环境开发开启使用。 最终做法是，在 applcation-prod.yml 文件不启用 p6spy 组件，我们将 application-dev.yml 的数据库配置复制一份到 application-prod.yml 文件中，注意，要将驱动、前缀恢复正常，内容如下： driver-class-nam…

Springboot 整合持久层框架 Mybatis Plus
type: item
date: 2024-11-20T10:26:00+08:00
description: 1. 什么是 MyBatis Plus？MyBatis Plus （简称 MP） 是一款持久层框架，说白话就是一款操作数据库的框架。它是一个 MyBatis 的增强工具，就像 iPhone手机一般都有个 plus 版本一样，它在 MyBatis 的基础上只做增强不做改变，为简化开发、提高效率而生。MyBatis Plus 的愿景是成为 MyBatis 最好的搭档，效率翻倍。2. MyBatis Plus 的优势快速开发：MyBatis Plus 提供了一系列的便捷功能，如自动生成 SQL 语句、通用 Mapper 等，使数据库操作更加高效，能够节省开发时间。更少的配置： Spring Boot 已经为我们提供了很多默认的配置，整合 MyBatis Plus 时只需少量的配置，减少了繁琐的配置步骤。内置分页插件：MyBatis Plus 内置了分页插件，无需额外的代码，就可以轻松实现分页查询。更好的支持： MyBatis Plus 在社区中有较广泛的使用，拥有活跃的维护者和开发者，您可以轻松找到解决方案和文档。3. 整合3.1 添加依赖在父项目的 pom.xml 文件中，声明 MP 的依赖版本号： <properties> // 省略... <mybatis-plus.version>3.5.2</mybatis-plus.version> </properties> <dependencyManagement> <dependencies> // 省略... <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>${mybatis-plus.version}</version> </dependency> </dependencies> </dependencyManagement> 然后，在具体的业务模块的 pom.xml 文件中，引入 MP 和 MySQL 依赖： <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> 3.2 配置文件编辑 applicaiton-dev.yml 文件，添加数据库链接相关的配置，包含连接池的配置：spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/weblog?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull username: root password: 123456 hikari: minimum-idle: 5 maximum-pool-size: 20 auto-commit: true idle-timeout: 30000 pool-name: Weblog-HikariCP max-lifetime: 1800000 connection-timeout: 30000 connection-test-query: SELECT 1 数据库链接池这块，我们使用的 Spring Boot 默认的 HikariCP，它是一个高性能的连接池实现 , 同时，它号称是速度最快的连接池：spring.datasource.hikari.minimum-idle: Hikari连接池中最小空闲连接数。spring.datasource.hikari.maximum-pool-size: Hikari连接池中允许的最大连接数。spring.datasource.hikari.auto-commit: 连接是否自动提交事务。spring.datasource.hikari.idle-timeout: 连接在连接池中闲置的最长时间，超过这个时间会被释放。spring.datasource.hikari.pool-name: 连接池的名字。spring.datasource.hikari.max-lifetime: 连接在连接池中的最大存活时间，超过这个时间会被强制关闭。spring.dat…

Spring Boot 实现 JSR 380优雅的参数校验
type: item
date: 2024-11-19T15:36:00+08:00
description: 1. 介绍在业务开发中，我们需要对前端传入的各种参数做格式、长度、合规性校验，如果全部用if去判断写在业务代码里，观感非常的不好，也不够轻量。2. JSR 380 参数校验注解Spring Boot 提供了简洁的方法，让我们能够利用 Java 校验 API (JSR 380) 中定义的注解进行参数校验。JSR 380，也被称为 Bean Validation 2.0，是 Java Bean 验证规范的一个版本。该规范定义了一系列注解，用于验证 Java Bean 对象的属性，确保它们满足某些条件或限制。以下是 JSR 380 中提供的主要验证注解及其描述：@NotNull: 验证对象值不应为 null。@AssertTrue: 验证布尔值是否为 true。@AssertFalse: 验证布尔值是否为 false。@Min(value): 验证数字是否不小于指定的最小值。@Max(value): 验证数字是否不大于指定的最大值。@DecimalMin(value): 验证数字值（可以是浮点数）是否不小于指定的最小值。@DecimalMax(value): 验证数字值（可以是浮点数）是否不大于指定的最大值。@Positive: 验证数字值是否为正数。@PositiveOrZero: 验证数字值是否为正数或零。@Negative: 验证数字值是否为负数。@NegativeOrZero: 验证数字值是否为负数或零。@Size(min, max): 验证元素（如字符串、集合或数组）的大小是否在给定的最小值和最大值之间。@Digits(integer, fraction): 验证数字是否在指定的位数范围内。例如，可以验证一个数字是否有两位整数和三位小数。@Past: 验证日期或时间是否在当前时间之前。@PastOrPresent: 验证日期或时间是否在当前时间或之前。@Future: 验证日期或时间是否在当前时间之后。@FutureOrPresent: 验证日期或时间是否在当前时间或之后。@Pattern(regexp): 验证字符串是否与给定的正则表达式匹配。@NotEmpty: 验证元素（如字符串、集合、Map 或数组）不为 null，并且其大小/长度大于0。@NotBlank: 验证字符串不为 null，且至少包含一个非空白字符。@Email: 验证字符串是否符合有效的电子邮件格式。除了上述的标准注解，JSR 380 也支持开发者定义和使用自己的自定义验证注解。此外，这个规范还提供了一系列的APIs和工具，用于执行验证和处理验证结果。大部分现代Java框架（如 Spring 和 Jakarta EE）都与 JSR 380 兼容，并支持其验证功能。3. 项目集成3.1 引入依赖pom.xml 文件添加参数校验依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>3.2 实体类参数校验package com.windblog.web.model; import lombok.Data; import javax.validation.constraints.*; @Data public class User { // 用户名 @NotBlank(message = "用户名不能为空") private String username; // 性别 @NotNull(message = "性别不能为空") private Integer sex; // 年龄 @NotNull(message = "年龄不能为空") @Min(value = 18, message = "年龄必须大于或等于 18") @Max(value = 100, message = "年龄必须小于或等于 100") private Integer age; // 邮箱 @NotBlank(message = "邮箱不能为空") @Email(message = "邮箱格式不正确") private String email; } 3.3 Controller 参数校验针对每个字段的校验注解添加完成后，还需要在 controller 层进行捕获，并将错误信息返回。编辑 TestController 类，代码如下：@RestController @Slf4j public class TestController { @PostMapping("/test") @ApiOperationLog(description = "测试接口") public ResponseE…

Spring Boot 自定义注解，AOP切面实现 API 请求日志记录
type: item
date: 2024-11-14T11:20:00+08:00
description: 1.前言在后端业务中，对每次请求的入参、被请求类、方法，以及出参、执行耗时等信息进行日志打印，是很有必要的，有了这些信息，当某个接口出现问题时，可以帮助我们快速完成问题的追踪。那么，Spring Boot 中要如何实现呢? 相对于这种与主流程有一定的功能解耦的相对独立小功能可以使用springboot的切面来在关键地方织入我们的程序。2.什么是自定义注解 (Custom Annotations)？Java 注解是从 Java 5 开始引入的，它为我们提供了一种元编程的方法，允许我们在不改变代码逻辑的情况下为代码添加元数据。这些元数据可以在编译时或运行时通过反射被访问。自定义注解就是用户定义的，用于为代码提供元数据的注解。例如，本小节中自定义的 @ApiOperationLog 注解，它用来表示一个方法在执行时需要被记录日志。3.什么是 AOP (面向切面编程)？AOP（Aspect-Oriented Programming，面向切面编程）是一个编程范式，它提供了一种能力，让开发者能够模块化跨多个对象的横切关注点（例如日志、事务管理、安全等）。主要概念包括：切点 (Pointcuts): 定义在哪里应用切面（即在哪里插入横切关注点的代码）。通知 (Advices): 定义在特定切点上要执行的代码。常见的通知类型有：前置通知、后置通知、环绕通知等。切面 (Aspects): 切面将切点和通知结合起来，定义了在何处和何时应用特定的逻辑。例如，使用AOP，我们可以为所有使用 @ApiOperationLog 注解的方法自动添加日志逻辑，而不需要在每个方法中手动添加。4.开始动手4.1 添加依赖添加 jackson 工具，它用于将出入参转为 json 字符串： <properties> ...省略 <jackson.version>2.15.2</jackson.version> </properties> <dependencies> ...省略 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> </dependencies> 4.2 自定义注解新建一个名为 aspect 的包，用于放置切面相关的功能类接着，在其中创建一个名为 ApiOperationLog 的自定义注解：package com.wind.weblog.common.aspect; import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Documented public @interface ApiOperationLog { /** * 日志方法描述 * * @return */ String description() default ""; } 元注解说明：@interface用来声明这是一个注解，接口是interface来声明@Retention(RetentionPolicy.RUNTIME)： 这个元注解用于指定注解的保留策略，即注解在何时生效。RetentionPolicy.RUNTIME 表示该注解将在运行时保留，这意味着它可以通过反射在运行时被访问和解析。@Target({ElementType.METHOD})： 这个元注解用于指定注解的目标元素，即可以在哪些地方使用这个注解。ElementType.METHOD 表示该注解只能用于方法上。这意味着您只能在方法上使用这个特定的注解。@Documented： 这个元注解用于指定被注解的元素是否会出现在生成的Java文档中。如果一个注解使用了 @Documented，那么在生成文档时，被注解的元素及其注解信息会被包含在文档中。这可以帮助文档生成工具（如 JavaDoc）在生成文档时展示关于注解的信息。4.3 日志切面aspectj 注解说明在配置 AOP 切面之前，我们需要了解下 aspectj 相关注解的作用：@Aspect：声明该类为一个切面类；@Pointcut：定义一个切点，后面跟随一个表达式，表达式可以定义为切某个注解，也可以切某个 package 下的方法；切点定义…

Spring Boot 整合 Logback 日志替代 log4j
type: item
date: 2024-11-14T10:09:00+08:00
description: 1.介绍Logback 是日志框架 SLF4J 的一个实现，它被设计用来替代 log4j。Logback 提供了更高的性能，更丰富的日志功能和更好的配置选项。1.1 为什么要用它？在 Spring Boot 中，Logback 是默认的日志实现，至于官方为何用它作为默认日志组件，有以下几个原因：性能：Logback 在性能上超越了许多其他的日志实现，尤其是在高并发环境下。灵活性：Logback 提供了高度灵活的日志配置方式，支持从 XML、Groovy 以及编程式的方式进行配置。功能丰富：除了基本的日志功能，Logback 还提供了如日志归档、日志级别动态修改、事件监听等高级功能。与 SLF4J 集成：SLF4J 是一个日志门面（facade），使得应用程序可以在运行时更换日志实现。Logback 作为 SLF4J 的一个原生实现，可以无缝地与其集成。与 Spring Boot 的自动配置集成：Spring Boot 提供了对 Logback 的自动配置，这意味着开发者无需手动配置 Logback，只需提供一个简单的配置文件即可。2.引入依赖由于 Spring Boot 默认使用 Logback，所以当你在 pom.xml 中加入 spring-boot-starter-web 依赖时，它会自动包含 Logback 相关依赖，无需额外添加：<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>3.自定义 Logback 配置为了满足特定的日志需求，我们通常会自定义 Logback 配置。这里需要注意，配置文件我们统一放置在 weblog-web 模块中，方便统一管理。然后在 src/main/resources 目录下，创建一个名为 logback-weblog.xml 的文件。<?xml version="1.0" encoding="UTF-8"?> <configuration > <jmxConfigurator/> <include resource="org/springframework/boot/logging/logback/defaults.xml" /> <property scope="context" name="appName" value="weblog" /> <property name="LOG_FILE" value="/app/weblog/logs/${appName}.%d{yyyy-MM-dd}"/> <property name="FILE_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/> <!--<property name="CONSOLE_LOG_PATTERN" value="${FILE_LOG_PATTERN}"/>--> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>${LOG_FILE}-%i.log</FileNamePattern> <MaxHistory>30</MaxHistory> <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>10MB</maxFileSize> </TimeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${FILE_LOG_PATTERN}</pattern> </encoder> </appender> <springProfile name="dev"> <include resource="org/springframework/boot/logging/logback/console-appender.xml" /> <root leve…

用宝塔轻松部署java springboot的前后端分离项目
type: item
date: 2024-11-13T11:17:00+08:00
description: 1.介绍传统启动java项目需要上传jar然后用命令行nohup java -jar去启动，然后前端也是单独启动，同时还需配置nginx，稍不留神出了差错就要定位很久，而宝塔运维面板对于前后端分离的项目，只需一个步骤就可以集合管理前后端同时自动设置好nginx和域名等，很方便，还能实时监测系统占用查看日志也很方便，所以推荐这种方式2.前置准备云服务器安装完宝塔 这个直接百度有一键脚本宝塔安装jdk 软件商店搜索jdk即可 这次项目是用的1.8宝塔安装数据库 并将后端的配置中的正式环境数据库地址改为宝塔的数据库信息购买一个域名（没有就用ip:端口访问 最好是买一个）springboot项目已打包成jar前端项目已打包（这里是vue3项目 打包的是dist包）3.步骤创建一个文件夹，上传你的jar包和前端包注意前端包要解压网站里添加java项目不出意外你保存完就能看到项目运行了点击项目，在左侧可以看到项目的启动日志，方便看为啥挂了左侧的ssl可以申请免费域名证书，开启https访问，注意你先将你的域名解析到你的服务器然后访问你的域名就可以浏览你的网站了！

springboot项目配置Lombok 避免编写那些冗余的 Java 样板式代码
type: item
date: 2024-11-13T09:35:00+08:00
description: 1.介绍Lombok 是一个超酷的 Java 库，它能让你避免编写那些冗余的 Java 样板式代码，如对象中的 get、set、toString 等方法，解放你的双手，堪称偷懒神器，在企业级项目开发中，是必会的一个库。1.1 简化 Getter 和 Setter 方法在传统的 Java 开发中，你经常需要为每个类的属性手动编写 Getter 和 Setter 方法，但是有了 Lombok，你只需要在属性上加上 @Getter 和 @Setter 注解，Lombok 就会为你自动生成这些方法。1.2 自动生成构造函数通过 @NoArgsConstructor、@RequiredArgsConstructor 或 @AllArgsConstructor 注解，你可以快速生成无参构造函数、带有必需参数的构造函数或者带有全部参数的构造函数。不用每个需要注入的对象都添加@Autowrite了1.3 自动生成 equals 和 hashCode 方法通过 @EqualsAndHashCode 注解，Lombok 会根据类的字段自动生成 equals() 和 hashCode() 方法，让你的类更易于比较和使用在集合中。1.4 日志记录更轻松使用 @Slf4j 注解，你可以直接在类中使用 log 对象，而无需手动创建日志记录器。1.5 简化异常抛出通过 @SneakyThrows 注解，你可以在方法中抛出受检异常，而无需显式地在方法上声明或捕获它们，但是用的较少，业务中一般的异常都需要catch去处理1.6 数据类简化使用 @Data 注解，Lombok 会为你自动生成所有常用方法，如 Getter、Setter、toString() 等，让你的数据类更加简洁1.7 链式调用使用 @Builder 注解，Lombok 可以帮你创建一个更优雅的构建器模式，让你的对象初始化更加流畅。例子：ArticleDO articleDO = ArticleDO.builder() .title(publishArticleReqVO.getTitle()) .titleImage(publishArticleReqVO.getTitleImage()) .description(publishArticleReqVO.getDescription()) .build();2.项目中添加 Lombok 依赖想要在项目中使用 Lombok, 首先, 你需要在项目中添加 Lombok 依赖。只需要在项目的 pom.xml 文件中添加以下依赖：<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.28</version> <scope>provided</scope> </dependency>3.IDEA 配置 Lombok 插件TIP: IDEA 2020.3 及以上版本已经内置安装了 Lombok 插件，如果你的 IDEA 版本大于该版本，则不用管，否则需要按下面的步骤，来手动安装 Lombok 插件。除了添加依赖外，还需要在 IDEA 中安装 Lombok 插件，才能正式的使用 Lombok。根据你的系统依次点击菜单：Windows 系统：File -> Settings... -> Plugins;Mac 系统：IntelliJ IDEA -> Preferences -> Plugins;点击 Marketplace ， 进入插件市场, 输入关键词 lombok, 搜索该插件：然后，点击 Install 按钮开始安装，这里已经安装过了，所以显示的打钩。安装完成后，点击 Apply 按钮应用设置即可。

JAVA 使用aspose-words 实现 word转pdf
type: item
date: 2023-12-05T15:57:00+08:00
description: 2023.12.5 更新项目运行中发现，引入了org.apache.poi太多的包，导致内部有依赖冲突且很不好排查，修改后直接使用aspose-words的包就能实现word转换pdf更新后的pom依赖<dependency> <groupId>com.luhuiguo</groupId> <artifactId>aspose-words</artifactId> <version>23.1</version> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.5</version> </dependency>核心代码：import com.aspose.words.Document; import com.aspose.words.License; import com.aspose.words.SaveFormat; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; public class WordToPdf { public static File docToPdf(String docPath, String pdfPath) { System.out.println("WORD转化PDF开始>>>>"); //转化用时 File pdfFile = new File(pdfPath); try { long old = System.currentTimeMillis(); String s = "<License><Data><Products><Product>Aspose.Total for Java</Product><Product>Aspose.Words for Java</Product></Products><EditionType>Enterprise</EditionType><SubscriptionExpiry>20991231</SubscriptionExpiry><LicenseExpiry>20991231</LicenseExpiry><SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber> <Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature></License>"; ByteArrayInputStream is = new ByteArrayInputStream(s.getBytes()); License license = new License(); license.setLicense(is); Document document = new Document(docPath); FileOutputStream outputStream = new FileOutputStream(pdfFile); document.save(outputStream, SaveFormat.PDF); long now = System.currentTimeMillis(); outputStream.close(); is.close(); System.out.println("WORD转化PDF共耗时：" + ((now - old) / 1000.0) + "秒"); //转化用时 } catch (Exception e) { System.out.println("转化失败"); e.printStackTrace(); } return pdfFile; } public static File docToPdf(InputStream docPathInputStream, String pdfPath) { File pdfFile = new File(pdfPath); try { long old = System.currentTimeMillis(); String s = "<License><Da…