pdf生成排查记录与解决方案

pdf生成排查记录与解决方案

第一次出错:ClassNotFound异常

错误信息

复制代码
Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: org/springframework/http/client/ClientHttpRequestFactory

排查步骤

1. 验证类路径是否存在
java 复制代码
package com.tgerp.workaffairs.service.impl;

public class ClassPathTest {
    public static void main(String[] args) {
        try {
            Class<?> clazz = Class.forName("org.springframework.http.client.ClientHttpRequestFactory");
            System.out.println("Class found: " + clazz);
        } catch (ClassNotFoundException e) {
            System.out.println("Class not found in classpath");
        }
    }
}

结果:类存在,排除基础依赖缺失。

2. 依赖分析
  • 问题spring-web依赖缺失或版本不匹配
  • 检查方法
    • IDEA依赖分析工具
    • Maven依赖树检查
3. 解决方案

Maven项目:

xml 复制代码
<!-- 非Spring Boot项目 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>与你的Spring核心版本一致</version>
</dependency>

<!-- Spring Boot项目 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Gradle项目:

groovy 复制代码
implementation 'org.springframework:spring-web:版本号'
// 或Spring Boot项目
implementation 'org.springframework.boot:spring-boot-starter-web'
4. 依赖冲突排查
bash 复制代码
# Maven项目
mvn dependency:tree

# Gradle项目
gradle dependencies
5. 构建清理
bash 复制代码
# Maven
mvn clean install

# Gradle
./gradlew clean build

# IDE缓存清理
# IntelliJ: File → Invalidate Caches and Restart
# Eclipse: Project → Clean
6. 依赖完整性检查
bash 复制代码
# Linux/Mac
find . -name "*.jar" | xargs grep -l "ClientHttpRequestFactory"

# Windows PowerShell
Get-ChildItem -Recurse -Filter "*.jar" | ForEach-Object {
    if (Select-String -Path $_.FullName -Pattern "ClientHttpRequestFactory" -Quiet) {
        $_.FullName
    }
}

第二次出错:连接被拒绝

错误信息

复制代码
org.springframework.web.client.ResourceAccessException: 
I/O error on POST request for "https://xxxxxx:8443/xxxx": 
Connection refused: connect; 
nested exception is java.net.ConnectException: Connection refused: connect

HTTP/HTTPS连接过程

  1. 发起连接:当你的浏览器(或任何客户端程序)想要访问 http://www.example.com 时,它知道需要连接服务器的 80 端口(HTTP默认端口)或 443 端口(HTTPS默认端口)。

  2. 请求操作系统:应用程序向操作系统发起一个网络连接请求(例如,调用 socket() 和 connect() 系统调用)。

  3. 操作系统分配端口:你的操作系统会从它的 "本地临时端口范围" 中,挑选一个当前未被使用的端口。

    • 在Linux/Unix/Windows系统中,这个范围通常是 1024 到 65535。

    • 端口 0-1023 被称为"知名端口",通常需要管理员权限才能绑定,用于HTTP、FTP、SSH等标准服务。

  4. 建立连接:操作系统将这个随机选择的端口(例如 54321)作为 源端口,将目标服务器的IP地址和端口(例如 93.184.216.34:80)作为 目标地址,发起TCP三次握手。

  5. 端口绑定:一旦连接建立,这个 本地IP:54321 到 服务器IP:80 的"套接字"组合在整个通信会话期间(直到你关闭网页)都会保持不变。

通信过程如下:

• 你的请求包:[源IP:你的IP, 源端口:54321] -> [目标IP:服务器IP, 目标端口:80]

• 服务器的响应包:[源IP:服务器IP, 源端口:80] -> [目标IP:你的IP, 目标端口:54321]
失败
成功
需要
不需要
客户端
TCP三次握手
是否成功?
Connection refused

TCP连接问题
TLS/SSL握手
是否需要客户端证书?
双向认证

需提供客户端证书
单向认证

服务器证书验证
HTTPS加密通信

排查步骤

1. 网络连通性检查
bash 复制代码
# 检查TCP连接(替换实际地址)
telnet xxxxxx 8443
# 或
nc -zv xxxxxx 8443

# Windows PowerShell
Test-NetConnection xxxxxx -Port 8443
2. 防火墙检查
bash 复制代码
# Linux检查防火墙
sudo iptables -L
sudo firewall-cmd --list-all

# Windows检查防火墙
netsh advfirewall show allprofiles
3. SSL证书验证
java 复制代码
// 临时绕过SSL验证(仅用于测试,生产环境不推荐)
@Configuration
public class SSLConfig {
    
    @PostConstruct
    public void disableSSLValidation() throws Exception {
        // 仅用于测试环境
        TrustManager[] trustAllCerts = new TrustManager[]{
            new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() { return null; }
                public void checkClientTrusted(X509Certificate[] certs, String authType) { }
                public void checkServerTrusted(X509Certificate[] certs, String authType) { }
            }
        };
        
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
    }
}
4. 连接超时设置
java 复制代码
@Configuration
public class RestTemplateConfig {
    
    @Bean
    public RestTemplate restTemplate() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(5000); // 5秒连接超时
        factory.setReadTimeout(10000);    // 10秒读取超时
        return new RestTemplate(factory);
    }
}

根本原因

端口访问权限问题 :应用程序所在服务器无法访问目标服务器的8443端口。
白名单问题:应用程序所在服务器未加入到对方的白名单。

