類(lèi)加載機(jī)制(2)類(lèi)加載器源碼解析

目錄

<span id="jump1">一、什么是類(lèi)的加載(類(lèi)初始化)</span>

類(lèi)加載器的任務(wù)是,根據(jù)類(lèi)的全限定名來(lái)讀取類(lèi)的二進(jìn)制字節(jié)流,加載進(jìn)JVM。并轉(zhuǎn)換成對(duì)應(yīng)的java.lang.Class對(duì)象實(shí)例。

image.png

<span id="jump1_1">1、BootstrapClassLoader</span>

啟動(dòng)類(lèi)加載器主要加載的是JVM自身需要的類(lèi),這個(gè)類(lèi)加載使用C++語(yǔ)言實(shí)現(xiàn)的,是虛擬機(jī)自身的一部分,負(fù)責(zé)加載存放在 JDK\jre\lib(JDK代表JDK的安裝目錄,下同)下,或被 -Xbootclasspath參數(shù)指定的路徑中的,并且能被虛擬機(jī)識(shí)別的類(lèi)庫(kù)(如rt.jar,所有的java.開(kāi)頭的類(lèi)均被 BootstrapClassLoader加載)。啟動(dòng)類(lèi)加載器是無(wú)法被Java程序直接引用的??偨Y(jié)一句話:<mark>啟動(dòng)類(lèi)加載器加載java運(yùn)行過(guò)程中的核心類(lèi)庫(kù)JRE\lib\rt.jar, sunrsasign.jar, charsets.jar, jce.jar, jsse.jar, plugin.jar 以及存放在JRE\classes里的類(lèi),也就是JDK提供的類(lèi)等常見(jiàn)的比如:Object、Stirng、List…</mark>

<span id="jump1_2">2、ExtensionClassLoader</span>

該加載器由 sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn),它負(fù)責(zé)加載 JDK\jre\lib\ext目錄中,或者由 java.ext.dirs系統(tǒng)變量指定的路徑中的所有類(lèi)庫(kù)(如javax.開(kāi)頭的類(lèi)),開(kāi)發(fā)者可以直接使用擴(kuò)展類(lèi)加載器

<span id="jump1_3">3、ApplicationClassLoader</span>

ApplicationClassLoader,該類(lèi)加載器由 sun.misc.Launcher$AppClassLoader來(lái)實(shí)現(xiàn),它負(fù)責(zé)加載用戶(hù)類(lèi)路徑(ClassPath)所指定的類(lèi),開(kāi)發(fā)者可以直接使用該類(lèi)加載器,如果應(yīng)用程序中沒(méi)有自定義過(guò)自己的類(lèi)加載器,一般情況下這個(gè)就是程序中默認(rèn)的類(lèi)加載器??偨Y(jié)一句話:<mark>應(yīng)用程序類(lèi)加載器加載CLASSPATH變量指定路徑下的類(lèi) 即指你自已在項(xiàng)目工程中編寫(xiě)的類(lèi)</mark>

<span id="jump1_4">4、CustomClassLoader</span>

<span id="jump1_5">5、線程上下文類(lèi)加載器</span>

<font color=red>Thread.currentThread().getContextClassLoader()</font>獲取線程上下文類(lèi)加載器,線程上下文加載器其實(shí)很重要,它違背(破壞)雙親委派模型,很好地打破了雙親委派模型的局限性,盡管我們?cè)陂_(kāi)發(fā)中很少用到,但是框架組件開(kāi)發(fā)絕對(duì)要頻繁使用到線程上下文類(lèi)加載器,如Tomcat等等…

<span id="jump2">二、java虛擬機(jī)入口應(yīng)用:sun.misc.Launcher</span>

<span id="jump2_1">

1、 Launcher類(lèi)就是類(lèi)加載器的入口工具類(lèi),其中維護(hù)了四種加載器。

