Java自定義類加載器實現(xiàn)類隔離詳解
一、背景
某服務(wù)需要連接操作多種組件(每種組件可能有多個版本),如kafka、mongodb、es、mysql等等,并且后續(xù)需要適配更多的組件。

主要難點:連接操作多組件多版本,且同種組件的不同版本所依賴的jar包可能不一樣,操作源碼也可能發(fā)生改變,項目無法直接依賴jar包,會產(chǎn)生類沖突
二、解決思路
由于每種組件的不同版本所依賴的jar包不同,我們可以借鑒tomcat的實現(xiàn)方式,通過自定義類加載器打破雙親委派機制來實現(xiàn)類隔離,從而達到操作多組件多版本的目的。

2.1 創(chuàng)建依賴所在目錄
針對每一種組件我們創(chuàng)建一個目錄,比如/data/kafka、/data/mongodb、/data/es等,且每種組件的不同版本創(chuàng)建對應(yīng)的子目錄,比如/data/kafka/0.10、/data/kafka/0.11,目錄結(jié)構(gòu)如下
| ----/data
| --------/kafka
| ------------/0.10
| ------------/0.11
| --------/mysql
| ------------/5.7
| ------------/8.0
| ...
把每種組件不同版本對應(yīng)的依賴包放在各個子目錄下面。
2.2 定義操作接口
在common公共模塊中定義一個接口AbstractOperator,該接口定義一些通用方法,如下:
public interface Operator {
/**
* 測試連接
* @param connectionInfo
* @return
*/
boolean testConnection(String connectionInfo);
/**
* 獲取組件版本
* @return
*/
String getVersion(String connectionInfo);
}再定義各種組件的接口,如KafkaOperator、MysqlOperator等,使其繼承該通用接口。組件接口內(nèi)部包含一些組件自身的操作,如KafkaOperator中定義了getTopics、createTopic、deleteTopic等方法。代碼如下:
public interface KafkaOperator extends Operator{
/**
* 獲取topic列表
* @param connectionInfo
* @return
*/
List<String> getTopics(String connectionInfo);
/**
* 創(chuàng)建topic
* @param connectionInfo
* @param topic
* @return
*/
boolean createTopic(String connectionInfo, String topic);
/**
* 刪除topic
* @param connectionInfo
* @param topic
* @return
*/
boolean deleteTopic(String connectionInfo, String topic);
}2.3 編寫并構(gòu)建業(yè)務(wù)包
大致步驟如下:
1.針對每種組件的不同版本,可以在項目下新建一個模塊,該模塊依賴common公共模塊
2.創(chuàng)建入口類com.kamier.Entry(所有組件的不同版本的入口類的全限定名統(tǒng)一為com.kamier.Entry),并實現(xiàn)對應(yīng)的組件接口,比如Kafka的0.10版本,那么就實現(xiàn)KafkaOperator接口。

