Java中的ClassLoader具體解說

java中的.java文件經過編譯今後,號碼大全就會生成類文件.class文件關鍵詞挖掘工具。class文件是以二進制字節碼寄存在硬盤中的。當咱們需求運用或加載Java文件到JVM中的時分,會從硬盤中讀取字節碼的class文件,然後經過類加載器將class文件加載到JVM中。也即是說,一切的Java文件都是經過類加載器加載到JVM中的。當然類加載器也是一個Java文件。那麼第一個類加載器又是怎麼加載到JVM中的呢?在發動JVM的時分,會調運一個本地辦法findBootStrapClass辦法加載最初始的那個ClassLoader,private native Class findBootstrapClass(String name),這個本地辦法運用C++編寫的。


1.體系已有3品種加載器

    1.1 BooststrapClassLoader(boot) 加載rt.jar下面的類(此加載器採用C++編寫,通常開發中是看不到的)  

    1.2 ExtClassLoader  加載ExtClassLoader下面的類(ext文件夾下面的jar)

    1.3 AppClassLoader  加載classpaht下面的類

    咱們寫的類簡直都是經過AppClassLoader這個加載器加載到JVM中的。


2.類加載器的加載機制(雙親託付機制)

    每一個類加載器都有一個對應的parentClassLoader。

    2.1    體系類加載器的父子關係

        自界說類加載器的爸爸是AppClassLoader

        AppClassLoader的爸爸是ExtClassLoader

        ExtClassLoader的爸爸是BooststrapClassLoader

Java代碼  保藏代碼

public class TestClassLoader {  

    public static void main(String[] args) {  

        // 當時目標的類加載器  

        ClassLoader loader = new TestClassLoader().getClass().getClassLoader();  

        // 從當時目標的類加載器想上找他的各個祖先  

        while (loader != null) {  

            System.out.println(loader.getClass().getName());  

            loader = loader.getParent();  

        }  

        // 曉得找到最終的祖先是null  

        System.out.println(loader);  

    }  

}  

輸出:  

sun.misc.Launcher$AppClassLoader  

sun.misc.Launcher$ExtClassLoader  

null  

 

    2.2    類加載器加載的次序

        類加載器是從根向下加載的

        也即是boot-》ExiClassLoader -》AppClassLoader

        當一個類需求被加載的時分,首要是由AppClassLoader加載器將其傳遞給其爸爸ExtClassLoader,然後ExtClassLoader再將其傳遞給他的爸爸boot。

        當boot發現他的加載規模內有對應的class,就加載到JVM中,否則交給兒子ExtClassLoader處置。

        ExtClassLoader再在他的加載規模類找有沒有對應的class,有就加載到JVM中,沒有就交給AppClassLoader處置。

        AppClassLoader再在classpath途徑下找對應的class,找到就加載,沒有就報反常。

        緣由:這樣能夠確保JVM中某一個className對應的是同一個class,由於都是從根向下加載的。

        避免重複加載,當爸爸現已加載了該類的時分,就沒有必要子ClassLoader再加載一次。 

        要是從下向上加載,能夠致使某一個className在JVM中對應好幾個class。能夠咱們會界說自個的類加載器和自個的加載規模。

        當自個界說的類加載在他們各自的規模都發現需求加載的類,那麼他們能夠都會加載,致使JVM中一個className對應好幾個不一樣的class

  

    2.3    比方咱們自個界說一個類加載器去加載java.lang.String這個類,那麼咱們是不能到達咱們目的的。

        由於加載機制是從上到下加載,當傳遞到上面的boot的時分,現已被加載到JVM中,輪不到咱們自界說的類加載器去加載它。

        但是,咱們肯定是能夠自個界說一個類加載器去加載咱們指定的類的。


3.怎麼自界說一個類加載

    首要,咱們需求承繼ClassLoadar

    然後,咱們不能破壞本來的類加載機制(雙親託付機制),所以咱們不能掩蓋loadClass辦法,咱們需求掩蓋findclass辦法。

    最終,在findClass辦法中寫入咱們的類加載器的代碼。

    查看源碼解說:

Java代碼  保藏代碼

protected Class loadClass(String name, boolean resolve)  

    throws ClassNotFoundException  