1、bootClassPath ,這個(gè)是c實(shí)現(xiàn)的,我們?cè)趈ava層面看不到
2、extcl = ExtClassLoader 擴(kuò)展類(lèi)加載器,用于加載java.ext.dirs目錄下的類(lèi)。
3、loader = AppClassLoader(dirs,extcl) 應(yīng)用類(lèi)加載器,將擴(kuò)展類(lèi)加載設(shè)置給AppClassLoader,當(dāng)做<font color=red>邏輯父類(lèi)parent</font>,加載用戶(hù)java.class.path下的類(lèi)。
4、Thread.currentThread().setContextClassLoader(loader); 將當(dāng)前類(lèi)加載器設(shè)置給線程上下文類(lèi)加載器

<font color=red>邏輯父類(lèi)</font>是我的個(gè)人理解:

<mark>1、因?yàn)楸旧鞥xtClassLoader、AppClassLoader都是集成的URLClassLoader。并不沒(méi)有真正意義上的繼承關(guān)系。
2、這里設(shè)置parent,只是在URLClassLoader里存儲(chǔ)了一個(gè)parent的對(duì)象。然后開(kāi)放了一個(gè)getParent()的方法。實(shí)現(xiàn)了形式上的父類(lèi)關(guān)系。
3、為什么要這樣做呢?主要是為了實(shí)現(xiàn)雙親委派模型,讓他們有了邏輯上的“父子”關(guān)系。

import sun.misc.SharedSecrets;
import sun.misc.URLClassPath;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.util.StringTokenizer;
import java.util.Vector;

public class Launcher {
   private static Launcher launcher      = new Launcher();
   //這個(gè)就是BootstrapClassLoader的位置
   private static String   bootClassPath =
           System.getProperty("sun.boot.class.path");

   public static Launcher getLauncher() {
       return launcher;
   }

   //應(yīng)用層類(lèi)加載器
   private ClassLoader loader;

   public Launcher() {
       // Create the extension class loader
       //擴(kuò)展類(lèi)加載器
       ClassLoader extcl;
       try {
           //初始化內(nèi)部類(lèi)擴(kuò)展類(lèi)加載器對(duì)象。
           extcl = ExtClassLoader.getExtClassLoader();
       } catch (IOException e) {
           throw new InternalError(
                   "Could not create extension class loader", e);
       }

       // Now create the class loader to use to launch the application
       //通過(guò)內(nèi)部類(lèi),獲取應(yīng)用層類(lèi)加載器對(duì)象
       try {
           loader = AppClassLoader.getAppClassLoader(extcl);
       } catch (IOException e) {
           throw new InternalError(
                   "Could not create application class loader", e);
       }

       //設(shè)置AppClassLoader為線程上下文類(lèi)加載器,這里注意設(shè)置給線程的是應(yīng)用層加載器loader。
       //等Thread.currentThread().getContextClassLoader()時(shí)獲取的就是應(yīng)用層加載器對(duì)象
       Thread.currentThread().setContextClassLoader(loader);
   }

   /*
    * Returns the class loader used to launch the main application.
    */
   public ClassLoader getClassLoader() {
       return loader;
   }

   /*
    * The class loader used for loading installed extensions.
    * 擴(kuò)展類(lèi)加載器:
    * 負(fù)責(zé)加載存放在JRE的lib/ext/目錄下的jar包中的Class。
    *
    * 當(dāng)然我們可以指定-D java.ext.dirs參數(shù)來(lái)添加和改變ExtClassLoader的加載路徑
    */
   static class ExtClassLoader extends URLClassLoader {
       static {
           ClassLoader.registerAsParallelCapable();
       }