3.編寫業(yè)務(wù)邏輯代碼
public class Entry implements KafkaOperator {
@Override
public List<String> getTopics(String connectionInfo) {
return null;
}
@Override
public boolean createTopic(String connectionInfo, String topic) {
return false;
}
@Override
public boolean deleteTopic(String connectionInfo, String topic) {
return false;
}
@Override
public boolean testConnection(String connectionInfo) {
return false;
}
@Override
public String getVersion(String connectionInfo) {
return null;
}
}4.打成jar包
5.將jar包放在對應(yīng)的目錄下,與依賴包同級,如/data/kafka/0.10
2.4 自定義類加載器
經(jīng)過前面的準備工作,組件的每個版本的目錄下都有了相應(yīng)的依賴包和業(yè)務(wù)包。
開始編寫一個自定義類加載器繼承URLClassLoader,重寫loadClass方法,優(yōu)先加載當前類加載器路徑下的class來打破雙親委派模式,代碼如下
public static class MyClassLoader extends URLClassLoader {
public MyClassLoader(URL[] urls) {
super(urls);
}
public Class<?> loadClass(String name) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// 先檢查當前類加載器是否已經(jīng)裝載該類
Class<?> c = findLoadedClass(name);
if (c == null) {
try {
// 在當前類加載器的路徑下查找
c = findClass(name);
} catch (ClassNotFoundException e) {
// 說明在當前類加載器的路徑下沒有找到
}
if (c == null) {
// 走雙親委派機制
if (getParent() != null) {
c = getParent().loadClass(name);
}
}
}
return c;
}
}
}針對每種組件的不同版本,我們創(chuàng)建與其對應(yīng)的自定義類加載器,并將該版本對應(yīng)目錄下的所有jar包(包括依賴包和業(yè)務(wù)包)的URL傳入。
2.5 主流程步驟
步驟如下:
當我們從頁面上接收到一個獲取Kafka(版本為0.10)topic列表的請求時,先判斷是否已經(jīng)初始化過Kafka(0.10版本)的類加載器,如果還未初始化,則進行類加載器的初始化。
URL[] urls = null;
File dir = new File("/data/kafka/0.10");
if (dir.isDirectory()) {
File[] files = dir.listFiles();
urls = new URL[files.length];
for (int i = 0; i < files.length; i++) {
urls[i] = files[i].toURL();
}
}
MyClassLoader contextClassLoader = new MyClassLoader(urls);通過類加載器加載入口類com.kamier.Entry并實例化,通過反射調(diào)用對應(yīng)的方法(組件與其對應(yīng)的方法列表可以統(tǒng)一維護在數(shù)據(jù)庫中)。
Class loadClass = contextClassLoader.loadClass("com.kamier.Entry");
Object entry = loadClass.newInstance();
Method method = loadClass.getDeclaredMethod("getTopics");
List<String> a = (List) method.invoke(entry, 參數(shù));獲取到結(jié)果并返回
三、總結(jié)
至此整個實現(xiàn)步驟就結(jié)束了,我們通過自定義類加載器的方式來實現(xiàn)類隔離,從而達到操作多組件多版本的目的。
到此這篇關(guān)于Java自定義類加載器實現(xiàn)類隔離詳解的文章就介紹到這了,更多相關(guān)Java類加載器實現(xiàn)類隔離內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java的Struts框架中<results>標簽的使用方法
這篇文章主要介紹了Java的Struts框架中<results>標簽的使用方法,Struts框架是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-11-11
SpringCloud使用Nacos保存和讀取變量的配置方法
在使用SpringCloud開發(fā)微服務(wù)時,經(jīng)常會遇到一些比較小的后臺參數(shù)配置,這些配置不足以單獨開一張表去存儲,而且其他服務(wù)會讀取該參數(shù),這篇文章主要介紹了SpringCloud使用Nacos保存和讀取變量,需要的朋友可以參考下2022-07-07
Java中redisTemplate注入失敗NullPointerException異常問題解決
這篇文章主要介紹了Java中redisTemplate注入失敗NullPointerException異常問題解決,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2023-08-08
Java concurrency之鎖_動力節(jié)點Java學(xué)院整理
這篇文章主要為大家詳細介紹了Java concurrency之鎖的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-06-06
Java弱鍵集合WeakHashMap及ConcurrentCache原理詳解
這篇文章主要介紹了Java弱鍵集合WeakHashMap及ConcurrentCache原理詳解,基于哈希表的Map接口實現(xiàn),支持null鍵和值,但是WeakHashMap具有弱鍵,可用來實現(xiàn)緩存存儲,在進行GC的時候會自動回收鍵值對,需要的朋友可以參考下2023-09-09
eclipse實現(xiàn)可認證的DH密鑰交換協(xié)議
這篇文章主要介紹了eclipse實現(xiàn)可認證的DH密鑰交換協(xié)議,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-06-06