{  

    synchronized (getClassLoadingLock(name)) {  

        // 首要查看name對應的Class是不是現已被加載  

        Class c = findLoadedClass(name);  

        //假如沒有被加載  

        if (c == null) {  

            long t0 = System.nanoTime();  

            //測驗讓parentClassLoader去加載  

            try {  

                if (parent != null) {  

                    //當parent不爲null的時分,讓parent去loadClass  

                    c = parent.loadClass(name, false);  

                } else {  

                    //當parent爲null的時分,就調運本地辦法  

                    c = findBootstrapClassOrNull(name);  

                }  

            } catch (ClassNotFoundException e) {  

             

            }  

            //當parentClassLoader沒有加載的時分  

            if (c == null) {  

                long t1 = System.nanoTime();  

                //調運findClass辦法去加載  

                c = findClass(name);  

            }  

        }  

-        indexRead arguments from command-line "http://www.shoudashou.com"

-        indexRead arguments from command-line "http://www.4lunwen.cn"

-        indexRead arguments from command-line "http://www.zx1234.cn"

-        indexRead arguments from command-line "http://www.penbar.cn"

-        indexRead arguments from command-line "http://www.whathappy.cn"

-        indexRead arguments from command-line "http://www.lunjin.net"

-        indexRead arguments from command-line "http://www.ssstyle.cn"

-        indexRead arguments from command-line "http://www.91fish.cn"

-        indexRead arguments from command-line "http://www.fanselang.com"

        if (resolve) {  

            resolveClass(c);  

        }  

        return c;  

    }  

}  

 

4.舉例

    目標:自界說一個類加載器加咱們指定途徑下,經過我麼加密的class。

    進程:找到指定途徑下的class文件,解密,加載到JVM中。

    

    4.1先界說一個需求被加密編譯的類,一起運用它進行測驗

Java代碼  保藏代碼

public class MyClass extends Date {  

    @Override  

    public String toString() {  

        return "hello world";  

    }  

}  

     

    4.2加密本來的class文件

Java代碼  保藏代碼

public static void main(String[] args) throws Exception {  

    String inputSrc = args[0];//本來的class文件的途徑和文件名  

    String outputSrc = args[1];//加密今後寄存的文件途徑和文件名  

    FileInputStream fis = new FileInputStream(inputSrc);  

    FileOutputStream fos = new FileOutputStream(outputSrc);  

    //調用加密算法  

    cypher(fis, fos);  

    fis.close();  

    fos.close();  

}  

  

/** 

 * 加密解密函數辦法 

 * 

 * @param is 

 * @param os 

 * @throws IOException 

 */  

private static void cypher(InputStream is, OutputStream os) throws IOException {  

    int b = -1;  

    while ((b = is.read()) != -1) {  

        // 將0成爲1,將1成爲0  

        os.write(b ^ 0xff);  

    }  

}  

 

    4.3編寫咱們自個的類加載器

Java代碼  保藏代碼

public class MyClassLoader extends ClassLoader {  

    //要加載的類的途徑  

    private String clas***c;  

  

    public MyClassLoader() {  

  

    }  

  

    public MyClassLoader(String clas***c) {  

        this.clas***c = clas***c;  

    }  

  

    @Override  

    protected Class findClass(String name) throws ClassNotFoundException {  

        //先找到自個的加密的class文件的方位  

        String classFielName = clas***c + "\\" + name + ".class";  

        try {  

            FileInputStream fis = new FileInputStream(classFielName);  

            ByteArrayOutputStream baos = new ByteArrayOutputStream();  

            //調運解密算法  

            cypher(fis, baos);  

            fis.close();  

            byte[] bytes = baos.toByteArray();  

            //將讀出來的二進制轉換爲Class字節碼  

            return defineClass(null, bytes, 0, bytes.length);  

        } catch (Exception e) {  

            e.printStackTrace();  

        }  

  

        return super.findClass(name);  

    }  

}  

 

    4.4運用咱們自個的類加載加載咱們加密的類到JVM中

Java代碼  保藏代碼

//首要運用自個的類加載器去加載咱們加密的class文件  

//留意,這個當地的加載類的途徑下的class應該是咱們加密今後文件的方位  

Class clazz = new MyClassLoader("E:/AllWorkspace/workspace1/classLoaderTest/bin/com/gusi/test").loadClass("MyClass");  

//經過反射,測驗咱們的classLoader  

Date date = (Date) clazz.newInstance();  

System.out.println(date.toString());  



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章