       /**
        * create an ExtClassLoader. The ExtClassLoader is created
        * within a context that limits which files it can read
        */
       public static ExtClassLoader getExtClassLoader() throws IOException {
           //獲取java.ext.dirs路徑下所有文件
           final File[] dirs = getExtDirs();

           try {
               // Prior implementations of this doPrivileged() block supplied
               // aa synthesized ACC via a call to the private method
               // ExtClassLoader.getContext().

               //這里具體ACC(AccessController)的原理不再細(xì)看了,只看結(jié)果就好。
               //1、這里的意思就是run方法里或返回一個(gè) ExtClassLoader對(duì)象,但是默認(rèn)走父類(lèi)URLClassLoader的構(gòu)造方法。
               //  將dirs目錄設(shè)置給URLClassLoader()。
               //2、這個(gè)意思其實(shí)就是相當(dāng)于ExtClassLoader沒(méi)有做特殊的處理,僅僅就是用父類(lèi)的URLClassLoader得方法。
               //  而ExtClassLoader只是初始化了一下東西。和AppClassLoader很相似
               return AccessController.doPrivileged(
                       new PrivilegedExceptionAction<ExtClassLoader>() {
                           public ExtClassLoader run() throws IOException {
                               int len = dirs.length;
                               for (int i = 0; i < len; i++) {
                                   MetaIndex.registerDirectory(dirs[I]);
                               }
                               return new ExtClassLoader(dirs);
                           }
                       });
           } catch (java.security.PrivilegedActionException e) {
               throw (IOException) e.getException();
           }
       }

       public ExtClassLoader(File[] dirs) throws IOException {
           super(getExtURLs(dirs), (ClassLoader)null, sun.misc.Launcher.factory);
           SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
       }

       private static File[] getExtDirs() {
           String s = System.getProperty("java.ext.dirs");
           File[] dirs;
           if (s != null) {
               StringTokenizer st =
                       new StringTokenizer(s, File.pathSeparator);
               int count = st.countTokens();
               dirs = new File[count];
               for (int i = 0; i < count; i++) {
                   dirs[i] = new File(st.nextToken());
               }
           } else {
               dirs = new File[0];
           }
           return dirs;
       }


       private static URL[] getExtURLs(File[] var0) throws IOException {
           Vector var1 = new Vector();

           for(int var2 = 0; var2 < var0.length; ++var2) {
               String[] var3 = var0[var2].list();
               if (var3 != null) {
                   for(int var4 = 0; var4 < var3.length; ++var4) {
                       if (!var3[var4].equals("meta-index")) {
                           File var5 = new File(var0[var2], var3[var4]);
                           var1.add(sun.misc.Launcher.getFileURL(var5));
                       }
                   }
               }
           }

           URL[] var6 = new URL[var1.size()];
           var1.copyInto(var6);
           return var6;
       }
   }

   /**
    * The class loader used for loading from java.class.path.
    * runs in a restricted security context.
    *
    * 應(yīng)用類(lèi)加載器:
    * 和ExtClassloader一樣,只是將類(lèi)的目錄設(shè)置給URLClassLoader了。
    *
    * 同時(shí)需要說(shuō)明的是,
    *     參數(shù)1:類(lèi)的加載目錄,java.class.path。就是我們的項(xiàng)目目錄
    *     參數(shù)2:設(shè)置父類(lèi)ExtClassLoader,實(shí)際上并不是真正的繼承關(guān)系,
    *          因?yàn)锳ppClassLoader和ExtClassLoader都extends URLClassLoader。
    *          只是邏輯意義上的parent,目的是實(shí)現(xiàn)雙親委派模型。
    */
   static class AppClassLoader extends URLClassLoader {
       final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);

       public static ClassLoader getAppClassLoader(final ClassLoader extcl)
               throws IOException {
           final String s    = System.getProperty("java.class.path");
           final File[] path = (s == null) ? new File[0] : getClassPath(s);


           //將java.class.path的項(xiàng)目目錄的類(lèi),同時(shí)設(shè)置邏輯意義的parent extcl,就是上邊初始化的ExtClassLoader對(duì)象
           return AccessController.doPrivileged(
                   new PrivilegedAction<AppClassLoader>() {
                       public AppClassLoader run() {
                           URL[] urls =
                                   (s == null) ? new URL[0] : pathToURLs(path);
                           return new AppClassLoader(urls, extcl);
                       }
                   });
       }


       AppClassLoader(URL[] urls, ClassLoader parent) {
           //第二個(gè)參數(shù)是parent,也就是實(shí)現(xiàn)邏輯父子關(guān)系的方式。將parent存儲(chǔ)下來(lái),然后暴露getParent()返回parent
           super(urls, parent, sun.misc.Launcher.factory);
           this.ucp.initLookupCache(this);
       }
   }
}

<span id="jump2_2">2、 ExtClassLoader 源碼

源碼已經(jīng)在Launcher里粘過(guò)了,這里總結(jié)一下。

1、ExtClassLoader 是Launcher的內(nèi)部類(lèi)
2、extends URLClassLoader的子類(lèi)
3、負(fù)責(zé)加載java.ext.dirs目錄下的類(lèi)
4、可以指定-D java.ext.dirs參數(shù)來(lái)添加和改變ExtClassLoader的加載路徑

<span id="jump2_3">3、AppClassLoader</span>

1、AppClassLoader 是Launcher的內(nèi)部類(lèi)
2、extends URLClassLoader的子類(lèi)。
3、負(fù)責(zé)加載java.class.path目錄下的類(lèi),即用戶(hù)代碼里的類(lèi)
4、和ExtClassLoader是<font color=red>邏輯父子關(guān)系</font>,

4.1因?yàn)樗麄兌际侵苯蛹蒛RLClassLoader,并沒(méi)有真實(shí)的繼承關(guān)系。
4.2但是初始化AppClassLoader時(shí),實(shí)際使用的是URLClassLoader(dirs,parent)構(gòu)造函數(shù),第二個(gè)參數(shù)是存儲(chǔ)一個(gè)parent對(duì)象。然后暴露一個(gè)getParent()方法實(shí)現(xiàn)邏輯意義上的父子關(guān)系

<span id="jump2_4">4、URLClassLoader</span>

正如上邊所說(shuō)的,

  • 1、AppClassLoader、ExtClassLoader實(shí)際上并沒(méi)有繼承關(guān)系,只是在AppClassLoader里存了一個(gè)叫做parent的ExtClassLoader對(duì)象而已,也就是邏輯父類(lèi)。
  • 2、那么按這樣理解,是不是ExtClassLoader會(huì)存一個(gè)BootStrapClassLoader的對(duì)象呢?其實(shí)并沒(méi)有,而是在ClassLoader中單獨(dú)有一個(gè)native的c方法<font color=red> findBootstrapClass()</font> ,底層是c實(shí)現(xiàn)的。作用就是通過(guò)BootStrapClassLoader實(shí)現(xiàn)對(duì)類(lèi)的加載。也就是說(shuō)BootStrapClassLoader是底層c寫(xiě)的,并沒(méi)有一個(gè)實(shí)際的java類(lèi)。

簡(jiǎn)單看一下繼承關(guān)系:

image.png

看一下關(guān)鍵代碼:

import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;

import java.io.Closeable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.SecureClassLoader;


public class URLClassLoader extends SecureClassLoader implements Closeable {
    ...
}

public class SecureClassLoader extends ClassLoader {
    ...
}

public abstract class ClassLoader {

    ...

    //這個(gè)存儲(chǔ)的就是邏輯父類(lèi)的對(duì)象。
    //1、AppClassLoader存儲(chǔ)的是ExtClassLoader的對(duì)象。
    //2、ExtClassLoader存儲(chǔ)的是null。
    //
    // The parent class loader for delegation
    // Note: VM hardcoded the offset of this field, thus all new fields
    // must be added *after* it.
    private final java.lang.ClassLoader parent;

    @CallerSensitive
    public final java.lang.ClassLoader getParent() {
        if (parent == null)
            return null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // Check access to the parent class loader
            // If the caller's class loader is same as this class loader,
            // permission check is performed.
            checkClassLoaderPermission(parent, Reflection.getCallerClass());
        }
        return parent;
    }


    /**
     * Returns a class loaded by the bootstrap class loader;
     * or return null if not found.
     
     返回一個(gè)通過(guò)bootstrapClassLoader加載的class對(duì)象。如果沒(méi)找到就返回null
     */
    private Class<?> findBootstrapClassOrNull(String name)
    {
        if (!checkName(name)) return null;

        return findBootstrapClass(name);
    }

    // return null if not found
    private native Class<?> findBootstrapClass(String name);

}

<span id="jump4">三、雙親委派模型</span>

