跟着我体验一下传说中非常厉害的类加载器吧!
制作加密 class 目标类 我们要加载的类很简单,它只有一个 hello()
方法。编译这个类生成 class 文件,待会要用
1 2 3 4 5 6 7 8 9 10 package ClassLoader;public class Hello { public void hello () { System.out.println("Hello, classLoader!" ); } public static void main (String[] args) { System.out.println(); } }
加密 下面这段代码读取了刚才生成的 Hello.class ,加密之后保存为 Hello.xlass
encode()
实现了一个简单的加密,加载类的时候使用同样的方法就可以解密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package ClassLoader;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;public class EncodeFile { public static void main (String[] args) { String name = "Hello" ; EncodeFile ef = new EncodeFile(); byte [] fileByteArray = ef.loadFile(name); fileByteArray = ef.encode(fileByteArray);; ef.storeFile(fileByteArray, name); } public byte [] loadFile(String name){ File f = new File(this .getClass().getResource(name + ".class" ).getPath()); byte [] fileByteArray = new byte [(int )f.length()]; try { new FileInputStream(f).read(fileByteArray); } catch (Exception e) { e.printStackTrace(); } return fileByteArray; } void storeFile (byte [] fileByteArray, String name) { String p = this .getClass().getResource("" ).getPath(); File file = new File(p + "/" + name + ".xlass" ); try (FileOutputStream fop = new FileOutputStream(file)) { if (!file.exists()) { file.createNewFile(); } fop.write(fileByteArray); fop.flush(); } catch (IOException e) { e.printStackTrace(); } } public byte [] encode (byte [] fileToEncode){ for (int i=0 ; i< fileToEncode.length; i++){ fileToEncode[i] = (byte ) (255 - fileToEncode[i]); } return fileToEncode; } }
加载 接下来我们定义自己的加载器,把刚才的 xlass 文件解密之后加载到 JVM 中,并反射运行它的 hello()
方法。
具体方法是继承 ClassLoader
类,覆盖它的 findClass()
方法,在该方法中使用 defineClass()
将字节流转成 Class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package ClassLoader;import java.io.File;import java.io.FileInputStream;import java.lang.reflect.Method;public class MyClassLoader extends ClassLoader { public static void main (String[] args) { try { Class<?> clazz = new MyClassLoader().findClass("Hello" ); Object obj = clazz.getConstructor().newInstance(); Method method = clazz.getMethod("hello" ); method.invoke(obj); } catch (Exception e) { e.printStackTrace(); } } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { File f = new File(this .getClass().getResource(name + ".xlass" ).getPath()); byte [] fileByteArray = new byte [(int )f.length()]; try { new FileInputStream(f).read(fileByteArray); } catch (Exception e) { e.printStackTrace(); } fileByteArray = decode(fileByteArray); String pack = this .getClass().getPackage().getName(); return defineClass(pack + "." + name, fileByteArray, 0 , fileByteArray.length); } public byte [] decode (byte [] fileToDecode){ for (int i=0 ; i< fileToDecode.length; i++){ fileToDecode[i] = (byte ) (255 - fileToDecode[i]); } return fileToDecode; } }
运行结果 我们的类加载器解密了 xlass 并将它加载到了 JVM 中
原理 类加载的原则是双亲委派模型:如果一个类加载器收到了类加载的请求,它首先会把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。
我们定义的这个 findClass()
方法会在下面这个地方调用,如代码所示,如果该类还没有被加载并且父加载器无法加载个类(当然肯定不能加载),就会调用我们定义的 findClass()
去加载这个类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { 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) { } if (c == null ) { long t1 = System.nanoTime(); c = findClass(name); PerfCounter.getParentDelegationTime().addTime(t1 - t0); PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }