用戶定製本身的ClassLoader可以實現如下的一些應用:java
findClass()的功能是找到class文件並把字節碼載入到內存中。本身定義的ClassLoader通常覆蓋改方法。以便使用不一樣的載入路徑,而後調用defineClass()解析字節碼。安全
defineClass()方法用來將byte字節流解析成JVM可以識別的Class對象。markdown
有了這種方法意味着咱們不單單可以經過class文件實例化對象。還可以經過其它方式實例化對象,如咱們經過網絡接收到一個類的字節碼,拿這個字節碼流直接建立類的Class對象形式實例化對象。網絡
本身定義的載入器可以覆蓋方法loadClass()以便定義不一樣的載入機制。
假設本身定義的載入器僅覆蓋了findClass(),而未覆蓋loadClass(即載入規則同樣,但載入路徑不一樣);則調用getClass().getClassLoader()返回的仍然是AppClassLoader!因爲真正的load類,仍是AppClassLoader.ide
如下演示一個方法載入指定路徑下(」D:/workspace_jee/JavaTest/src/」)的類文件。post
this
package classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class PathClassLoader extends ClassLoader {
private String classPath;
public PathClassLoader(String classPath)
{
this.classPath = classPath;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] classData = getData(name); if (classData == null) { throw new ClassNotFoundException(); } else { return defineClass(name, classData, 0, classData.length); } } private byte[] getData(String className) { String path = classPath + File.separatorChar+className.replace('.', File.separatorChar)+".class"; try { InputStream is = new FileInputStream(path); ByteArrayOutputStream stream = new ByteArrayOutputStream(); byte[] buffer = new byte[2048]; int num = 0; while((num = is.read(buffer))!=-1) { stream.write(buffer,0,num); } return stream.toByteArray(); } catch(IOException e) { e.printStackTrace(); } return null; } public static void main(String args[]) throws ClassNotFoundException, InstantiationException, IllegalAccessException { ClassLoader pcl = new PathClassLoader("D:/workspace_jee/JavaTest/src/"); Class c = pcl.loadClass("classloader.SingleClass"); System.out.println(c.newInstance()); } }
輸出結果:classloader.SingleClass@22a7fdef加密
假設咱們從網路上下載一個class文件的字節碼,但是爲了安全性在傳輸以前對這個字節碼進行了簡單的加密處理,而後再經過網絡傳輸。spa
當client接收到這個類的字節碼後需要通過解密才幹還原成原始的類格式。而後再經過ClassLoader的defineClass()方法建立這個類的實例,最後完畢類的載入工做。code
比方上面的代碼中,在獲取到字節碼(byte[] classData = getData(name);)以後再經過一個類似如下的代碼:
private byte[] deCode(byte[] src){
byte[] decode = null;
//do something niubility! 精密解碼過程
return decode;
}
將字節碼解碼成所需要的字節碼就能夠。
JVM默認不能熱部署類,因爲載入類時會去調用findLoadedClass(),假設類已被載入,就不會再次載入。
JVM推斷類是否被載入有兩個條件:完整類名是否同樣,ClasssLoader是不是同一個
因此要實現熱部署的話,僅僅需要使用ClassLoader的不一樣實例來載入。
假設用同一個ClassLoader實例來載入同一個類,則會拋出LinkageError.
Jsp就是一個熱部署的樣例。
例如如下所看到的:
package classloader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class ClassReloader extends ClassLoader {
private String classPath;
String classname = "classloader.SingleClass";
public ClassReloader(String classpath)
{
this.classPath = classpath;
}
protected Class<?> findClass(String name) throws ClassNotFoundException{
byte [] classData = getData(name);
if(classData == null)
{
throw new ClassNotFoundException();
}
else
{
return defineClass(classname,classData,0,classData.length);
}
}
private byte[] getData(String className)
{
String path = classPath+className;
try
{
InputStream is = new FileInputStream(path);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int num = 0;
while((num = is.read(buffer))!=-1)
{
stream.write(buffer,0,num);
}
return stream.toByteArray();
}
catch(IOException e)
{
e.printStackTrace();
}
return null;
}
public static void main(String[] args)
{
try
{
String path = "D:/workspace_jee/JavaTest/src/classloader/";
ClassReloader reloader = new ClassReloader(path);
Class r = reloader.findClass("SingleClass.class");
System.out.println(r.newInstance());
// ClassReloader reloader2 = new ClassReloader(path);
Class r2 = reloader.findClass("SingleClass.class");
System.out.println(r2.newInstance());
}
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e)
{
e.printStackTrace();
}
}
}
這段代碼的執行結果爲:
java.lang.LinkageError: loader (instance of classloader/ClassReloader): attempted duplicate class definition for name: "classloader/SingleClass"
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.lang.ClassLoader.defineClass(Unknown Source)
at classloader.ClassReloader.findClass(ClassReloader.java:26)
at classloader.ClassReloader.main(ClassReloader.java:62)
比較兩個類是否「相等」,僅僅有在這兩個類是由同一個類載入器載入的前提下才有意義。不然。即便這兩個類來源於同一個Class文件,被同一個虛擬機載入,僅僅要載入他們的類載入器不一樣,那這兩個類就必然不相等。