<font id="jump4_1">(1)工作流程:

  • 1、類(lèi)加載器收到加載請(qǐng)求,不會(huì)直接加載,而是直接拋給父加載器,類(lèi)似<font color=red>冒泡式委托</font>。CustomerClassLoader -> AppClassLoader -> ExtClassLoader -> BootStrapClassLoader。
  • 2、當(dāng)加載時(shí),父加載器加載類(lèi)失敗再依次調(diào)用子類(lèi)加載器,進(jìn)行加載。如果最終customerClassLoader也加載不到,就拋異常。類(lèi)似<font color=red>遞進(jìn)式加載</font>


    image.png

圖來(lái)自"宜春"大神的:【JVM篇二】

<font id="jump4_2">(2)設(shè)計(jì)意義

  • 保證jdk系統(tǒng)類(lèi)的唯一性,安全性。
雙親委派模式的存在可以保證,java.lang.String等jdk類(lèi),不被自定義的類(lèi)加載器隨意替換,防止篡改。

<span id="jump5">四、ClassLoader源碼分析

<span id="jump5_1">1、ClassLoader#loadClass()源碼分析


public abstract class ClassLoader {

    protected Class<?> loadClass(String name, boolean resolve)
            throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // 1、判斷是否加載過(guò)該類(lèi)
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    //2、如果有父加載器,則調(diào)用邏輯父加載器。加載成功就會(huì)存起來(lái)。在findClass里查找
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        //3、使用BootstrapClassLoader加載器加載類(lèi)
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    //4、通過(guò)子類(lèi)自定義的方法加載類(lèi)。
                    //這個(gè)方法是我們自定義類(lèi)加載器的主要重寫(xiě)方法。默認(rèn)是拋出異常。
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

}

<span id="jump5_2">2、<font color=red>URLClassLoader</font>#findClass()源碼分析

<mark>這個(gè)方法的作用就是

<mark>1、通過(guò)path路徑找到.class文件,
2、將.class加載進(jìn)內(nèi)存byte[]二進(jìn)制數(shù)組,調(diào)用defineClass方法將byte[]轉(zhuǎn)換成java可以使用的Class<?>字節(jié)碼對(duì)象

默認(rèn)ClassLoader的findClass方法是空方法。

protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

我們這里以URLClassLoader的findClass為例看源碼:


/**
 * Finds and loads the class with the specified name from the URL search
 * path. Any URLs referring to JAR files are loaded and opened as needed until the class is found.
 *
 * 從URL路徑中查找并加載具有特定名稱(chēng)的類(lèi)。
 * 所有JAR包下的class使用之前都需要先加載進(jìn)內(nèi)存。
 * 
 * @param name the name of the class
 * @return the resulting class
 * @exception ClassNotFoundException if the class could not be found,
 *            or if the loader is closed.
 * @exception NullPointerException if {@code name} is {@code null}.
 */
protected Class<?> findClass(final String name)
        throws ClassNotFoundException
{
    final Class<?> result;
    try {
        result = AccessController.doPrivileged(
                new PrivilegedExceptionAction<Class<?>>() {
                    public Class<?> run() throws ClassNotFoundException {
                        //name就是定義的類(lèi)路徑。
                        // 例如:AppClassLoader默認(rèn)設(shè)置的是java.class.path.上邊Launcher源碼里可以看到
                        String path = name.replace('.', '/').concat(".class");
                        Resource res = ucp.getResource(path, false);
                        if (res != null) {
                            try {
                                //通過(guò)路徑來(lái)找到對(duì)應(yīng)的.class文件,并將二進(jìn)制數(shù)據(jù)轉(zhuǎn)換生成Class<?>對(duì)象
                                return defineClass(name, res);
                            } catch (IOException e) {
                                throw new ClassNotFoundException(name, e);
                            }
                        } else {
                            return null;
                        }
                    }
                }, acc);
    } catch (java.security.PrivilegedActionException pae) {
        throw (ClassNotFoundException) pae.getException();
    }
    if (result == null) {
        throw new ClassNotFoundException(name);
    }
    return result;
}

<span id="jump5_3">3、ClassLoader#defineClass(byte[] b, int off, int len)源碼分析

我們這里只看最內(nèi)層重載函數(shù)。

