类加载器学习
ClassLoader
loadClass(加载指定的Java类)
findClass(查找指定的Java类)
findLoadedClass(查找JVM已经加载过的类)
defineClass(定义一个Java类)
resolveClass(链接指定的Java类)
一切的Java类都必须经过JVM加载后才能运行,而ClassLoader的主要作用就是Java类文件的加载。在JVM类加载器中最顶层的是Bootstrap ClassLoader(引导类加载器)、Extension ClassLoader(扩展类加载器)、App ClassLoader(系统类加载器),AppClassLoader是默认的类加载器,如果类加载时我们不指定类加载器的情况下,默认会使用AppClassLoader加载类,ClassLoader.getSystemClassLoader()返回的系统类加载器也是AppClassLoader。
值得注意的是某些时候我们获取一个类的类加载器时候可能会返回一个null值,如:java.io.File.class.getClassLoader()将返回一个null对象,因为java.io.File类在JVM初始化的时候会被Bootstrap ClassLoader(引导类加载器)加载(该类加载器实现于JVM层,采用C++编写),我们在尝试获取被Bootstrap ClassLoader类加载器所加载的类的ClassLoader时候都会返回null。
ClassLoader类有如下核心方法:
Java类动态加载方式
Java类加载方式分为显式和隐式,显式即我们通常使用Java反射或者ClassLoader来动态加载一个类对象,而隐式指的是类名.方法名()或new类实例。显式类加载方式也可以理解为类动态加载,我们可以自定义类加载器去加载任意的类。
常用的类动态加载方式:
// 反射加载TestHelloWorld示例 Class.forName("com.anbai.sec.classloader.TestHelloWorld"); // ClassLoader加载TestHelloWorld示例 this.getClass().getClassLoader().loadClass("com.anbai.sec.classloader.TestHelloWorld");
Class.forName("类名")默认会初始化被加载类的静态属性和方法,如果不希望初始化类可以使用Class.forName("类名", 是否初始化类, 类加载器),而ClassLoader.loadClass默认不会初始化类方法。
自定义类加载器过程
提前写一段恶意代码,比如弹计算机。,并将其编译成class文件放置C盘下,test.class
package com.superman.test; import java.io.IOException; public class test { static { try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { e.printStackTrace(); } } }
TestClassLoader.java 代码
package com.superman.test; import java.io.IOException; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Paths; public class TestClassLoader extends ClassLoader { // TestHelloWorld类名 private static String testClassName = "com.superman.test.test"; // TestHelloWorld类字节码 byte[] testClassBytes; { try { testClassBytes = Files.readAllBytes(Paths.get("C:\\test.class")); } catch (IOException e) { e.printStackTrace(); } } // private static byte[] testClassBytes = new byte[]{ // -54, -2, -70, -66, 0, 0, 0, 51, 0, 17, 10, 0, 4, 0, 13, 8, 0, 14, 7, 0, 15, 7, 0, // 16, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, // 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, // 1, 0, 5, 104, 101, 108, 108, 111, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, // 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 10, 83, 111, 117, 114, 99, // 101, 70, 105, 108, 101, 1, 0, 19, 84, 101, 115, 116, 72, 101, 108, 108, 111, 87, 111, // 114, 108, 100, 46, 106, 97, 118, 97, 12, 0, 5, 0, 6, 1, 0, 12, 72, 101, 108, 108, 111, // 32, 87, 111, 114, 108, 100, 126, 1, 0, 40, 99, 111, 109, 47, 97, 110, 98, 97, 105, 47, // 115, 101, 99, 47, 99, 108, 97, 115, 115, 108, 111, 97, 100, 101, 114, 47, 84, 101, 115, // 116, 72, 101, 108, 108, 111, 87, 111, 114, 108, 100, 1, 0, 16, 106, 97, 118, 97, 47, 108, // 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 0, 33, 0, 3, 0, 4, 0, 0, 0, 0, 0, 2, 0, 1, // 0, 5, 0, 6, 0, 1, 0, 7, 0, 0, 0, 29, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, // 1, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 7, 0, 1, 0, 9, 0, 10, 0, 1, 0, 7, 0, 0, 0, 27, 0, 1, // 0, 1, 0, 0, 0, 3, 18, 2, -80, 0, 0, 0, 1, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 10, 0, 1, 0, 11, // 0, 0, 0, 2, 0, 12 // }; @Override public Class<?> findClass(String name) throws ClassNotFoundException { // 只处理test类 if (name.equals(testClassName)) { // 调用JVM的native方法定义test类 return defineClass(testClassName, testClassBytes, 0, testClassBytes.length); } return super.findClass(name); } public static void main(String[] args) { // 创建自定义的类加载器 TestClassLoader loader = new TestClassLoader(); try { // 使用自定义的类加载器加载test类 Class testClass = loader.loadClass(testClassName); // 反射创建test类,等价于 test t = new test(); Object testInstance = testClass.newInstance(); // 反射获取hello方法 // Method method = testInstance.getClass().getMethod("hello"); // 反射调用hello方法,等价于 String str = t.hello(); // String str = (String) method.invoke(testInstance); // // System.out.println(str); } catch (Exception e) { e.printStackTrace(); } } }