解决方案

  1. 联系网络管理员,开通服务器之间的端口访问权限
  2. 检查目标服务是否正常运行在8443端口
  3. 验证网络策略和防火墙规则

第三次出错:文件路径格式错误

错误信息

复制代码
java.io.FileNotFoundException: 
file:\\D:\\Program%20Files\\tgerp-cloud-server\\tgerp-modules-workaffairs.jar!\\BOOT-INF\\classes!\\template\\stamp.svg 
(文件名、目录名或卷标语法不正确。)

问题分析

错误路径格式

复制代码
file:\\D:\\Program%20Files\\tgerp-cloud-server\\tgerp-modules-workaffairs.jar!\\BOOT-INF\\classes!\\template\\stamp.svg

问题

  1. 双重"!"分隔符jar!\\BOOT-INF\\classes!\\template\\stamp.svg不正确
  2. URL编码问题%20未正确解码为空格
  3. 协议格式错误 :应该是jar:file:///格式

正确路径格式

复制代码
jar:file:///D:/Program%20Files/tgerp-cloud-server/tgerp-modules-workaffairs.jar!/BOOT-INF/classes/template/stamp.svg

解决方案

方案1:使用ClassPathResource(推荐)
java 复制代码
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import java.io.InputStream;

// 读取JAR包内的资源
Resource resource = new ClassPathResource("template/stamp.svg");
InputStream inputStream = resource.getInputStream();

// 如果不在类路径根目录,使用相对路径
Resource resource2 = new ClassPathResource("static/template/stamp.svg");
方案2:使用类加载器
java 复制代码
// 获取资源流
InputStream inputStream = getClass().getClassLoader()
    .getResourceAsStream("template/stamp.svg");

// 获取资源URL
URL resourceUrl = getClass().getClassLoader()
    .getResource("template/stamp.svg");
方案3:修复文件路径(如果必须使用文件系统路径)
java 复制代码
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

// 解码URL编码的路径
String decodedPath = URLDecoder.decode("D:\\Program%20Files\\tgerp-cloud-server", 
    StandardCharsets.UTF_8.name());

// 使用正确的路径分隔符
String correctPath = decodedPath.replace("\\", "/") + 
    "/tgerp-modules-workaffairs.jar";

// 构建正确的JAR URL
String jarUrl = "jar:file:///" + correctPath + "!/BOOT-INF/classes/template/stamp.svg";
方案4:Spring Boot资源加载最佳实践
java 复制代码
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStream;

@Component
public class ResourceLoaderService {
    
    @Value("classpath:template/stamp.svg")
    private Resource stampResource;
    
    @PostConstruct
    public void loadResource() throws IOException {
        try (InputStream is = stampResource.getInputStream()) {
            // 处理资源
            // ...
        }
    }
    
    // 或者使用ResourceLoader
    @Autowired
    private ResourceLoader resourceLoader;
    
    public void loadResourceDynamic(String path) throws IOException {
        Resource resource = resourceLoader.getResource("classpath:" + path);
        // 使用资源...
    }
}

预防措施

  1. 统一资源加载方式 :在Spring Boot项目中统一使用ClassPathResourceResourceLoader
  2. 路径标准化 :使用Paths.get()toUri()方法处理路径
  3. 编码处理:始终对路径进行URL编码/解码处理
  4. 错误处理:添加适当的异常处理机制
java 复制代码
public class ResourceUtils {
    
    public static InputStream loadResource(String classpath) throws IOException {
        Resource resource = new ClassPathResource(classpath);
        if (!resource.exists()) {
            throw new FileNotFoundException("Resource not found: " + classpath);
        }
        return resource.getInputStream();
    }
    
    public static String getResourcePath(String classpath) {
        try {
            URL url = ResourceUtils.class.getClassLoader().getResource(classpath);
            return url != null ? url.getPath() : null;
        } catch (Exception e) {
            return null;
        }
    }
}

总结

问题 原因 解决方案
NoClassDefFoundError 依赖缺失或版本冲突 添加正确依赖,检查依赖树
Connection refused 网络连接问题 检查网络、防火墙、端口访问权限
FileNotFoundException JAR内资源路径格式错误 使用ClassPathResource或类加载器读取资源

最佳实践建议

  1. 使用Maven/Gradle依赖管理,避免版本冲突
  2. 网络连接配置适当的超时和重试机制
  3. 资源加载统一使用Spring的Resource API
  4. 完善的异常处理和日志记录
相关推荐
Sylvia-girl2 小时前
Java之异常
java·开发语言
Sylvia33.2 小时前
网球/羽毛球数据API:专业赛事数据服务的技术实现
java·前端·websocket·json
爱丽_2 小时前
Spring 框架
java·后端·spring
期待のcode2 小时前
浅堆深堆与支配树
java·jvm·算法
小北方城市网2 小时前
SpringBoot 集成 RabbitMQ 实战(消息队列):实现异步通信与系统解耦
java·spring boot·后端·spring·rabbitmq·mybatis·java-rabbitmq
开开心心_Every2 小时前
文件数量统计工具:支持多层文件夹数量统计
游戏·微信·pdf·excel·语音识别·swift·lisp
超级数据查看器2 小时前
超级数据查看器 更新日志(包含的功能)
android·java·数据库·sqlite·安卓
青衫码上行2 小时前
如何构建maven项目
java·学习·maven
不穿格子的程序员2 小时前
JVM篇2:根可达性算法-垃圾回收算法和三色标记算法-CMS和G1
java·jvm·g1·根可达性算法·三色标记算法