亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Java類加載器及自定義

hiyang / 3578人閱讀

摘要:自定義類加載器示例代碼類加載器獲取的字節(jié)流字節(jié)流解密被加載的類測(cè)試代碼以上代碼,展示了自定義類加載器加載類的方法。這就需要自定義類加載器,以便對(duì)加載的類庫(kù)進(jìn)行隔離,否則會(huì)出現(xiàn)問題對(duì)于非的文件,需要轉(zhuǎn)為類,就需要自定義類加載器。

Java類加載器的作用是尋找類文件,然后加載Class字節(jié)碼到JVM內(nèi)存中,鏈接(驗(yàn)證、準(zhǔn)備、解析)并初始化,最終形成可以被虛擬機(jī)直接使用的Java類型。

類加載器種類

有兩種類加載器:
1 啟動(dòng)類加載器(Bootstrap ClassLoader)
由C++語(yǔ)言實(shí)現(xiàn)(針對(duì)HotSpot VM),負(fù)責(zé)將存放在lib目錄或-Xbootclasspath參數(shù)指定的路徑中的類庫(kù)加載到JVM內(nèi)存中,像java.lang.、java.util.、java.io.*等等??梢酝ㄟ^vm參數(shù)“-XX:+TraceClassLoading”來(lái)獲取類加載信息。我們無(wú)法直接使用該類加載器。

2 其他類加載器(Java語(yǔ)言實(shí)現(xiàn))
1)擴(kuò)展類加載器(Extension ClassLoader)
負(fù)責(zé)加載libext目錄或java.ext.dirs系統(tǒng)變量指定的路徑中的所有類庫(kù)。我們可以直接使用這個(gè)類加載器。
2)應(yīng)用程序類加載器(Application ClassLoader),或者叫系統(tǒng)類加載器
負(fù)責(zé)加載用戶類路徑(classpath)上的指定類庫(kù),我們可以直接使用這個(gè)類加載器。一般情況,如果我們沒有自定義類加載器默認(rèn)就是用這個(gè)加載器。
3)自定義類加載器
通過繼承ClassLoader類實(shí)現(xiàn),主要重寫findClass方法。

類加載器使用順序

在JVM虛擬機(jī)中,如果一個(gè)類加載器收到類加載的請(qǐng)求,它首先不會(huì)自己去嘗試加載這個(gè)類,而是把這個(gè)請(qǐng)求委派給父類加載器完成。每個(gè)類加載器都是如此,只有當(dāng)父加載器在自己的搜索范圍內(nèi)找不到指定的類時(shí)(即ClassNotFoundException),子加載器才會(huì)嘗試自己去加載。

也就是說(shuō),對(duì)于每個(gè)類加載器,只有父類(依次遞歸)找不到時(shí),才自己加載 。這就是雙親委派模型。

為什么需要雙親委派模型呢?這可以提高Java的安全性,以及防止程序混亂。
提高安全性方面:
假設(shè)我們使用一個(gè)第三方Jar包,該Jar包中自定義了一個(gè)String類,它的功能和系統(tǒng)String類的功能相同,但是加入了惡意代碼。那么,JVM會(huì)加載這個(gè)自定義的String類,從而在我們所有用到String類的地方都會(huì)執(zhí)行該惡意代碼。
如果有雙親委派模型,自定義的String類是不會(huì)被加載的,因?yàn)樽铐攲拥念惣虞d器會(huì)首先加載系統(tǒng)的java.lang.String類,而不會(huì)加載自定義的String類,防止了惡意代碼的注入。

防止程序混亂
假設(shè)用戶編寫了一個(gè)java.lang.String的同名類,如果每個(gè)類加載器都自己加載的話,那么會(huì)出現(xiàn)多個(gè)String類,導(dǎo)致混亂。如果本加載器加載了,父加載器則不加載,那么以哪個(gè)加載的為準(zhǔn)又不能確定了,也增加了復(fù)雜度。

自定義類加載器

我們可以自定義類加載器,只需繼承ClassLoader抽象類,并重寫findClass方法(如果要打破雙親委派模型,需要重寫loadClass方法)。原因可以查看ClassLoader的源碼:

protected Class loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // First, check if the class has already been loaded
        Class c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    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();
                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;
    }
}

這個(gè)是ClassLoader中的loadClass方法,大致流程如下:
1)檢查類是否已加載,如果是則不用再重新加載了;
2)如果未加載,則通過父類加載(依次遞歸)或者啟動(dòng)類加載器(bootstrap)加載;
3)如果還未找到,則調(diào)用本加載器的findClass方法;
以上可知,類加載器先通過父類加載,父類未找到時(shí),才有本加載器加載。

因?yàn)樽远x類加載器是繼承ClassLoader,而我們?cè)倏磃indClass方法:

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

可以看出,它直接返回ClassNotFoundException。
因此,自定義類加載器必須重寫findClass方法。

自定義類加載器示例代碼:
類加載器HClassLoader:

class HClassLoader extends ClassLoader {

private String classPath;

public HClassLoader(String classPath) {
    this.classPath = classPath;
}

@Override
protected Class findClass(String name) throws ClassNotFoundException {
    try {
        byte[] data = loadByte(name);
        return defineClass(name, data, 0, data.length);
    } catch (Exception e) {
        e.printStackTrace();
        throw new ClassNotFoundException();
    }

}

/**
 * 獲取.class的字節(jié)流
 *
 * @param name
 * @return
 * @throws Exception
 */
private byte[] loadByte(String name) throws Exception {
    name = name.replaceAll(".", "/");
    FileInputStream fis = new FileInputStream(classPath + "/" + name + ".class");
    int len = fis.available();
    byte[] data = new byte[len];
    fis.read(data);
    fis.close();

    // 字節(jié)流解密
    data = DESInstance.deCode("1234567890qwertyuiopasdf".getBytes(), data);

    return data;
  }
}

被加載的類Car:

public class Car {

    public Car() {
        System.out.println("Car:" + getClass().getClassLoader());
        System.out.println("Car Parent:" + getClass().getClassLoader().getParent());
    }

    public String print() {
        System.out.println("Car:print()");
        return "carPrint";
    }
 }

測(cè)試代碼:

@Test
public void testClassLoader() throws Exception {
    HClassLoader myClassLoader = new HClassLoader("e:/temp/a");
    Class clazz = myClassLoader.loadClass("com.ha.Car");
    Object o = clazz.newInstance();
    Method print = clazz.getDeclaredMethod("print", null);
    print.invoke(o, null);
}

以上代碼,展示了自定義類加載器加載類的方法。

需要注意的是:
執(zhí)行測(cè)試代碼前,必須將Car.class文件移動(dòng)到e:/temp/a下,并且按包名建立層級(jí)目錄(這里為com/ha/)。因?yàn)槿绻灰苿?dòng)Car.class文件,那么Car類會(huì)被AppClassLoader加載(自定義類加載器的parent是AppClassLoader)。

自定義類加載器的應(yīng)用

上面介紹了Java類加載器的相關(guān)知識(shí)。對(duì)于自定義類加載器,哪里可以用到呢?
主流的Java Web服務(wù)器,比如Tomcat,都實(shí)現(xiàn)了自定義的類加載器。因?yàn)樗鉀Q幾個(gè)問題:
1)Tomcat上可以部署多個(gè)不同的應(yīng)用,但是它們可以使用同一份類庫(kù)的不同版本。這就需要自定義類加載器,以便對(duì)加載的類庫(kù)進(jìn)行隔離,否則會(huì)出現(xiàn)問題;
2)對(duì)于非.class的文件,需要轉(zhuǎn)為Java類,就需要自定義類加載器。比如JSP文件。

這里舉一個(gè)其它的例子:Java核心代碼的加密。
假設(shè)我們項(xiàng)目當(dāng)中,有一些核心代碼不想讓別人反編譯看到。當(dāng)前知道有兩種方法,一種是通過代碼混淆(推薦Allatori,商用收費(fèi));一種是自己編寫加密算法,對(duì)字節(jié)碼加密,加大反編譯難度。

代碼混淆如果用Allatori,比較簡(jiǎn)便。注意控制自己編寫類的訪問權(quán)限即可。接口用public,內(nèi)部方法用private,其他的用默認(rèn)的(即不加訪問修飾符)或者protected。代碼混淆這里不過多說(shuō)明,這里主要介紹一下字節(jié)碼加密。
大概的流程可以如下:

.class加密代碼:

@Test
public void testEncode() {
    String classFile = "e:/temp/a/com/ha/Car.class";
    FileInputStream fis = null;
    try {
        fis = new FileInputStream(classFile);
        int len = fis.available();
        byte[] data = new byte[len];
        fis.read(data);
        fis.close();

        data = DESInstance.enCode("1234567890qwertyuiopasdf".getBytes(), data);

        String outFile = "e:/temp/a/com/ha/EnCar.class";
        FileOutputStream fos = new FileOutputStream(outFile);
        fos.write(data);
        fos.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

}

類加載器中解密,查看上文中的:

// 字節(jié)流解密
data = DESInstance.deCode("1234567890qwertyuiopasdf".getBytes(), data);

加解密工具類:

public class DESInstance {

    private static String ALGORITHM = "DESede";

    /**
     * 加密
     *
     * @param key
     * @param src
     * @return
     */
    public static byte[] enCode(byte[] key, byte[] src) {

        byte[] value = null;
        SecretKey deskey = new SecretKeySpec(key, ALGORITHM);
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, deskey);
            value = cipher.doFinal(src);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return value;
    }

    /**
     * 解密
     *
     * @param key
     * @param src
     * @return
     */
    public static byte[] deCode(byte[] key, byte[] src) {
        byte[] value = null;
        SecretKey deskey = new SecretKeySpec(key, ALGORITHM);

        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, deskey);
            value = cipher.doFinal(src);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return value;
    }
}

注意秘鑰是24位,否則會(huì)報(bào)錯(cuò):
java.security.InvalidKeyException: Invalid key length
如果解密密碼錯(cuò)誤,則是如下錯(cuò)誤:
javax.crypto.BadPaddingException: Given final block not properly padded

當(dāng)然,這樣做還是會(huì)被反編譯破解,要加大難度,還需要其他處理的。

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

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

相關(guān)文章

  • Hibernate配置及自定義注冊(cè)映射文件

    摘要:一配置屬性詳解可以在各式各樣不同環(huán)境下工作而設(shè)計(jì)的因此存在著大量的配置參數(shù)。以簡(jiǎn)便操作,多數(shù)配置參數(shù)都有默認(rèn)的配置值也是我們?nèi)粘J褂玫谋仨毱贰? Hibernate (開放源代碼的對(duì)象關(guān)系映射框架) Hibernate是一個(gè)開放源代碼的對(duì)象關(guān)系映射框架,它對(duì)JDBC進(jìn)行了非常輕量級(jí)的對(duì)象封裝, 它將POJO與數(shù)據(jù)庫(kù)表建立映射關(guān)系,是一個(gè)全自動(dòng)的orm框架,hibernat...

    coordinate35 評(píng)論0 收藏0
  • JVM 的 工作原理,層次結(jié)構(gòu) 以及 GC工作原理

    摘要:執(zhí)行引擎作用執(zhí)行字節(jié)碼,或者執(zhí)行本地方法運(yùn)行時(shí)數(shù)據(jù)區(qū)其實(shí)就是指在運(yùn)行期間,其對(duì)內(nèi)存空間的劃分和分配。 雖是讀書筆記,但是如轉(zhuǎn)載請(qǐng)注明出處https://uestc-dpz.github.io..拒絕伸手復(fù)制黨 JVM Java 虛擬機(jī) Java 虛擬機(jī)(Java virtual machine,JVM)是運(yùn)行 Java 程序必不可少的機(jī)制。JVM實(shí)現(xiàn)了Java語(yǔ)言最重要的特征:即平臺(tái)...

    qqlcbb 評(píng)論0 收藏0
  • HystrixFeign的詳細(xì)構(gòu)建過程及自定義擴(kuò)展功能

    摘要:重要以及內(nèi)部類都是訪問級(jí)別,可以注入自定義的。的目的是將包裝成風(fēng)格以便開發(fā)。示例以下示例參考其中的和是自定義的。需要自定義,則實(shí)現(xiàn)類,需要自定義,則實(shí)現(xiàn)即可總結(jié)由于構(gòu)建過程所用到的是訪問級(jí)別的,不能使用自定義的以及是,給了我們擴(kuò)展的空間。 spring-cloud-openfeign-core-2.1.1.RELEASE.jar 中 HystrixFeign 的詳細(xì)構(gòu)建過程: @Ena...

    曹金海 評(píng)論0 收藏0
  • android知識(shí)大總結(jié) - 收藏集 - 掘金

    摘要:中簡(jiǎn)單搞定接口訪問,以及簡(jiǎn)析掘金最近總結(jié)的一些經(jīng)驗(yàn),對(duì)于或中使用接口的一些心得。這里,本文將數(shù)據(jù)結(jié)構(gòu)之學(xué)習(xí)總結(jié)掘金前言前面介紹了的數(shù)據(jù)結(jié)構(gòu),今天抽空學(xué)習(xí)總結(jié)一下另一種數(shù)據(jù)結(jié)構(gòu)。淺析事件傳遞掘金中的事件傳遞主要涉及三個(gè)方法和。 Android 系統(tǒng)中,那些能大幅提高工作效率的 API 匯總(持續(xù)更新中...) - 掘金前言 條條大路通羅馬。工作中,實(shí)現(xiàn)某個(gè)需求的方式往往不是唯一的,這些不...

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

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

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<