這個(gè)方法主要作用就是通過(guò)native的c語(yǔ)言的方法,<mark>將二進(jìn)制數(shù)組轉(zhuǎn)成java 可以使用的Class<?>字節(jié)碼對(duì)象。

protected final Class<?> defineClass(String name, byte[] b, int off, int len,
                                         ProtectionDomain protectionDomain)
        throws ClassFormatError
    {
        protectionDomain = preDefineClass(name, protectionDomain);
        String source = defineClassSourceLocation(protectionDomain);
        //調(diào)用native方法,將二進(jìn)制數(shù)組轉(zhuǎn)成class對(duì)象
        Class<?> c = defineClass1(name, b, off, len, protectionDomain, source);
        postDefineClass(c, protectionDomain);
        return c;
    }

<span id="jump5_4">4、小結(jié)

  • <mark>loadClass() 作用就是實(shí)現(xiàn)雙親委派機(jī)制的邏輯
  • <mark>findClass() 作用就是通過(guò)path,找到.class文件,并加載進(jìn)內(nèi)存(放到byte[]二進(jìn)制數(shù)組中)。
  • <mark>defineClass() 作用就是將二進(jìn)制數(shù)組byte[]轉(zhuǎn)成java能使用的Class<?>字節(jié)碼對(duì)象

<span id="jump6">五、自定義類(lèi)加載器</span>

<font id="jump6_1">1、作用

<font color=red>核心是擴(kuò)展java虛擬機(jī)的動(dòng)態(tài)加載類(lèi)的機(jī)制</font>,用戶(hù)可以定制類(lèi)的加載邏輯,比如:熱更新。就是通過(guò)自定類(lèi)加載器,來(lái)加載網(wǎng)絡(luò)請(qǐng)求的類(lèi)。將.class文件通過(guò)網(wǎng)絡(luò)下載到本地,然后通過(guò)自定義的類(lèi)加載器加載。

<font id="jump6_2">2、實(shí)現(xiàn)邏輯

通過(guò)上邊對(duì)ClassLoader的源碼分析。已經(jīng)很清楚了,我們可以直接繼承ClassLoader

  • 1、<font color=red>重寫(xiě)loadClass()</font>方法。但是jdk1.2之后官方已經(jīng)<font color=red>不建議</font>直接重寫(xiě)loadClass()了,因?yàn)槟怯锌赡懿环想p親委派機(jī)制了,畢竟是在這個(gè)方法實(shí)現(xiàn)的雙親委派邏輯。
  • 2、<font color=red>重寫(xiě)findClass()</font>方法,通常來(lái)說(shuō),我們重寫(xiě)這個(gè)方法足夠了。這個(gè)方法是找到.class文件并加載進(jìn)內(nèi)存,存到byte[]二進(jìn)制數(shù)組中。

<font id="jump6_3">3、示例

(1)創(chuàng)建一個(gè)Demo.class文件

  • 1、創(chuàng)建package目錄com.test的Demo.java文件。
注意package包名必須有,這是java文件規(guī)范。默認(rèn)就是文件目錄。
  • 2、使用javac Demo.java 命令直接生成Demo.class文件
  • 3、放到/Users/wangzheng/workspace_java/com/test 目錄下,準(zhǔn)備使用自定義加載器加載
package com.test;

public class Demo {
    public Demo(){
        System.out.println("================================");
        System.out.println("我是MyClassLoader加載器加載進(jìn)內(nèi)存的。");
        System.out.println("================================");
    }
}

(2)自定義MyClassLoader類(lèi)加載器


import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;

public class MyClassLoaderDemo extends ClassLoader {

    //自定義根目錄
    String customRootPath = "";

    public MyClassLoaderDemo(String rootPath) {
        this.customRootPath = rootPath;
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        final Class<?> result;
        //1、拼接成自定義的.class存儲(chǔ)目錄。也就是Demo.class的目錄
        String filePath = customRootPath + '/' + name.replace('.', '/').concat(".class");

        //2、通過(guò)Demo.class目錄,文件流方式加載進(jìn)byte[]二進(jìn)制數(shù)組。
        byte[] bytes = loadPathToByteArray(filePath);

        //3、調(diào)用native的方法,將byte[]二進(jìn)制數(shù)組,轉(zhuǎn)成Class<?>字節(jié)碼對(duì)象
        result = defineClass(name, bytes, 0, bytes.length);

        return result;
    }

