动态类加载

类加载的机制

  1. 类加载与反序列化

    类加载的时候会执行代码

    初始化:静态代码块

    实例化:构造代码块、无参构造函数

  2. 动态类加载

    Class.forname 默认是要初始化的,但是也可以选择不初始化的forName 版本

User类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package org.javaSecBase.myClassLoader;

public class User {
public String name;
// 构造函数
public User(String name) {
System.out.println("构造函数 User");
this.name = name;
System.out.println("初始化name 为: "+name);
}

private void say(String name){
System.out.println("I am " + name);
}

public void say(){
System.out.println("I am " + name);
}

}

Main

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
package org.javaSecBase.myClassLoader;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Run {
public static void main(String[] args) throws ClassNotFoundException {
UserClassLoader userClassLoader = new UserClassLoader("/Users/august/code/java/JavaSecLearn/src/main/java/org/javaSecBase/myClassLoader/User.class");
try {
Class<?> clazz = userClassLoader.loadClass("org.javaSecBase.myClassLoader.User");

Constructor<?> userConstructor = clazz.getConstructor(String.class);
Object user = userConstructor.newInstance("august");
Method sayMethod = clazz.getDeclaredMethod("say", String.class);
sayMethod.setAccessible(true);
sayMethod.invoke(user, "august");

Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(user, "august2");

Method publicSayMethod = clazz.getMethod("say");
publicSayMethod.invoke(user);

} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException |
NoSuchFieldException e) {
throw new RuntimeException(e);
}

}
}

双亲委派模型

image-20251008160504143

底层原理,实现任意类加载

loadClass->findClass(重写)->defineClass(从字节a码加载类)

实现类加载

方法一

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
52
53
54
55
56
57
58
59
60
package org.javaSecBase.myClassLoader;

import java.io.IOException;
import java.lang.reflect.Constructor;
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 Run {
public void run1(){
UserClassLoader userClassLoader = new UserClassLoader("/Users/august/code/java/JavaSecLearn/src/main/java/org/javaSecBase/myClassLoader/User.class");
try {
Class<?> clazz = userClassLoader.loadClass("org.javaSecBase.myClassLoader.User");

Constructor<?> userConstructor = clazz.getConstructor(String.class);
Object user = userConstructor.newInstance("august");
Method sayMethod = clazz.getDeclaredMethod("say", String.class);
sayMethod.setAccessible(true);
sayMethod.invoke(user, "august");

Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(user, "august2");

Method publicSayMethod = clazz.getMethod("say");
publicSayMethod.invoke(user);

} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException |
NoSuchFieldException e) {
throw new RuntimeException(e);
}

}

public void run2() throws NoSuchMethodException, IOException, InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchFieldException {
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();

Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class,byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);

byte[] bytes = Files.readAllBytes(Paths.get("/Users/august/code/java/learn/src/main/java/Test.class"));

Class userClass = (Class) defineClassMethod.invoke(systemClassLoader, "Test", bytes, 0, bytes.length);

Object userConstructor = userClass.newInstance();




}
public static void main(String[] args) throws ClassNotFoundException, IOException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {
Run run = new Run();
run.run2();
}
}

在第二个run2 中只需要一个class 文件和可控的defineClass 函数就可以完成危险的动态类加载

方法二

除了上述方法可以完成类加载还可以使用unsafe

但是unsafe 不能直接使用,所以需要使用反射进行创建

这里是通过单例模式的theUnsafe 变量直接获取对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void run3(){
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();

// 由于unsafe 内部有一个单厉模式,那么直接获取这个成员变量
try {
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafeField.get(null);
byte[] bytes = Files.readAllBytes(Paths.get("/Users/august/code/java/learn/src/main/java/Test.class"));
Class<?> testClass = unsafe.defineClass("Test", bytes, 0, bytes.length, systemClassLoader, null);
testClass.newInstance();

} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}

}

方法三

除了获取单例模式的theUnsafe 变量

还可以直接反射加载

MyUnsafe.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
package org.example;

import sun.misc.Unsafe;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class MyUnsafe {
public Unsafe unsafe;
public static MyUnsafe New(){
MyUnsafe myUnsafe = new MyUnsafe();
try {
Constructor constructor = Unsafe.class.getDeclaredConstructor();
constructor.setAccessible(true);
Unsafe unsafe = (Unsafe) constructor.newInstance();
myUnsafe.unsafe = unsafe;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
return myUnsafe;
}
}

run4

1
2
3
4
5
6
7
8
9
10
11
public void run4() throws ClassNotFoundException, IOException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, InstantiationException {

ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
MyUnsafe myUnsafe = MyUnsafe.New();
byte[] bytes = Files.readAllBytes(Paths.get("/Users/august/code/java/learn/src/main/java/Test.class"));
Class<?> testClass = myUnsafe.unsafe.defineClass("Test", bytes, 0, bytes.length, systemClassLoader, null);
testClass.newInstance();


}

方法四

Sring 中可以有一个public 的unsafe 获取的函数


动态类加载
https://tsy244.github.io/2025/10/08/java安全/动态类加载/
Author
August Rosenberg
Posted on
October 8, 2025
Licensed under