<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

    引自-http://zeroturnaround.com/rebellabs/reloading-objects-classes-classloaders/
            -來(lái)自JRebel的研發(fā)公司zeroturnaround
    ________________________________________________________________
    Reloading Java Classes 101: Objects, Classes and ClassLoaders

    Welcome to Turnaround article series from ZeroTurnaround.

    In this article we will review how to reload a Java class using a dynamic classloader. To get there we’ll see how objects, classes and classloaders are tied to each other and the process required to make changes. We begin with a bird’s eye view of the problem, explains the reloading process, and then proceed to a specific example to illustrate typical problems and solutions. Other articles in the series include:

    A Bird’s Eye View

    The first thing to understand when talking about reloading Java code is the relation between classes and objects. All Java code is associated with methods contained in classes. Simplified, you can think of a class as a collection of methods, that receive “this” as the first argument. The class with all its methods is loaded into memory and receives a unique identity. In the Java API this identity is represented by an instance of java.lang.Class that you can access using theMyObject.class expression.

    Every object created gets a reference to this identity accessible through the Object.getClass()method. When a method is called on an object, the JVM consults the class reference and calls the method of that particular class. That is, when you call mo.method() (where mo is an instance of MyObject), then the JVM will call mo.getClass().getDeclaredMethod("method").invoke(mo) (this is not what the JVM actually does, but the result is the same).


    Every Class object is in turn associated with its classloader (MyObject.class.getClassLoader()). The main role of the class loader is to define a class scope — where the class is visible and where it isn’t. This scoping allows for classes with the same name to exist as long as they are loaded in different classloaders. It also allows loading a newer version of the class in a different classloader.


    The main problem with code reloading in Java is that although you can load a new version of a class, it will get a completely different identity and the existing objects will keep referring the previous version of the class. So when a method is called on those objects it will execute the old version of the method.

    Let’s assume that we load a new version of the MyObject class. Let’s refer to the old version asMyObject_1 and to the new one as MyObject_2. Let’s also assume that MyObject.method() returns “1” in MyObject_1 and “2” in MyObject_2. Now if mo2 is an instance of MyObject_2:

    • mo.getClass() != mo2.getClass()
    • mo.getClass().getDeclaredMethod("method").invoke(mo)
      != mo2.getClass().getDeclaredMethod("method").invoke(mo2)
    • mo.getClass().getDeclaredMethod("method").invoke(mo2) throws a ClassCastException, because the Class identities of mo and mo2 do no match.

    This means that any useful solution must create a new instance of mo2 that is an exact copy ofmo and replace all references to mo with it. To understand how hard it is, remember the last time you had to change your phone number. It’s easy enough to change the number itself, but then you have to make sure that everyone you know will use the new number, which is quite a hassle. It’s just as difficult with objects (in fact, it’s actually impossible, unless you control the object creation yourself), and we’re talking about many objects that you must update at the same time.

    Down and Dirty

    Let’s see how this would look in code. Remember, what we’re trying to do here is load a newer version of a class, in a different classloader. We’ll use an Example class that looks like this:

    public class Example implements IExample {
      
    private int counter;
      
    public String message() {
        
    return "Version 1";
      }
      
    public int plusPlus() {
        
    return counter++;
      }
      
    public int counter() {
        
    return counter;
      }
    }

    We’ll use a main() method that will loop infinitely and print out the information from the Exampleclass. We’ll also need two instances of the Example class: example1 that is created once in the beginning and example2 that is recreated on every roll of the loop:

    public class Main {
      
    private static IExample example1;
      
    private static IExample example2;

      
    public static void main(String[] args)  {
        example1 = ExampleFactory.newInstance();

        
    while (true) {
          example2 = ExampleFactory.newInstance();

          System.out.println("1) " +
            example1.message() + " = " + example1.plusPlus());
          System.out.println("2) " +
            example2.message() + " = " + example2.plusPlus());
          System.out.println();

          Thread.currentThread().sleep(3000);
        }
      }
    }

    IExample is an interface with all the methods from Example. This is necessary because we’ll be loading Example in an isolated classloader, so Main cannot use it directly (otherwise we’d get aClassCastException).

    public interface IExample {
      String message();
      
    int plusPlus();
    }

    From this example, you might be surprised to see how easy it is to create a dynamic class loader. If we remove the exception handling it boils down to this:

    public class ExampleFactory {
      
    public static IExample newInstance() {
        URLClassLoader tmp =
          
    new URLClassLoader(new URL[] {getClassPath()}) {
            
    public Class loadClass(String name) {
              
    if ("example.Example".equals(name))
                
    return findClass(name);
              
    return super.loadClass(name);
            }
          };

        
    return (IExample)
          tmp.loadClass("example.Example").newInstance();
      }
    }

    The method getClassPath() for the purposes of this example could return the hardcoded classpath. However, in the full source code (available in the Resources section below) you can see how we can use the ClassLoader.getResource() API to automate that.

    Now let’s run Main.main and see the output after waiting for a few loop rolls:

    1) Version 1 = 3 2) Version 1 = 0

    As expected, while the counter in the first instance is updated, the second stays at “0”. If we change the Example.message() method to return “Version 2”. The output will change as follows:

    1) Version 1 = 4 2) Version 2 = 0

    As we can see, the first instance continues incrementing the counter, but uses the old version of the class to print out the version. The second instance class was updated, however all of the state is lost.

    To remedy this, let’s try to reconstruct the state for the second instance. To do that we can just copy it from the previous iteration.

    First we add a new copy() method to Example class (and corresponding interface method):

    public IExample copy(IExample example) {
      
    if (example != null)
        counter = example.counter();
      
    return this;
    }

    Next we update the line in the Main.main() method that creates the second instance:

    example2 = ExampleFactory.newInstance().copy(example2);

    Now waiting for a few iterations yields:

    1) Version 1 = 3 2) Version 1 = 3

    And changing Example.message() method to return “Version 2” yields:

    1) Version 1 = 4 2) Version 2 = 4

    As you can see even though it’s possible for the end user to see that the second instance is updated and all its state is preserved, it involves managing that state by hand. Unfortunately, there is no way in the Java API to just update the class of an existing object or even reliably copy its state, so we will always have to resort to complicated workarounds.

    In subsequent articles we’ll review how web containers, OSGi, Tapestry 5, Grails and others confront the problem of managing state when reloading classes, then we’ll dig into how HotSwap, Dynamic Languages, and the Instrumentation API work, and go behind the scenes with JRebel as well.     
    ___________________________________________________________________________________________
      筆記記錄:
            1.可以存在兩個(gè)相同名字的class,只要他們是由不同的classloader加載即可,即允許用一個(gè)不同的classloader加載一個(gè)新的class
            2.如果想達(dá)到hotswap的目的,新加載的class實(shí)例必須要復(fù)制舊的實(shí)例的狀態(tài)并且替換所有舊的實(shí)例的引用。非常麻煩,就好比你自己更換電話號(hào)碼很簡(jiǎn)單,但是要確保所有人都知道你換了新號(hào)碼一樣.
            3.如同上述的例子一樣,必須手動(dòng)的管理新舊實(shí)例的狀態(tài)保存。沒有任何的Java API幫助我們替換一個(gè)舊的實(shí)例的class或者可靠的復(fù)制它的狀態(tài),所以我們有時(shí)不得不求助于更加復(fù)雜的解決方案.
            4.可參考web containers,OSGI,Tapestry 5,Grails等是如何面對(duì)上述reloading class遇到的問(wèn)題.
             5.注意接口IExample很重要.-->
                    1.IExample為公有接口,由系統(tǒng)classloader加載.
                    2.如何沒有接口,則無(wú)法實(shí)現(xiàn)hotswap,因?yàn)椴煌腸lassloader加載的hotswap class均不同,會(huì)報(bào)ClassCastException.但如果面向接口編程,則沒有問(wèn)題,雖然是不同的classloader加載的,但是都是公用接口.
                    3.如果沒有接口,也可以考慮將Example作為公有接口類,但是后續(xù)hotswap的class必須繼承該Example才可以滿足需求._不過(guò)不建議使用繼承,比較麻煩.
    _______________________________________________________________________________
    附本人測(cè)試代碼:

    package com.mavsplus.example.java.rjc;

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

        public String message();

        public int plusPlus();

        public int counter();
        
        public IExample copy(IExample example);
    }


    package com.mavsplus.example.java.rjc;

    import java.net.URL;
    import java.net.URLClassLoader;

    /**
     * 每次都new一個(gè)classloader來(lái)加載class并且實(shí)例化
     * 
     * @author landon
     * @since 1.8.0_25
     
    */
    public class ExampleFactory {

        public static IExample newInstance() throws Exception {
            // 必須用url形式,本地file需要:file:形式-如"file:\\E:\\github\\mavsplus-all\\mavsplus-examples\\src\\main\\resources\\rjc\\"
            
    // 這里硬編碼-該目錄下只有編譯后的Example.class_即只有Example.class用urlClassloader加載,其他均默認(rèn)加載
            
    // 重點(diǎn):一個(gè)比較容易犯錯(cuò)的地方:是最后的路徑少了"\",否則會(huì)報(bào)ClassNotFoundException
            URLClassLoader newClazzLoader = new URLClassLoader(new URL[] { new URL(
                    "file:\\E:\\github\\mavsplus-all\\mavsplus-examples\\src\\main\\resources\\rjc\\") }) {

                // 注意:必須覆寫loadClass方法-否則還會(huì)默認(rèn)加載默認(rèn)classpath下的Example(因?yàn)橛肊clipse編寫的Example類,默認(rèn)targe目錄也是在classpath下)
                @Override
                public Class<?> loadClass(String name) throws ClassNotFoundException {
                    if (name.equals("com.mavsplus.example.java.rjc.Example")) {
                        return findClass(name);
                    }

                    return super.loadClass(name);
                }
            };

            return (IExample) newClazzLoader.loadClass("com.mavsplus.example.java.rjc.Example").newInstance();
        }
    }


    package com.mavsplus.example.java.rjc;

    /**
     * 用來(lái)reload的class
     * 
     * @author landon
     * @since 1.8.0_25
     
    */
    public class Example implements IExample {

        private int counter;

        @Override
        public String message() {
            // return "Version 1";
            return "Version 2";
        }

        @Override
        public int plusPlus() {
            return counter++;
        }

        @Override
        public int counter() {
            return counter;
        }

        @Override
        public IExample copy(IExample example) {
            if (example != null) {
                counter = example.counter();
            }

            return this;
        }
    }


    package com.mavsplus.example.java.rjc;

    import java.util.concurrent.TimeUnit;

    /**
     * <a href=
     * "http://zeroturnaround.com/rebellabs/reloading-objects-classes-classloaders/"
     * >rjc series 1</>
     * 
     * 測(cè)試reloading java class
     * 
     * <pre>
     *   ___________________________________________
     *       1.默認(rèn)輸出
     *           1)Version 1 = 0
     *              2)Version 1 = 0
     *              
     *              1)Version 1 = 1
     *              2)Version 1 = 0
     *              
     *              1)Version 1 = 2
     *              2)Version 1 = 0
     *              
     *              1)Version 1 = 3
     *              2)Version 1 = 0
     *              
     *              1)Version 1 = 4
     *              2)Version 1 = 0
     *      --->即每次reload時(shí),example2的狀態(tài)一直是0.
     *  _____________________________________________
     *          2.修改Example.java,將message方法修改,返回Version 2,保存(可以直接在程序運(yùn)行的時(shí)候進(jìn)行修改,因?yàn)檫\(yùn)行的程序是用的是另一個(gè)地方的Example的clazz)
     *                  ->將Example.class放到指定的加載位置
     *              1)Version 1 = 5
     *              2)Version 2 = 0
     *              
     *              1)Version 1 = 6
     *              2)Version 2 = 0
     *              
     *              1)Version 1 = 7
     *              2)Version 2 = 0
     *              
     *              1)Version 1 = 8
     *              2)Version 2 = 0
     *              
     *              1)Version 1 = 9
     *              2)Version 2 = 0
     *         從輸出看_確實(shí)新加載了修改過(guò)的Example.class_因?yàn)閙essage返回值變化了--->但是因?yàn)槊看味际切录虞d并且實(shí)例化的,所以實(shí)例的狀態(tài)還是0
     * ________________________________________________________________________________________
     *         3.繼續(xù)修改Example.java,增加一個(gè)copy方法,用來(lái)復(fù)制舊的狀態(tài)數(shù)據(jù)并且修改main(exampel2的賦值)_
     *         1)Version 1 = 7
     *         2)Version 1 = 7
     * 
     *         1)Version 1 = 8
     *         2)Version 1 = 8
     * 
     *         1)Version 1 = 9
     *         2)Version 2 = 9
     * 
     *         1)Version 1 = 10
     *         2)Version 2 = 10
     *         從輸出看,每次reload example2后,舊的實(shí)例狀態(tài)也保存了_純手動(dòng)維護(hù)這個(gè)實(shí)例狀態(tài)
     * </pre>
     * 
     * @author landon
     * @since 1.8.0_25
     
    */
    public class RJCTest {

        private static IExample example1;
        private static IExample example2;

        public static void main(String[] args) throws Exception {
            example1 = ExampleFactory.newInstance();

            while (true) {
                // 第一次運(yùn)行的時(shí)候,example2為null->第二次后則會(huì)克隆舊的對(duì)象狀態(tài)
                example2 = ExampleFactory.newInstance().copy(example2);

                System.out.println("1)" + example1.message() + " = " + example1.plusPlus());
                System.out.println("2)" + example2.message() + " = " + example2.plusPlus());

                System.out.println();

                TimeUnit.SECONDS.sleep(5);
            }
        }
    }
    posted on 2015-12-11 16:08 landon 閱讀(2977) 評(píng)論(1)  編輯  收藏 所屬分類: HotSwapClassLoader

    FeedBack:
    # re: Java HotSwap Ⅳ-Reloading Java Classes Series 1-Objects, Classes and ClassLoaders
    2015-12-12 13:20 | java論壇
    難得的一篇java重載的好文章,要是中文的就好了,能翻譯成中文嗎  回復(fù)  更多評(píng)論
      
    主站蜘蛛池模板: 亚洲午夜久久久久久噜噜噜| 无遮挡a级毛片免费看| 亚洲精品无码久久久久AV麻豆| 97视频免费在线| 免费观看久久精彩视频| 99热这里只有精品6免费| 欧美色欧美亚洲另类二区| 亚洲一区无码中文字幕乱码| 亚洲熟妇av一区二区三区漫画| 高清国语自产拍免费视频国产| 久久久久久曰本AV免费免费| 野花香在线视频免费观看大全| 国产综合成人亚洲区| 久久亚洲国产最新网站| 亚洲第一页在线播放| 亚洲综合国产精品| 亚洲狠狠婷婷综合久久久久 | 国产AV无码专区亚洲AV漫画 | 亚洲人成电影网站| 中文字幕亚洲色图| 亚洲乱码国产乱码精品精| 亚洲日韩精品无码专区网站| 免费A级毛片在线播放不收费| 拨牐拨牐x8免费| 99爱在线精品免费观看| 久久国产色AV免费观看| 88av免费观看| 在线a免费观看最新网站| 中文字幕免费视频| 免费人成在线观看69式小视频| 99久久人妻精品免费二区| 日本一卡精品视频免费| 99视频在线免费看| 足恋玩丝袜脚视频免费网站| 99久久国产免费-99久久国产免费| 少妇人妻偷人精品免费视频| 日本最新免费网站| 免费人成在线视频| 韩国二级毛片免费播放| 国产jizzjizz视频全部免费| www.亚洲精品|