1. <tt id="5hhch"><source id="5hhch"></source></tt>
    1. <xmp id="5hhch"></xmp>

  2. <xmp id="5hhch"><rt id="5hhch"></rt></xmp>

    <rp id="5hhch"></rp>
        <dfn id="5hhch"></dfn>

      1. 最新java classloader詳解

        時(shí)間:2023-03-04 02:41:41 JAVA認(rèn)證 我要投稿
        • 相關(guān)推薦

        2016最新java classloader詳解

          Classloader 類加載器,用來加載Java類到 Java 虛擬機(jī)中的一種加載器。那么Classloader 類有什么原理呢?下面跟yjbys小編一起來學(xué)習(xí)一下!

          JAVA啟動(dòng)后,是經(jīng)過JVM各級(jí)ClassLoader來加載各個(gè)類到內(nèi)存。為了更加了解加載過程,我通過分析和寫了一個(gè)簡單的ClassLoader來粗淺的分析它的原理。

          JVM的ClassLoader分三層,分別為Bootstrap ClassLoader,Extension ClassLoader,System ClassLoader,他們不是類繼承的父子關(guān)系,是邏輯上的上下級(jí)關(guān)系。

          Bootstrap ClassLoader是啟動(dòng)類加載器,它是用C++編寫的,從%jre%/lib目錄中加載類,或者運(yùn)行時(shí)用-Xbootclasspath指定目錄來加載。

          Extension ClassLoader是擴(kuò)展類加載器,從%jre%/lib/ext目錄加載類,或者運(yùn)行時(shí)用-Djava.ext.dirs制定目錄來加載。

          System ClassLoader,系統(tǒng)類加載器,它會(huì)從系統(tǒng)環(huán)境變量配置的classpath來查找路徑,環(huán)境變量里的.表示當(dāng)前目錄,是通過運(yùn)行時(shí)-classpath或-Djava.class.path指定的目錄來加載類。

          一般自定義的Class Loader可以從java.lang.ClassLoader繼承,不同classloader加載相同的類,他們?cè)趦?nèi)存也不是相等的,即它們不能互相轉(zhuǎn)換,會(huì)直接拋異常。java.lang.ClassLoader的核心加載方法是loadClass方法,如:

          protected synchronized Class loadClass(String name, boolean resolve)

          throws ClassNotFoundException

          {

          // First, check if the class has already been loaded

          Class c = findLoadedClass(name);

          if (c == null) {

          try {

          if (parent != null) {

          c = parent.loadClass(name, false);

          } else {

          c = findBootstrapClass0(name);

          }

          } catch (ClassNotFoundException e) {

          // If still not found, then invoke findClass in order

          // to find the class.

          c = findClass(name);

          }

          }

          if (resolve) {

          resolveClass(c);

          }

          return c;

          }

          通過上面加載過程,我們能知道JVM默認(rèn)是雙親委托加載機(jī)制,即首先判斷緩存是否有已加載的類,如果緩存沒有,但存在父加載器,則讓父加載器加載,如果不存在父加載器,則讓Bootstrap ClassLoader去加載,如果父類加載失敗,則調(diào)用本地的findClass方法去加載。

          可以通過下面三條語句,輸入現(xiàn)在加載的各個(gè)classloader的加載路徑:

          System.out.println("sun.boot.class.path:" + System.getProperty("sun.boot.class.path"));

          System.out.println("java.ext.dirs:" + System.getProperty("java.ext.dirs"));

          System.out.println("java.class.path:" +System.getProperty("java.class.path"));

          ClassLoader cl = Thread.currentThread().getContextClassLoader();//ClassLoader.getSystemClassLoader()

          System.out.println("getContextClassLoader:" +cl.toString());

          System.out.println("getContextClassLoader.parent:" +cl.getParent().toString());

          System.out.println("getContextClassLoader.parent2:" +cl.getParent().getParent());

          輸出結(jié)果為:

          sun.boot.class.path:C:\Program Files\Java\jre7\lib\resources.jar;C:\Program Files\Java\jre7\lib\rt.jar;C:\Program Files\Java\jre7\lib\sunrsasign.jar;C:\Program Files\Java\jre7\lib\jsse.jar;C:\Program Files\Java\jre7\lib\jce.jar;C:\Program Files\Java\jre7\lib\charsets.jar;C:\Program Files\Java\jre7\classes

          java.ext.dirs:C:\Program Files\Java\jre7\lib\ext;C:\Windows\Sun\Java\lib\ext

          java.class.path:E:\MyProjects\workspace\TestConsole\bin

          getContextClassLoader:sun.misc.Launcher$AppClassLoader@19dbc3b

          getContextClassLoader.parent:sun.misc.Launcher$ExtClassLoader@b103dd

          getContextClassLoader.parent2:null

          從上面的運(yùn)行結(jié)果可以看出邏輯上的層級(jí)繼承關(guān)系。雙親委托機(jī)制的作用是防止系統(tǒng)jar包被本地替換,因?yàn)椴檎曳椒ㄟ^程都是從最底層開始查找。 因此,一般我們自定義的classloader都需要采用這種機(jī)制,我們只需要繼承java.lang.ClassLoader實(shí)現(xiàn)findclass即可,如果需要更多控制,自定義的classloader就需要重寫loadClass方法了,比如tomcat的加載過程,這個(gè)比較復(fù)雜,可以通過其他文檔資料查看相關(guān)介紹。

          各個(gè)ClassLoader加載相同的類后,他們是不互等的,這個(gè)當(dāng)涉及多個(gè)ClassLoader,并且有通過當(dāng)前線程上線文獲取ClassLoader后轉(zhuǎn)換特別需要注意,可以通過線程的setContextClassLoader設(shè)置一個(gè)ClassLoader線程上下文,然后再通過Thread.currentThread().getContextClassLoader()獲取當(dāng)前線程保存的Classloader。但是自定義的類文件,放到Bootstrap ClassLoader加載目錄,是不會(huì)被Bootstrap ClassLoader加載的,因?yàn)樽鳛閱?dòng)類加載器,它不會(huì)加載自己不熟悉的jar包的,并且類文件必須打包成jar包放到加載器加載的根目錄,才可能被擴(kuò)展類加載器所加載。

          下面我自定義一個(gè)簡單的classloader:

          public class TestClassLoader extends ClassLoader {

          //定義文件所在目錄

          private static final String DEAFAULTDIR="E:\\MyProjects\\workspace\\TestConsole\\bin\\";

          public Class findClass(String name) throws ClassNotFoundException {

          byte[] b = null;

          try {

          b = loadClassData(GetClassName(name));

          } catch (Exception e) {

          e.printStackTrace();

          }

          return defineClass(name, b, 0, b.length);

          }

          @Override

          protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {

          if(name.startsWith("java.")){try {

          return super.loadClass(name, false);

          } catch (ClassNotFoundException e) {

          e.printStackTrace();

          }

          }

          byte[] b = null;

          try {

          b = loadClassData(GetClassName(name));

          } catch (Exception e) {

          e.printStackTrace();

          }

          return defineClass(name, b, 0, b.length);

          }

          private byte[] loadClassData(String filepath) throws Exception {

          int n =0;

          BufferedInputStream br = new BufferedInputStream(

          new FileInputStream(

          new File(filepath)));

          ByteArrayOutputStream bos= new ByteArrayOutputStream();

          while((n=br.read())!=-1){

          bos.write(n);

          }

          br.close();

          return bos.toByteArray();

          }

          public static String GetClassName(String name){

          return DEAFAULTDIR+name.replace('.','/')+".class";

          }

          }

          這個(gè)自定義的ClassLoader重寫了loadclass方法,但不用默認(rèn)的雙親委托,比如java.lang包下面的都無法解析,這里我簡單的判斷如果是java.開始的包則用父類去解析,能簡單的滿足雙親委托機(jī)制,但是其他相關(guān)非系統(tǒng)類加載也沒有用父類加載了。

          測(cè)試代碼如:

          TestClassLoader liuloader = new TestClassLoader();

          Myrunner runner = new Myrunner();

          runner.setContextClassLoader(liuloader);

          runner.start();

          Myrunner是我自定義繼承自Thread的線程,通過設(shè)置線程上下文的classloader后,線程內(nèi)部測(cè)試代碼如:

          ClassLoader cl1 = Thread.currentThread().getContextClassLoader();

          System.out.println(cl1);

          它將會(huì)輸出:

          com.liu.ClassLoader.TestClassLoader@347cdb,說明已經(jīng)為當(dāng)前線程上下文設(shè)置了自定義的Classloader了,如果這個(gè)線程內(nèi)部通過這個(gè)classloader加載一個(gè)類,再轉(zhuǎn)換成當(dāng)前的類,如代碼:

          Class c = cl1.loadClass("com.liu.ClassLoader.TestLoader2"); TestLoader2 tloader = (TestLoader2)c.newInstance();

          則為拋java.lang.ClassCastException異常: com.liu.ClassLoader.TestLoader2 cannot be cast to com.liu.ClassLoader.TestLoader2。

          因?yàn)閏l1當(dāng)前是 TestClassLoader加載的,而這個(gè)TestLoader2的類還是默認(rèn)由AppClassLoader加載,因此它們不能隱式轉(zhuǎn)換,Classloader加載相同的類,內(nèi)存認(rèn)為它們是沒有關(guān)系的對(duì)象。

          如果把我自定義的TestClassLoader里的LoadClass方法去掉,則采用了雙親委托機(jī)制,這樣我們除了指定的類以外,其他都會(huì)優(yōu)先用父類來加載。這樣可以解決剛才的java.lang.ClassCastException異常問題,為加載的對(duì)象建立一個(gè)抽象父類,自定義的Classloader負(fù)責(zé)加載子類,父類統(tǒng)一交給AppClassLoader或父加載器來加載,這樣線程內(nèi)部可以使用類試:

          Class c = cl1.loadClass("com.liu.ClassLoader.TestLoader2");

          BaseTest tloader = (BaseTest)c.newInstance();

          BaseTest是TestLoader2的父類,因?yàn)锽aseTest都是AppClassLoader或父加載器加載的,因此可以達(dá)到成功隱式轉(zhuǎn)換的目的。

          對(duì)于Tomcat等幾個(gè)處理的Classloader都是自定義并重寫了loadclass方法,內(nèi)部會(huì)更復(fù)雜處理。

        【最新java classloader詳解】相關(guān)文章:

        Java基礎(chǔ)知識(shí)詳解12-07

        最新連詞的種類詳解01-04

        科目一考試最新技巧口訣詳解02-27

        2016最新Java認(rèn)證筆試題及答案01-21

        2016年最新JAVA編程題及答案03-04

        最新飛行員考試知識(shí)詳解03-26

        2017年最新java面試題及答案03-09

        最新中興Java語言筆試真題及答案03-07

        最新科目四考試過關(guān)技巧詳解03-10

        最新用友T3操作流程詳解10-17

        国产高潮无套免费视频_久久九九兔免费精品6_99精品热6080YY久久_国产91久久久久久无码

        1. <tt id="5hhch"><source id="5hhch"></source></tt>
          1. <xmp id="5hhch"></xmp>

        2. <xmp id="5hhch"><rt id="5hhch"></rt></xmp>

          <rp id="5hhch"></rp>
              <dfn id="5hhch"></dfn>