成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

自定義類加載器-從.class和.jar中讀取

Jackwoo / 3410人閱讀

摘要:在沒(méi)有指定自定義類加載器的情況下,這就是程序的默認(rèn)加載器。自定義類加載器雙親委派模型避免由于字節(jié)碼被多次加載。首先自定義類加載器,最重要的就是先繼承這個(gè)類。

一. 類加載器

JVM中的類加載器:在jvm中,存在兩種類加載器,
a) Boostrap ClassLoader:這個(gè)是由c++實(shí)現(xiàn)的,所以在方法區(qū)并沒(méi)有Class對(duì)象的實(shí)例存在。用于加載JAVA_HOME/bin目錄下的jar包

b) 其他類加載器:由java實(shí)現(xiàn),可以在方法區(qū)找到其Class對(duì)象。這里又細(xì)分為幾個(gè)加載器

擴(kuò)展類加載器(Extension ClassLoader):它負(fù)責(zé)用于加載JAVA_HOME/lib/ext目錄中的,或者被java.ext.dirs系統(tǒng)變量指定所指定的路徑中所有類庫(kù),開(kāi)發(fā)者可以直接使用擴(kuò)展類加載器。java.ext.dirs系統(tǒng)變量所指定的路徑的可以通過(guò)程序來(lái)查看。System.getProperty("java.ext.dirs")

應(yīng)用程序類加載器(Application ClassLoader):負(fù)責(zé)加載用戶類路徑上指定的類庫(kù)。開(kāi)發(fā)者可以直接使用這個(gè)類加載器。ps:在沒(méi)有指定自定義類加載器的情況下,這就是程序的默認(rèn)加載器。

自定義類加載器(User ClassLoader):

雙親委派模型:避免由于Class字節(jié)碼被多次加載。底層類加載器在收到一個(gè)類加載的請(qǐng)求的時(shí)候,都先把請(qǐng)求轉(zhuǎn)發(fā)給其父加載器(并不是一個(gè)繼承的關(guān)系),父類查找不到才會(huì)讓子類去加載。如果強(qiáng)制只有雙親委派模型,那么,web服務(wù)器的隔離是無(wú)法實(shí)現(xiàn)的。

??由于最近想做一個(gè)類似tomcat一樣的簡(jiǎn)易版web服務(wù)器來(lái)加深理解http請(qǐng)求的處理過(guò)程。我們都清楚,每個(gè)web應(yīng)用在tomcat中都可以使用自己版本的jar。除了少量的包,如Servlet-api.jar,還有一些java原生的包之外,tomcat是會(huì)為每個(gè)不同的應(yīng)用加載不同的jar包或者class,且彼此之間不會(huì)相互影響。這一步是通過(guò)自定義類加載器來(lái)實(shí)現(xiàn)的,在虛擬機(jī)層面,判斷兩個(gè)Class是否相等的前提是他們是同一個(gè)類加載器加載的,否則就沒(méi)有意義了。這篇文章簡(jiǎn)單的實(shí)現(xiàn)一個(gè)自定義的加載過(guò)程,PS:這個(gè)例子并沒(méi)有破壞雙親委派模型,因?yàn)槔又幸廊粫?huì)查找父類,如果找不到再使用子類加載。接下來(lái)筆者會(huì)再更新破壞雙親委派模型的博客,這里挖個(gè)坑。
??首先自定義類加載器,最重要的就是先繼承ClassLoader這個(gè)類。加載器的加載流程是,給出一個(gè)Class文件的全限定名,然后調(diào)用loadClass方法,這個(gè)方法每部會(huì)現(xiàn)在自己已經(jīng)加載的類中查找,如果找到就返回。找不到則向父類查找,如果父類都找不到這才開(kāi)始自己加載,調(diào)用findClass方法。所以我們只要覆蓋findClass方法就可以實(shí)現(xiàn)自己定義的加載了。順帶一提,ClassLoader中有一個(gè)方法叫defineClass(String, byte[], int, int);這個(gè)方法通過(guò)傳進(jìn)去一個(gè)Class文件的字節(jié)數(shù)組,就可以方法區(qū)生成一個(gè)Class對(duì)象。所以要實(shí)現(xiàn)findClass的目標(biāo)就很明確了,只要將Class文件讀取進(jìn)來(lái),然后生成byte數(shù)組,調(diào)用defineClass方法就可以了。

