JEditorPaneHTMLEditorKitParserMethodConnectionPendingException连接挂起

wen java案例 1

本文目录导读:

JEditorPaneHTMLEditorKitParserMethodConnectionPendingException连接挂起

  1. 核心原因
  2. 解决方案
  3. 重要提示

你遇到的 ConnectionPendingException 异常,通常发生在使用 JEditorPaneHTMLEditorKit 加载远程 HTML 内容时,底层网络连接(如 HTTP 或 HTTPS)由于过慢、被阻塞或无法建立而导致超时或中断

这个问题在 Java Swing 应用中较为常见,尤其是当网络请求涉及大量资源、超时配置不正确或网络环境不稳定时。

以下是该问题的原因分析及解决方案:

核心原因

  • 网络超时:默认的 HttpURLConnection 或底层 socket 连接超时,但 HTMLEditorKit 的解析器(Parser)没有正确处理该异常。
  • 连接挂起:服务器没有响应(例如服务器过载、DNS 解析挂起),导致连接一直处于 pending 状态。
  • 代理问题:如果应用处于代理环境但未正确配置代理。
  • 死锁或阻塞:在 Swing 事件调度线程(EDT,Event Dispatch Thread)中直接进行网络加载,导致 UI 线程阻塞,进而引发连接超时或挂起。
  • HTTPS 证书问题:某些不安全的 HTTPS 连接会导致连接失败,但错误被包装为 ConnectionPendingException

解决方案

设置连接超时(推荐)

通过自定义 EditorKit 或修改 JEditorPane 的底层连接属性,设置明确的连接和读取超时时间。

import javax.swing.*;
import javax.swing.text.html.*;
import java.net.URL;
import java.net.URLConnection;
public class SafeHtmlViewer {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        JEditorPane editorPane = new JEditorPane();
        editorPane.setEditable(false);
        editorPane.setContentType("text/html");
        // 使用自定义 HTMLEditorKit 以控制连接
        HTMLEditorKit kit = new HTMLEditorKit() {
            @Override
            public Document createDefaultDocument() {
                HTMLDocument doc = (HTMLDocument) super.createDefaultDocument();
                // 增加一个自定义的异步加载器
                doc.setAsynchronousLoadPriority(0); // 0 表示同步加载,但更建议在后台线程加载
                return doc;
            }
        };
        editorPane.setEditorKit(kit);
        // 在后台线程加载页面,避免阻塞 EDT
        SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
            @Override
            protected Void doInBackground() throws Exception {
                URL url = new URL("https://your-slow-website.com");
                URLConnection conn = url.openConnection();
                // 关键设置:连接和读取超时
                conn.setConnectTimeout(5000);   // 5秒连接超时
                conn.setReadTimeout(5000);      // 5秒读取超时
                // 设置到 JEditorPane
                editorPane.setPage(conn.getURL());
                return null;
            }
            @Override
            protected void done() {
                // 更新 UI(可选)
            }
        };
        worker.execute();
        frame.add(new JScrollPane(editorPane));
        frame.setSize(800, 600);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

禁用自动异步加载(关键)

HTMLEditorKit 默认会使用异步加载来获取图片和 CSS,如果网络慢,这可能会导致解析器挂起。设置 asynchronousLoadPriority-1 来完全禁用异步加载,或者设置为 0 强制同步。

// 在你的 HTMLEditorKit 中
HTMLDocument doc = (HTMLDocument) editorPane.getDocument();
// 关键:禁用异步加载,避免连接挂起
doc.setAsynchronousLoadPriority(-1);

使用 S 外部 HttpClient + 流式加载

如果你需要更精细的连接控制(如 Proxy、认证),最可靠的方式是不使用 JEditorPane.setPage(),而是自己通过 HttpURLConnectionApache HttpClient 下载 HTML 内容字符串,然后通过 setText() 设置到 JEditorPane

SwingWorker<String, Void> worker = new SwingWorker<String, Void>() {
    @Override
    protected String doInBackground() throws Exception {
        URL url = new URL("https://example.com");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(5000);
        conn.setReadTimeout(5000);
        conn.setRequestMethod("GET");
        // 读取响应
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
            StringBuilder content = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }
            return content.toString();
        }
    }
    @Override
    protected void done() {
        try {
            String htmlContent = get();
            // 设置到 JEditorPane,此时完全基于本地字符串,不会有网络连接问题
            editorPane.setText(htmlContent);
            // 注意:setText 后需要移到光标位置或触发布局
            editorPane.setCaretPosition(0);
        } catch (Exception e) {
            // 处理异常(如超时)
            editorPane.setText("<html><body>加载失败:" + e.getMessage() + "</body></html>");
        }
    }
};
worker.execute();

重要提示

  • 永远不要在 EDT 中执行网络操作:即使是 JEditorPane.setPage() 看似一行代码,内部也会发起网络请求,如果不使用 SwingWorkerThread,UI 会冻结,进而导致连接挂起。
  • 检查你的 HTML 内容是否引用了不可达的外部资源(CSS、JS、图片)?即使主页面加载成功,如果引用的资源超时,也可能触发此异常。HTMLEditorKit 的同步/异步加载优先级对这部分资源也会产生影响。
  • 如果控制台有 SSL 警告,可能需要导入证书或设置 TrustManager
方案 适用场景 关键代码
方案一 简单的超时问题 setConnectTimeout(5000) + setReadTimeout(5000)
方案二 异步加载资源导致挂起 doc.setAsynchronousLoadPriority(-1)
方案三 需要精细网络控制/代理 自己下载 HTML 再 setText()

最彻底的解决方法是方案三(手动下载 + setText),因为它完全绕过了 HTMLEditorKit 内建的、容易出错的网络连接逻辑。

抱歉,评论功能暂时关闭!