第一步: 給你一個編譯好的class文件以及它的包名,建立一個對象出來。java
1)class文件源代碼數組
- package com.wsc.classloader;
-
- public class Tool{
-
- public void print() {
- }
- }
2)使用javac Tool.java 編譯成class文件ide
3)將Tool.class文件讀取到內存中,生成byte[]數組加密
- private byte[] loadClassFile(String clazzPath) throws IOException {
- FileInputStream fis = new FileInputStream(clazzPath);
- BufferedInputStream bis = new BufferedInputStream(fis);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
-
- byte[] buffer = new byte[1024 * 256];
- int ch = 0;
- while ((ch = bis.read(buffer, 0, buffer.length)) != -1) {
- baos.write(buffer, 0, ch);
- }
- return baos.toByteArray();
- }
4)自定義ClassLoader,使用ClassLoader中的defineClass方法:protected final Class<?> defineClass(String name, byte[] b, int off, int len)。參數分別是類名稱,class文件對應的字節數組,起始位置和終止位置。spa
- @Override
- protected Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException {
- Class<?> c = findLoadedClass(name);
- if (c == null) {
- c = defineClass(name, data, 0, data.length);
- }
- return c;
- }
總體代碼是:調試
- package com.wsc.classloader;
-
- import java.io.BufferedInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.FileInputStream;
- import java.io.IOException;
-
- public class ClassLoaderOne extends ClassLoader {
-
- public static void main(String[] args) throws Exception {
-
- ClassLoaderOne loader = new ClassLoaderOne(
- "E:\\JAVA\\JAVAFX\\ClassLoader\\libs\\Tool.class");
- Class<?> clazz = loader.loadClass("com.wsc.classloader.Tool");
- Object o = clazz.newInstance();
- System.out.println(o.getClass().getClassLoader());
-
- }
-
- private byte[] data;
-
- public ClassLoaderOne(String clazzPath) throws IOException {
- data = loadClassFile(clazzPath);
- }
-
-
- private byte[] loadClassFile(String clazzPath) throws IOException {
- FileInputStream fis = new FileInputStream(clazzPath);
- BufferedInputStream bis = new BufferedInputStream(fis);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
-
- byte[] buffer = new byte[1024 * 256];
- int ch = 0;
- while ((ch = bis.read(buffer, 0, buffer.length)) != -1) {
- baos.write(buffer, 0, ch);
- }
- return baos.toByteArray();
- }
-
- @Override
- protected Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException {
- Class<?> c = findLoadedClass(name);
- if (c == null) {
- c = defineClass(name, data, 0, data.length);
- }
- return c;
- }
-
- }
感受是這樣的,跑一下:對象
- Exception in thread "main" java.lang.SecurityException: Prohibited package name: java.lang
- at java.lang.ClassLoader.preDefineClass(Unknown Source)
- at java.lang.ClassLoader.defineClass(Unknown Source)
- at java.lang.ClassLoader.defineClass(Unknown Source)
- at com.wsc.classloader.ClassLoaderOne.loadClass(ClassLoaderOne.java:52)
- at java.lang.ClassLoader.loadClass(Unknown Source)
- at java.lang.ClassLoader.defineClass1(Native Method)
- at java.lang.ClassLoader.defineClass(Unknown Source)
- at java.lang.ClassLoader.defineClass(Unknown Source)
- at com.wsc.classloader.ClassLoaderOne.loadClass(ClassLoaderOne.java:52)
- at java.lang.ClassLoader.loadClass(Unknown Source)
- at com.wsc.classloader.ClassLoaderOne.main(ClassLoaderOne.java:14)
意思是:禁止加載名爲java.lang的包。blog
緣由是:雖然Tool類中沒有使用任何引入java.lang下類,可是它的父類Object是在java.lang下的,classloader加載Tool類時會把它全部的關係網都加載出來才行,父類Object確定是要加載的。接口
這樣就簡單了!無非多寫一個If else.使用父加載器(類加載器都有一個父類加載器)加載便可。內存
- @Override
- protected Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException {
- Class<?> c = findLoadedClass(name);
- if (name.equals("java.lang.Object")) {
- ClassLoader parent = getParent();
- c = parent.loadClass(name);
- }
- if (c == null) {
- c = defineClass(name, data, 0, data.length);
- }
- return c;
- }
跑一下結果是:
- com.wsc.classloader.ClassLoaderOne@ca470
第二步:新的問題
- Method[] methods = clazz.getMethods();
- for (int i = 0; i < methods.length; i++) {
- String name = methods[i].getName();
- System.out.println(name);
- Class<?>[] params = methods[i].getParameterTypes();
- for (int j = 0; j < params.length; j++) {
- System.out.println(params[j].toString());
- }
- }
這個時候仍是會報剛纔的錯誤,由於Method類也在java.lang包下,只能在增長一個If else.
顯然,代碼應該這樣寫
- @Override
- protected Class<?> loadClass(String name, boolean resolve)
- throws ClassNotFoundException {
- Class<?> c = findLoadedClass(name);
- if (c == null) {
-
- if (getParent() != null) {
- try {
- c = getParent().loadClass(name);
- } catch (Exception e) {
-
- }
-
- }
-
- if (c == null) {
- c = defineClass(name, data, 0, data.length);
- }
- }
-
- return c;
- }
打印結果:
- toString
- print
- class java.lang.String
- getClass
- hashCode
- equals
- class java.lang.Object
- notify
- notifyAll
- wait
- long
- int
- wait
- wait
- long
- com.wsc.classloader.ClassLoaderOne@fcfa52
第三步:若是本地能夠經過.class文件建立,遠程固然也已同一個道理(若是須要加密,在本地多一個解密便可)。若是class文件是遠程調用的話,本地通常使用接口或者反射兩種方法調用。首選是接口,反射一是效率,而是要清楚全部的方法名稱、參數名稱過於麻煩。
因爲遠程加載class文件到本地,若是出錯很難定位出錯位置。幸虧,classloader使用規則默認是根據URLClassLoader來使用的,會先根據檢查本地是否有該類,因此能夠直接將源碼放在本地便可調試,固然發佈的時候必定要刪除。
如圖:
經過這個基本的入門程序能夠了解ClassLoader的基本流程。
1