@Override
    protected Class findClass(String name){
        try {
            byte[] result = getClassFromFileOrMap(name);
            if(result == null){
                throw new FileNotFoundException();
            }else{
                return defineClass(name, result, 0, result.length);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

??那么如何找到Class文件呢?手動(dòng)生成/網(wǎng)絡(luò)下載我們就暫時(shí)不談?wù)?。這里只說(shuō)兩種最常見(jiàn)的,一是直接.class文件中查找,二是從jar包中加載。
從class文件中加載非常簡(jiǎn)單。只要找到相應(yīng)的文件,就可以通過(guò)字節(jié)流讀取進(jìn)來(lái)。代碼如下:

input = new FileInputStream(file);
ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
int bufferSize = 4096; 
byte[] buffer = new byte[bufferSize]; 
int bytesNumRead = 0; 
while ((bytesNumRead = input.read(buffer)) != -1) { 
    baos.write(buffer, 0, bytesNumRead); 
}
return baos.toByteArray();

??從jar讀取則相對(duì)麻煩一點(diǎn),java給我們提供了一個(gè)專門(mén)用來(lái)讀取jar包文件的類,抽象成一個(gè)JarFile的對(duì)象。通過(guò)調(diào)用這個(gè)對(duì)象的getInputStream方法,也是可以獲取文件的輸入流,從而讀取字節(jié)數(shù)組。筆者做了一點(diǎn)相應(yīng)的緩存,如果每次查找文件都要先讀取jar文件,再遍歷查找class文件是非常耗時(shí)的操作。于是,筆者選擇再加載之前,把所有的jar包中的所有class讀取到內(nèi)存中,保存在一個(gè)map對(duì)象中。建立一個(gè)全限定名和字節(jié)數(shù)組的映射。這樣在加載階段,就能省下很多的時(shí)間了。全部的代碼如下

package com.chasel.cloader;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 自定義的類加載器【子類優(yōu)先】
 * @author hujiancai
 * @description 
 * @data 2017年3月11日
 * @version v_0.1
 */
public class MyWebAppLoader extends ClassLoader{
    /**
     * lib:表示加載的文件在jar包中
     * 類似tomcat就是{PROJECT}/WEB-INF/lib/
     */
    private String lib;
    /**
     * classes:表示加載的文件是單純的class文件
     * 類似tomcat就是{PROJECT}/WEB-INF/classes/
     */
    private String classes;
    /**
     * 采取將所有的jar包中的class讀取到內(nèi)存中
     * 然后如果需要讀取的時(shí)候,再?gòu)膍ap中查找
     */
    private Map map;
    
    /**
     * 只需要指定項(xiàng)目路徑就好
     * 默認(rèn)jar加載路徑是目錄下{PROJECT}/WEB-INF/lib/
     * 默認(rèn)class加載路徑是目錄下{PROJECT}/WEB-INF/classes/
     * @param webPath
     * @throws MalformedURLException 
     * @throws SecurityException 
     * @throws NoSuchMethodException 
     */
    public MyWebAppLoader(String webPath) throws NoSuchMethodException, SecurityException, MalformedURLException{
        lib = webPath + "WEB-INF/lib/";
        classes = webPath + "WEB-INF/classes/";
        map = new HashMap(64);
        
        preReadJarFile();
    }

    /**
     * 按照父類的機(jī)制,如果在父類中沒(méi)有找到的類
     * 才會(huì)調(diào)用這個(gè)findClass來(lái)加載
     * 這樣只會(huì)加載放在自己目錄下的文件
     * 而系統(tǒng)自帶需要的class并不是由這個(gè)加載
     */
    @Override
    protected Class findClass(String name){
        try {
            byte[] result = getClassFromFileOrMap(name);
            if(result == null){
                throw new FileNotFoundException();
            }else{
                return defineClass(name, result, 0, result.length);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 從指定的classes文件夾下找到文件
     * @param name
     * @return
     */
    private byte[] getClassFromFileOrMap(String name){
        String classPath = classes + name.replace(".", File.separatorChar) + ".class";
        File file = new File(classPath);
        if(file.exists()){
            InputStream input = null;
            try {
                input = new FileInputStream(file);
                ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
                int bufferSize = 4096; 
                byte[] buffer = new byte[bufferSize]; 
                int bytesNumRead = 0; 
                while ((bytesNumRead = input.read(buffer)) != -1) { 
                    baos.write(buffer, 0, bytesNumRead); 
                }
                return baos.toByteArray();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally{
                if(input != null){
                    try {
                        input.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            
        }else{
            if(map.containsKey(name)) {
                //去除map中的引用,避免GC無(wú)法回收無(wú)用的class文件
                return map.remove(name);
            }
        }
        return null;
    }
    
    /**
     * 預(yù)讀lib下面的包
     */
    private void preReadJarFile(){
        List list = scanDir();
        for(File f : list){
            JarFile jar;
            try {
                jar = new JarFile(f);
                readJAR(jar);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    /**
     * 讀取一個(gè)jar包內(nèi)的class文件,并存在當(dāng)前加載器的map中
     * @param jar
     * @throws IOException
     */
    private void readJAR(JarFile jar) throws IOException{
        Enumeration en = jar.entries();
        while (en.hasMoreElements()){
            JarEntry je = en.nextElement();
            String name = je.getName();
            if (name.endsWith(".class")){
                String clss = name.replace(".class", "").replaceAll("/", ".");
                if(this.findLoadedClass(clss) != null) continue;
                
                InputStream input = jar.getInputStream(je);
                ByteArrayOutputStream baos = new ByteArrayOutputStream(); 
                int bufferSize = 4096; 
                byte[] buffer = new byte[bufferSize]; 
                int bytesNumRead = 0; 
                while ((bytesNumRead = input.read(buffer)) != -1) { 
                    baos.write(buffer, 0, bytesNumRead); 
                }
                byte[] cc = baos.toByteArray();
                input.close();
                map.put(clss, cc);//暫時(shí)保存下來(lái)
            }
        }
    }
    
    /**
     * 掃描lib下面的所有jar包
     * @return
     */
    private List scanDir() {
        List list = new ArrayList();
        File[] files = new File(lib).listFiles();
        for (File f : files) {
            if (f.isFile() && f.getName().endsWith(".jar"))
                list.add(f);
        }
        return list;
    }
    
    /**
     * 添加一個(gè)jar包到加載器中去。
     * @param jarPath
     * @throws IOException 
     */
    public void addJar(String jarPath) throws IOException{
        File file = new File(jarPath);
        if(file.exists()){
            JarFile jar = new JarFile(file);
            readJAR(jar);
        }
    }
}

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/66798.html

相關(guān)文章

  • JVM實(shí)戰(zhàn)---加載的過(guò)程

    任何程序都需要加載到內(nèi)存才能與CPU進(jìn)行交流 同理, 字節(jié)碼.class文件同樣需要加載到內(nèi)存中,才可以實(shí)例化類 ClassLoader的使命就是提前加載.class 類文件到內(nèi)存中 在加載類時(shí),使用的是Parents Delegation Model(溯源委派加載模型) Java的類加載器是一個(gè)運(yùn)行時(shí)核心基礎(chǔ)設(shè)施模塊,主要是在啟動(dòng)之初進(jìn)行類的加載、鏈接、初始化 showImg(https://s...

    bladefury 評(píng)論0 收藏0
  • 詳細(xì)深入分析 Java ClassLoader 工作機(jī)制

    摘要:作用負(fù)責(zé)將加載到中審查每個(gè)類由誰(shuí)加載父優(yōu)先的等級(jí)加載機(jī)制將字節(jié)碼重新解析成統(tǒng)一要求的對(duì)象格式類結(jié)構(gòu)分析為了更好的理解類的加載機(jī)制,我們來(lái)深入研究一下和他的方法。就算兩個(gè)是同一份字節(jié)碼,如果被兩個(gè)不同的實(shí)例所加載,也會(huì)認(rèn)為它們是兩個(gè)不同。 申明:本文首發(fā)于 詳細(xì)深入分析 ClassLoader 工作機(jī)制 ,如有轉(zhuǎn)載,注明原出處即可,謝謝配合。 什么是 ClassLoader ? 大家...

    mdluo 評(píng)論0 收藏0
  • JAVA加載機(jī)制全解析

    摘要:當(dāng)程序使用某個(gè)類時(shí),如果該類還沒(méi)被初始化,加載到內(nèi)存中,則系統(tǒng)會(huì)通過(guò)加載連接初始化三個(gè)過(guò)程來(lái)對(duì)該類進(jìn)行初始化。一旦一個(gè)類被加載到中之后,就不會(huì)再次載入了。它既可以從本地文件系統(tǒng)獲取二進(jìn)制文件來(lái)加載類,也可以遠(yuǎn)程主機(jī)獲取二進(jìn)制文件來(lái)加載類。 當(dāng)程序使用某個(gè)類時(shí),如果該類還沒(méi)被初始化,加載到內(nèi)存中,則系統(tǒng)會(huì)通過(guò)加載、連接、初始化三個(gè)過(guò)程來(lái)對(duì)該類進(jìn)行初始化。該過(guò)程就被稱為類的初始化 類加載 ...

    tomener 評(píng)論0 收藏0
  • 紀(jì)念我曾經(jīng)的 JAVA 姿勢(shì)

    摘要:,關(guān)閉不當(dāng)編譯器警告信息。創(chuàng)建固定大小的線程池。此線程池不會(huì)對(duì)線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)或者說(shuō)能夠創(chuàng)建的最大線程大小。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。 目前在搞 Node.js,曾經(jīng)的 JAVA 知識(shí)忘了好多,為此整理了下,感嘆下工業(yè)語(yǔ)言還是有相當(dāng)?shù)膬?yōu)勢(shì)的。 流 Java所有的流類位于java.io包中,都分別繼承字以下四種抽象流類型。 Type 字節(jié)...

    The question 評(píng)論0 收藏0
  • 加載機(jī)制,雙親委派模型,搞定大廠高頻面試題

    摘要:驗(yàn)證驗(yàn)證是連接階段的第一步,這一階段的目的是為了確保文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。字節(jié)碼驗(yàn)證通過(guò)數(shù)據(jù)流和控制流分析,確定程序語(yǔ)義是合法的符合邏輯的。 看過(guò)這篇文章,大廠面試你「雙親委派模型」,硬氣的說(shuō)一句,你怕啥? 讀該文章姿勢(shì) 打開(kāi)手頭的 IDE,按照文章內(nèi)容及思路進(jìn)行代碼跟蹤與思考 手頭沒(méi)有 IDE,先收藏,回頭看 (萬(wàn)一哪次面試問(wèn)...

    Object 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<