溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java怎么自定義類加載器實(shí)現(xiàn)類隔離

發(fā)布時(shí)間:2023-03-02 11:21:25 來(lái)源:億速云 閱讀:127 作者:iii 欄目:開發(fā)技術(shù)

這篇“Java怎么自定義類加載器實(shí)現(xiàn)類隔離”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來(lái)看看這篇“Java怎么自定義類加載器實(shí)現(xiàn)類隔離”文章吧。

    一、背景

    某服務(wù)需要連接操作多種組件(每種組件可能有多個(gè)版本),如kafka、mongodb、es、mysql等等,并且后續(xù)需要適配更多的組件。

    Java怎么自定義類加載器實(shí)現(xiàn)類隔離

    主要難點(diǎn):連接操作多組件多版本,且同種組件的不同版本所依賴的jar包可能不一樣,操作源碼也可能發(fā)生改變,項(xiàng)目無(wú)法直接依賴jar包,會(huì)產(chǎn)生類沖突

    二、解決思路

    由于每種組件的不同版本所依賴的jar包不同,我們可以借鑒tomcat的實(shí)現(xiàn)方式,通過(guò)自定義類加載器打破雙親委派機(jī)制來(lái)實(shí)現(xiàn)類隔離,從而達(dá)到操作多組件多版本的目的。

    Java怎么自定義類加載器實(shí)現(xiàn)類隔離

    2.1 創(chuàng)建依賴所在目錄

    針對(duì)每一種組件我們創(chuàng)建一個(gè)目錄,比如/data/kafka、/data/mongodb、/data/es等,且每種組件的不同版本創(chuàng)建對(duì)應(yīng)的子目錄,比如/data/kafka/0.10、/data/kafka/0.11,目錄結(jié)構(gòu)如下

    | ----/data
    | --------/kafka
    | ------------/0.10
    | ------------/0.11
    | --------/mysql
    | ------------/5.7
    | ------------/8.0
    | ...

    把每種組件不同版本對(duì)應(yīng)的依賴包放在各個(gè)子目錄下面。

    2.2 定義操作接口

    在common公共模塊中定義一個(gè)接口AbstractOperator,該接口定義一些通用方法,如下:

    public interface Operator {
        /**
         * 測(cè)試連接
         * @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.針對(duì)每種組件的不同版本,可以在項(xiàng)目下新建一個(gè)模塊,該模塊依賴common公共模塊

    2.創(chuàng)建入口類com.kamier.Entry(所有組件的不同版本的入口類的全限定名統(tǒng)一為com.kamier.Entry),并實(shí)現(xiàn)對(duì)應(yīng)的組件接口,比如Kafka的0.10版本,那么就實(shí)現(xiàn)KafkaOperator接口。

    Java怎么自定義類加載器實(shí)現(xiàn)類隔離

    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包放在對(duì)應(yīng)的目錄下,與依賴包同級(jí),如/data/kafka/0.10

    2.4 自定義類加載器

    經(jīng)過(guò)前面的準(zhǔn)備工作,組件的每個(gè)版本的目錄下都有了相應(yīng)的依賴包和業(yè)務(wù)包。

    開始編寫一個(gè)自定義類加載器繼承URLClassLoader,重寫loadClass方法,優(yōu)先加載當(dāng)前類加載器路徑下的class來(lái)打破雙親委派模式,代碼如下

    public static class MyClassLoader extends URLClassLoader {
    
            public MyClassLoader(URL[] urls) {
                super(urls);
            }
    
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                synchronized (getClassLoadingLock(name)) {
                    // 先檢查當(dāng)前類加載器是否已經(jīng)裝載該類
                    Class<?> c = findLoadedClass(name);
                    if (c == null) {
                        try {
                            // 在當(dāng)前類加載器的路徑下查找
                            c = findClass(name);
                        } catch (ClassNotFoundException e) {
                            // 說(shuō)明在當(dāng)前類加載器的路徑下沒(méi)有找到
                        }
    
                        if (c == null) {
                            // 走雙親委派機(jī)制
                            if (getParent() != null) {
                                c = getParent().loadClass(name);
                            }
                        }
                    }
                    return c;
                }
            }
        }

    針對(duì)每種組件的不同版本,我們創(chuàng)建與其對(duì)應(yīng)的自定義類加載器,并將該版本對(duì)應(yīng)目錄下的所有jar包(包括依賴包和業(yè)務(wù)包)的URL傳入。

    2.5 主流程步驟

    步驟如下:

    當(dāng)我們從頁(yè)面上接收到一個(gè)獲取Kafka(版本為0.10)topic列表的請(qǐng)求時(shí),先判斷是否已經(jīng)初始化過(guò)Kafka(0.10版本)的類加載器,如果還未初始化,則進(jìn)行類加載器的初始化。

    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);

    通過(guò)類加載器加載入口類com.kamier.Entry并實(shí)例化,通過(guò)反射調(diào)用對(duì)應(yīng)的方法(組件與其對(duì)應(yīng)的方法列表可以統(tǒng)一維護(hù)在數(shù)據(jù)庫(kù)中)。

    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é)果并返回。

    以上就是關(guān)于“Java怎么自定義類加載器實(shí)現(xiàn)類隔離”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對(duì)大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI