java 类加载器 解密_JAVA类加载器总结整理
一、What(是什么?)
1、概念
Java類加載器是Java運行時環(huán)境的一部分,負責動態(tài)加載Java類到JVM的內存空間中。每個Java類必須由某個類加載器裝入到內存中。每一個類加載器都有一個父類加載器(BootStrap引導類加載器沒有)。
2、 JVM中有3個默認的加載器:
(1) BootStrap:引導類加載器。這個加載器很特殊,它不是JAVA類,因此它不需要被別人加載,它嵌套在JVM內核里,也就是說JVM啟動的時候BootStrap就啟動了,它是C++寫的二進制代碼,可以加載別的類。這也是為什么System.class.getClassLoader()結果為null的原因,因為它不是JAVA類,所以它的引用返回null。負責加載核心Java庫,存儲在/jre/lib/rt.jar
(2) ExtClassLoader:擴展類加載器。
(3) AppClassLoader:根據類路徑來加載java類。一般我們自定義的類都是通過這個AppClassLoader加載。
3、類加載器及其委托機制
(1) 當Java虛擬機加載一個類時,如何加載呢?
首先當前線程的類加載器去加載線程中的第一個類(如類A):
l 如果類A引用了類B,Java虛擬機將加載類A的加載器去加載類B
l 還可以直接調用ClassLoader.loadClass()方法來指定某個類加載器去加載某個類
(2) 委托機制的作用—防止內存中出現(xiàn)多分同樣的字節(jié)碼
例如類A和類B都要加載System類:
l 如果不用委托機制,都是自己加載,那么類A會加載一份Sysem字節(jié)碼,同時類B也會加載一份字節(jié)碼看,這樣內存中就出現(xiàn)了兩份System字節(jié)碼。
l 如果使用委托機制,會遞歸地向父類查找,首選用BootStrap嘗試加載,如果找不到就向下。這里System就能在BootStrap中找到然后加載。如果此時B也加載System,也從BootStrap,此時BootStrap發(fā)現(xiàn)已經加載過System字節(jié)碼,則直接返回內存中的System字節(jié)碼而不是重新加載,這樣就保證了內存中只有一份字節(jié)碼。
例如:用戶使用一個自定義的類(沒有使用自定義類加載器),那么系統(tǒng)就開始從AppClassLoader向父類加載器發(fā)送請求,一直到BootStrap,然后BootStrap類加載器沒有父類,于是就開始查找對應路徑下是否有符合要求的類。如果沒有,則又向下查詢,最終回到AppClassLoader(請求的發(fā)起者),如果有則返回,沒有則會拋出ClassNotFoundException的異常。如果在AppClassLoader之前,其他類加載器已經找到,則由對應的類加載其返回。
代碼實例1:
public class ClassLoaderTest {
public static void main(String[] args) {
System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());
System.out.println("=========================================");
ClassLoader loader = ClassLoaderTest.class.getClassLoader();
while(loader != null) {
System.out.println(loader.getClass().getName());
loader = loader.getParent();
}
System.out.println(loader);
}
}
代碼實例2:
首先我們自定義一個類:
public class Secret{
public String key(){
return "The key is 5132561";
}
}
然后打印加載這個類的類加載器:
System.out.println(new Secret().getClass().getClassLoader().getClass().getName());
輸出結果如下:
因為從BootStrapèExtClassLoaderèAppClassLoader這個過程中,只有到AppClassLoader才找到對應的類,所以打印AppClassLoader。
代碼實例3:
如果我們將Secret的字節(jié)碼打成jar包并放到ExtClassLoader所指向的目錄–/jre/lib/ext目錄下,那么BootStrapèExtClassLoader,到ExtClassLoader就找到了對應的類并返回,這時就打印ExtClassLoader。
打包成jar包并保存到/jre/lib/ext目錄下。
再次輸出:
System.out.println(new Secret().getClass().getClassLoader().getClass().getName());
二、How(如何自定義類加載器?)
自定義的類加載器必須繼承ClassLoader,并實現(xiàn)重載findClass方法。
代碼實例:
public class DecodeClassLoader extends ClassLoader{
private String classDir;
public DecodeClassLoader(){}
public DecodeClassLoader(String classDir){
this.classDir = classDir;
}
@Override
protected Class> findClass(String name) {
File f = new File(classDir,name.substring(name.lastIndexOf(".") + 1) + ".class");
try {
InputStream in = new FileInputStream(f);
ByteArrayOutputStream out = new ByteArrayOutputStream();
encode(in, out);
byte[] bytes = out.toByteArray();
in.close();
out.close();
return defineClass(bytes, 0, bytes.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
return null;
}
private static void encode(InputStream in, OutputStream out) throws IOException{
int b = 0;
while((b=in.read())!= -1){
out.write(b ^ 0xff);
}
}
}
三、Where(在什么地方使用?)
(1)運行時裝載或卸載類。常用于:實現(xiàn)腳本語言
用于bean生成器
允許用戶定義的擴展性
允許命名控件之間的通信。
(2)改變Java字節(jié)碼的裝入,例如Java類字節(jié)碼的加密
(3)修改以裝入的字節(jié)碼
四、類加載器的綜合應用實例
通過一個加密類對一個重要的類字節(jié)碼進行加密,使得只有使用解密類加載器才可以成功加載并使用。示意圖如下:
(1)DecodeClassLoader.java見上面。
(2)加密類:對Secret字節(jié)碼進行加密
public class EncodeUtil {
private static void encode(InputStream in, OutputStream out) throws IOException{
int b = 0;
while((b=in.read())!= -1){
out.write(b ^ 0xff);
}
}
public static void main(String[] args) throws IOException {
String srcPath = "E:\\java_workspace\\004ClassLoaderDemo\\bin\\com\\shuwoom\\classloader\\Secret.class";//args[0];
String destDir = "shuwoomlib";
FileInputStream in = new FileInputStream(srcPath);
String destFileName = srcPath.substring(srcPath.lastIndexOf("\\") + 1);
String destPath = destDir + "\\" + destFileName;
System.out.println(destPath);
FileOutputStream out = new FileOutputStream(destPath);
encode(in,out);
in.close();
out.close();
}
}
(3)被加載的類:
public class Secret{
public String key(){
return "The key is 5132561";
}
}
首先運行EncodeUtil加密工具類,將Secret.class文件加密并保存到指定的shuwoomlib目錄下,此時,在bin/bom/shuwoom/classloader目錄下的Secret是未經加密的字節(jié)碼。現(xiàn)在我們將shuwoomlib目錄下加密的Secret.class替換掉bin/bom/shuwoom/classloader目錄下的Secret.class。那么AppClassLoader找到的就是經過加密的字節(jié)碼。
//如果直接使用AppClassLoader加載,會報錯。
System.out.println(new Secret().key());
編譯運行會報錯:
//通過DecodeClassLoader類加載器獲得Secret原字節(jié)碼
ClassLoader classLoader = new DecodeClassLoader("shuwoomlib");
Class clazz = classLoader.loadClass("Secret");
Method getKeyMethod = clazz.getMethod("key");
System.out.println(getKeyMethod.invoke(clazz.newInstance(), null));
此時才能正常使用。
總結
以上是生活随笔為你收集整理的java 类加载器 解密_JAVA类加载器总结整理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java.lang.unsatisfie
- 下一篇: java中的equals拿什么鞋的_Ja