<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    I want to fly higher
    programming Explorer
    posts - 114,comments - 263,trackbacks - 0
    1.前言
       本篇通過自定義的一個ClassLoader初步實現(xiàn)基礎的HotSwap。

    2.HotSwapClassLoader源代碼,解釋詳見源代碼注釋

    package com.mavsplus.example.java.classloader;

    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.URI;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.Arrays;
    import java.util.HashSet;
    import java.util.Set;

    /**
     * 一個用于Hot-Swap的classloader,繼承自{@link java.lang.ClassLoader}
     * 
     * <pre>
     *     1.Java 中,有四種類型的類加載器,分別為:BootStrapClassLoader、ExtClassLoader、AppClassLoader 以及用戶自定義的 ClassLoader。
     *          這四種類加載器分別負責不同路徑的類的加載,并形成了一個類加載的層次結(jié)構(gòu)。
     *     2.BootStrapClassLoader 處于類加載器層次結(jié)構(gòu)的最高層,負責 sun.boot.class.path 路徑下類的加載,默認為 jre/lib 目錄下的核心 API 或 -Xbootclasspath 選項指定的 jar 包。
     *    ExtClassLoader 的加載路徑為 java.ext.dirs,默認為 jre/lib/ext 目錄或者 -Djava.ext.dirs 指定目錄下的 jar 包加載
     *    AppClassLoader 的加載路徑為 java.class.path,默認為環(huán)境變量 CLASSPATH 中設定的值。也可以通過 -classpath 選型進行指定。
     *    用戶自定義 ClassLoader 可以根據(jù)用戶的需要定制自己的類加載過程,在運行期進行指定類的動態(tài)實時加載。
     *  3.一般來說,這四種類加載器會形成一種父子關系,高層為低層的父加載器。在進行類加載時,首先會自底向上挨個檢查是否已經(jīng)加載了指定類,
     *    如果已經(jīng)加載則直接返回該類的引用。如果到最高層也沒有加載過指定類,那么會自頂向下挨個嘗試加載,直到用戶自定義類加載器,如果還不能成功,就會拋出異常
     * </pre>
     * 
     * <pre>
     *     1.每個類加載器有自己的名字空間,對于同一個類加載器實例來說,名字相同的類只能存在一個,并且僅加載一次。不管該類有沒有變化,下次再需要加載時,它只是從自己的緩存中直接返回已經(jīng)加載過的類引用。
     *  2.實現(xiàn) Java 類的熱替換,首先必須要實現(xiàn)系統(tǒng)中同名類的不同版本實例的共存。要想實現(xiàn)同一個類的不同版本的共存,我們必須要通過不同的類加載器來加載該類的不同版本。
     *    即不能把這些類的加載工作委托給系統(tǒng)加載器來完成,因為它們只有一份。
     *  3.不能采用系統(tǒng)默認的類加載器委托規(guī)則,也就是說我們定制的類加載器的父加載器必須設置為 null
        //landon:其實也可以使用類加載器委托規(guī)則,但是前提是加載的class不能在classpath下,否則的話會優(yōu)先被系統(tǒng)類加載器加載到
     * </pre>
     * 
     * <a href = "http://www.ibm.com/developerworks/cn/java/j-lo-hotswapcls/"></a>
     * 
     * @author landon
     * @since 1.8.0_25
     
    */
    public class HotSwapClassLoader extends ClassLoader {

        /** 加載的類文件根目錄目錄,存放加載的.class文件 */
        private URI reloadBaseDir;
        /** 加載的類名集合,F(xiàn)ull-Name,不在此集合的類委托給系統(tǒng)加載器來完成 */
        private Set<String> reloadClazzs;

        public HotSwapClassLoader(URI loadDir) {
            // 指定父加載器為null
            super(null);

            this.reloadBaseDir = loadDir;

            reloadClazzs = new HashSet<>();
        }

        /**
         * 指定加載的類
         * 
         * <p>
         * 通過根目錄+Full-Name找到.class
         * 
         * @param clazzNames
         
    */
        public void assignLoadedClazzs(String clazzNames) {
            for (String clazzName : clazzNames) {
                defineClassFromPath(getLoadedClassPath(clazzName), clazzName);
            }

            // 添加至加載的集合
            reloadClazzs.addAll(Arrays.asList(clazzNames));
        }

        /**
         * 根據(jù)類名獲取所在路徑
         * 
         * @param clazzName
         * @return
         
    */
        private Path getLoadedClassPath(String clazzName) {
            String pathName = clazzName.replace('.', File.separatorChar);
            String classPathName = pathName + ".class";

            return Paths.get(reloadBaseDir).resolve(classPathName);
        }

        /**
         * 從指定的Path接收類字節(jié)碼->轉(zhuǎn)換為Class實例
         * 
         * @param path
         * @param clazzFullName
         * @return
         
    */
        private Class<?> defineClassFromPath(Path path, String clazzFullName) {
            File classFile = path.toFile();
            int fileLength = (int) classFile.length();

            byte[] rawBytes = new byte[fileLength];

            try {
                InputStream in = Files.newInputStream(path);
                in.read(rawBytes);
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            // Converts an array of bytes into an instance of class Class. Before
            
    // the Class can be used it must be resolved.
            return defineClass(clazzFullName, rawBytes, 0, fileLength);
        }

        // Loads the class
        
    // 本類加載器只加載reloadClazzs中的類,其余的類委托給系統(tǒng)類加載器進行加載
      // Foo的接口類IFoo加載也會調(diào)用此方法,不過其實用系統(tǒng)類加載器進行加載的
        @Override
        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
            Class<?> clazz = null;

            // 每個類加載器都維護有自己的一份已加載類名字空間,其中不能出現(xiàn)兩個同名的類。凡是通過該類加載器加載的類,無論是直接的還是間接的,都保存在自己的名字空間.
            
    // 該方法即在該名字空間中尋找指定的類是否存在,如果存在舊返回給類的引用;否則就返回null;這里的直接是指存在于該類加載器的加載路徑上并由該加載器完成加載,
            
    // 間接是指由類加載器把類的加載工作委托給其他類加載器完成類的實際加載

            
    // landon:1.因在loadClass之前,我們調(diào)用了{@link #defineClassFromPath}->通過調(diào)用{@link
            
    // #defineClass}->已經(jīng)實現(xiàn)了將reloadClazzs中的class的裝載工作->所以如果通過loadClass方法加載的class是reloadClazzs中的,
            
    // 則通過findLoadedClass方法可直接獲取
            
    // 2.當然還有一種做法是直接覆蓋findClass方法,在該方法中調(diào)用defineClass方法.而findClass的調(diào)用時機則是在沒有找到加載的類時調(diào)用的.
            
    // 其默認實現(xiàn)是直接拋出一個ClassNotFoundException
            clazz = findLoadedClass(name);

            if (!this.reloadClazzs.contains(name) && clazz == null) {
                clazz = getSystemClassLoader().loadClass(name);
            }

            if (clazz == null) {
                throw new ClassNotFoundException(name);
            }

            if (resolve) {
                // Links the specified class
                resolveClass(clazz);
            }

            return clazz;
        }
    }


    3.用于reload的測試類IFoo,Foo

    package com.mavsplus.example.java.classloader;

    /**
     * IFoo接口
     * 
     * @author landon
     * @since 1.8.0_25
     
    */
    public interface IFoo {

        public void say();
    }

    package com.mavsplus.example.java.classloader;

    /**
     * 用于hotswap的類
     * 
     * @author landon
     * @since 1.8.0_25
     
    */
    public class Foo implements IFoo {

        @Override
        public void say() {
            // 打印了類所有的ClassLoader
            System.out.println("Hello,HotSwap.[version 1][ClassLoader-" + getClass().getClassLoader().getClass() + "]");
        }
    }


    4.入口調(diào)用類HotSwapExample,解釋詳見源代碼注釋

    package com.mavsplus.example.java.classloader;

    import java.lang.reflect.Method;
    import java.util.concurrent.TimeUnit;

    /**
     * Hot-Swap例子
     * 
     * @author landon
     * @since 1.8.0_25
     
    */
    public class HotSwapExample {

        public static void main(String[] args) throws Exception {
            HotSwapExample example = new HotSwapExample();

            while (true) {
                example.systemLoad();
                example.hotswapLoad();

                TimeUnit.SECONDS.sleep(3);
            }
        }

        // 這里為了測試,每次調(diào)用方法的時候都會初始化一個HotSwapClassLoader對象
        
    // 線上產(chǎn)品只需要在hotswap的時候初始化一個HotSwapClassLoader對象即可
        public void hotswapLoad() throws Exception {
            // 初始化ClassLoader,指定加載根目錄及加載的類,這里通過ClassLoader.getSystemResource獲取classpath所在的目錄(即target\classes目錄)
            
    // 這樣我們直接就可以eclipse中直接進行修改Foo,編譯->下一次HotSwapClassLoader就直接會加載最新的Foo.class
            HotSwapClassLoader classLoader = new HotSwapClassLoader(ClassLoader.getSystemResource("").toURI());
            classLoader.assignLoadedClazzs("com.mavsplus.example.java.classloader.Foo");

            // 調(diào)用loadClass方法加載類
            Class<?> clazz = classLoader.loadClass("com.mavsplus.example.java.classloader.Foo");

            // 實例化
            Object foo = clazz.newInstance();
            Method method = foo.getClass().getMethod("say");
            method.invoke(foo);

            // 這樣直接調(diào)用會拋出java.lang.ClassCastException,因為即使是同一個類文件,如果是由不同的類加載器實例加載的,那么他們的類型就是不同的
            
    // clazz對象是由HotSwapClassLoader加載的,而foo2的類型生命和轉(zhuǎn)型的Foo類都是由方法所屬的類加載(默認是AppClassLoader)加載的,因此是
            
    // 完全不同的類型,所以會拋出轉(zhuǎn)型異常
            
    // Foo foo2 = (Foo)clazz.newInstance();
            
    // foo2.say();

            
    // 這里通過接口進行調(diào)用,并沒有拋出ClassCastException.因為HotSwapClassLoader只指定加載了Foo,IFoo接口的加載則會委托給系統(tǒng)類加載器加載
            
    // 所以轉(zhuǎn)型可成功
            IFoo foo3 = (IFoo) clazz.newInstance();
            foo3.say();
        }

        public void systemLoad() throws Exception {
            Foo foo = new Foo();
            foo.say();
        }
    }


    5.測試,控制臺輸出,紅色部分即是我們直接修改了Foo的實現(xiàn)后的結(jié)果

    Hello,HotSwap.[version 1][ClassLoader-class sun.misc.Launcher$AppClassLoader]
    Hello,HotSwap.[version 1][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]
    Hello,HotSwap.[version 1][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]
    Hello,HotSwap.[version 1][ClassLoader-class sun.misc.Launcher$AppClassLoader]
    Hello,HotSwap.[version 1][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]
    Hello,HotSwap.[version 1][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]
    Hello,HotSwap.[version 1][ClassLoader-class sun.misc.Launcher$AppClassLoader]
    Hello,HotSwap.[version 1][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]
    Hello,HotSwap.[version 1][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]
    Hello,HotSwap.[version 1][ClassLoader-class sun.misc.Launcher$AppClassLoader]
    Hello,HotSwap.[version 1][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]
    Hello,HotSwap.[version 1][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]
    Hello,HotSwap.[version 1][ClassLoader-class sun.misc.Launcher$AppClassLoader]
    Hello,HotSwap.[version 2][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]
    Hello,HotSwap.[version 2][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]
    Hello,HotSwap.[version 1][ClassLoader-class sun.misc.Launcher$AppClassLoader]
    Hello,HotSwap.[version 2][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]
    Hello,HotSwap.[version 2][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]
    Hello,HotSwap.[version 1][ClassLoader-class sun.misc.Launcher$AppClassLoader]
    Hello,HotSwap.[version 2][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]
    Hello,HotSwap.[version 2][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]
    Hello,HotSwap.[version 1][ClassLoader-class sun.misc.Launcher$AppClassLoader]
    Hello,HotSwap.[version 3][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]
    Hello,HotSwap.[version 3][ClassLoader-class com.mavsplus.example.java.classloader.HotSwapClassLoader]


    6.總結(jié):
      本篇用一個例子講解了基礎的Java實現(xiàn)HotSwap的方式。后續(xù)會繼續(xù)深入講解.
    posted on 2015-07-03 17:35 landon 閱讀(5425) 評論(0)  編輯  收藏 所屬分類: JVMHotSwapClassLoader
    主站蜘蛛池模板: 亚洲情侣偷拍精品| 亚洲一级特黄特黄的大片 | 久久久无码精品亚洲日韩按摩| 亚洲高清免费视频| 国产精品亚洲综合一区| 黄色视频在线免费观看| 亚洲国产精品VA在线观看麻豆| 国产精品网站在线观看免费传媒| 亚洲国产精彩中文乱码AV| 亚洲国产精品免费视频| 亚洲激情视频网站| 最近2019中文字幕免费看最新 | 麻豆亚洲AV永久无码精品久久| 日本免费人成网ww555在线| 久久久久亚洲精品天堂久久久久久| aa级毛片毛片免费观看久| 久久久久亚洲AV无码专区首| 91香焦国产线观看看免费 | 一区二区免费国产在线观看| 久久亚洲国产精品五月天婷| 精品亚洲成A人无码成A在线观看 | 24小时日本电影免费看| 精品亚洲成A人无码成A在线观看 | 毛片免费全部播放一级| 苍井空亚洲精品AA片在线播放| 亚洲成AV人在线观看网址| 中文字幕看片在线a免费| 亚洲人成亚洲精品| 毛片免费全部免费观看| fc2成年免费共享视频18| 亚洲视频在线视频| 四虎影视永久免费视频观看| a在线观看免费视频| 亚洲AV成人噜噜无码网站| 亚洲精品视频免费观看| 成人免费大片免费观看网站| 成人福利在线观看免费视频| 91在线亚洲精品专区| 亚洲AⅤ永久无码精品AA| 91麻豆国产自产在线观看亚洲 | 三年片免费高清版 |