Java 提供靈活的協議處理擴充機制,允許開發者註冊自定義 URL 協議(如 brules://、myapp://)。本文深入解析兩種核心註冊方式的原理、差異與最佳實務。
Java 透過 URLStreamHandler 抽象類別處理特定協議的連線邏輯。取得處理器的進入點為 URL.getURLStreamHandler(String protocol)。註冊機制有二:
URL.setURLStreamHandlerFactory(URLStreamHandlerFactory)-Djava.protocol.handler.pkgs=package.prefixpublic interface URLStreamHandlerFactory {
URLStreamHandler createURLStreamHandler(String protocol);
}
// 實作範例
public class MyProtocolFactory implements URLStreamHandlerFactory {
@Override
public URLStreamHandler createURLStreamHandler(String protocol) {
if ("brules".equals(protocol)) {
return new BrulesHandler();
}
return null; // 交給下一層處理(JVM 內建或 pkg 機制)
}
}
// 僅能呼叫一次,通常在伺服器啟動階段(Tomcat、JBoss、WebSphere 等)
URL.setURLStreamHandlerFactory(new MyProtocolFactory());
Error("factory already defined")-Djava.protocol.handler.pkgs指定封裝前綴,JVM 會嘗試載入 {prefix}.{protocol}.Handler 類別。
// 協議:brules
// 參數:-Djava.protocol.handler.pkgs=cn.ccb.wfcp
// 類別:cn.ccb.wfcp.brules.Handler extends URLStreamHandler
package cn.ccb.wfcp.brules;
public class Handler extends URLStreamHandler {
@Override
protected URLConnection openConnection(URL u) throws IOException {
return new RulesURLConnection(u);
}
}
使用 | 分隔多個前綴:-Djava.protocol.handler.pkgs=cn.ccb.wfcp|cn.ccb.wfcp2
{pkg}.{protocol}.HandlerClass.forName() 載入問題:Handler 必須在 JVM Classpath,但業務類別(如 RulesFiles)屬於應用層,不應放入 JVM Classpath。
// JVM Classpath 層(協議處理核心)
package cn.ccb.wfcp.brules;
public interface IRulesFiles {
List getRuleElements();
long getLastModified();
InputStream getInputStream() throws IOException;
}
public class RulesURLConnection extends URLConnection {
@Override
public long getLastModified() {
return getIRulesFiles().getLastModified();
}
@Override
public InputStream getInputStream() throws IOException {
return getIRulesFiles().getInputStream();
}
private IRulesFiles getIRulesFiles() {
try {
// 關鍵:使用執行緒上下文 ClassLoader 載入應用層實作
Class> clazz = Class.forName(
"cn.ccb.wfcp.brules.protocol.RulesFiles",
true,
Thread.currentThread().getContextClassLoader()
);
return (IRulesFiles) clazz.newInstance();
} catch (Exception e) {
throw new IOException("Failed to load RulesFiles", e);
}
}
}
// 應用層 Classpath(Web 應用 WAR / EAR 內)
package cn.ccb.wfcp.brules.protocol;
public class RulesFiles implements IRulesFiles {
@Override
public long getLastModified() {
return RuleFilesDAOImpl.instance().getLastModified();
}
// ... 其他實作委派給真正的業務邏輯
}
| 維度 | Factory 註冊 | JVM 參數註冊 |
|---|---|---|
| 優先順序 | 最高(優先被查詢) | 次之(Factory 返回 null 才嘗試) |
| 適用層級 | 伺服器核心、獨立應用 | 應用層、外掛模組、共用庫 |
| 部署靈活性 | 需程式碼控制啟動流程 | 僅需調整啟動腳本參數 |
| 多協議隔離 | 單一 Factory 管理所有 | 套件前綴天然隔離 |
Thread.currentThread().getContextClassLoader() 載入應用層類別,否則會發生 ClassNotFoundException 或載入到錯誤版本setURLStreamHandlerFactory 需 SecurityPermission("setFactory"),受限環境可能失敗Handler 實例會被快取重用,確保無狀態或妥善同步erp://module/entity/idconfig://namespace/keyvfs://mount/pathsvc://service-name/endpoint