    //將路徑轉(zhuǎn)成byte[]
    private byte[] loadPathToByteArray(String filePath) {

        try {
            //通過(guò)路徑,文件流讀取到byte[]二進(jìn)制數(shù)組中
            FileInputStream fis = new FileInputStream(filePath);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            int bufferSize = 1024;
            byte[] buffer = new byte[bufferSize];
            int length = 0;
            while ((length = fis.read(buffer)) != -1) {
                baos.write(buffer, 0, length);
            }
            return baos.toByteArray();

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        try {

            //設(shè)置Demo.class文件的根目錄。
            MyClassLoaderDemo myClassLoaderDemo = new MyClassLoaderDemo("/Users/wangzheng/workspace_java");
            //加載Demo.class
            Class<?> aClass = myClassLoaderDemo.loadClass("com.test.Demo");
            //實(shí)例化Demo對(duì)象
            Object o = aClass.newInstance();

            System.out.println(o.getClass().getClassLoader());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

打印結(jié)果:

================================
我是MyClassLoader加載器加載進(jìn)內(nèi)存的。
================================
com.crm.web.test.MyClassLoaderDemo@64a294a6

<font id="jump7">六、加載類(lèi)的三種方式

<font id="jump7_1">1、分類(lèi)

1、靜態(tài)加載,通過(guò)<font color=red>new</font>關(guān)鍵字來(lái)實(shí)例化對(duì)象

2、動(dòng)態(tài)加載,通過(guò)<font color=red>Class.forName()</font>方式動(dòng)態(tài)加載對(duì)象(反射加載),然后調(diào)用類(lèi)的<font color=red>newInstance()</font>實(shí)例化

3、動(dòng)態(tài)加載,通過(guò)類(lèi)加載器的<font color=red>loadClass()</font>方法來(lái)加載類(lèi),然后調(diào)用類(lèi)的<font color=red>newInstance()</font>方法實(shí)例化對(duì)象

<font id="jump7_2">2、三種方式的區(qū)別

(1)第一種和第二種,使用的是默認(rèn)類(lèi)加載器(this.getClass.getClassLoader)。第三種可以使用用戶(hù)自定義的類(lèi)加載器

(2)如果需要在當(dāng)前路徑外加載.class文件,那么只能用第三種方式。

(3) <mark>Class.forName()和ClassLoader.loadClass()區(qū)別:

1、Class.forName(className)一個(gè)參數(shù)的方法,默認(rèn)是會(huì)加載進(jìn)內(nèi)存的同時(shí)給static分配內(nèi)存(初始化)。

2、Class.forName(className,initialize,loader)中間initialize可以控制是否進(jìn)行(初始化)

3、ClassLoader.loadClass()方式是不會(huì)給static分配內(nèi)存(<font color=red>初始化:參考上一篇文章。)。

public static Class<?> forName(String className)
                throws ClassNotFoundException {
    Class<?> caller = Reflection.getCallerClass();
    //第二個(gè)參數(shù),就是是否進(jìn)行初始化階段。
    return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

/**
 * 這種三個(gè)參數(shù)的,initialize就是決定是否進(jìn)行初始化的。
 **/
 public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
        throws ClassNotFoundException
    {
        Class<?> caller = null;
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            // Reflective call to get caller class is only needed if a security manager
            // is present.  Avoid the overhead of making this call otherwise.
            caller = Reflection.getCallerClass();
            if (sun.misc.VM.isSystemDomainLoader(loader)) {
                ClassLoader ccl = ClassLoader.getClassLoader(caller);
                if (!sun.misc.VM.isSystemDomainLoader(ccl)) {
                    sm.checkPermission(
                        SecurityConstants.GET_CLASSLOADER_PERMISSION);
                }
            }
        }
        return forName0(name, initialize, loader, caller);
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容