Java ClassLoader

@Author: Patrilic @Time: 2020-4-06 23:17:44

Java ClassLoader

0x00 前言

ClassLoader 顾名思义就是类的加载器,用来动态加载JavaClass到JVM

简单来说,我们编写的.java文件经过javac编译后转换成java字节代码(.class)文件,而类加载器通过读取这个.class文件,转换成java.lang.Class的一个实例,用实例来表示一个java类。然后通过实例的newInstance()方法可以创建出该类的对象。

0x01 JVM运行机制

关于Java虚拟机这篇文章的学习,可以参考这篇文章:

https://www.artima.com/insidejvm/ed2/jvm.html

JVM的生命周期

JVM在Java程序开始执行时运行,结束时停止。每一个Java程序都拥有一个单独的JVM进程

Java应用启动时,生成一个Runtime实例,当程序完成后,该实例死亡。 如果同时启动多个Java应用,那么就会产生同样数量的Runtime实例,互不干扰。 而拥有main函数的class将作为进程的起点

main函数作为初始线程起点,可以控制其他任意线程。JVM中的线程分为 守护线程非守护线程

JVM架构

Java ClassLoader

类加载子系统

从上面的图可以看到,Class文件必须要经过 class loader subsystem 才能进入到runtime实例中

而这里的类加载子系统也称之为类加载器, 而系统提供的类加载器有以下三个:

java.lang.ClassLoader
$JAVA_HOME/jre/lib/*.jar
ClassLoader.getSystemClassLoader()

当然,我们也可以自定义一个ClassLoader,只需要继承 java.lang.ClassLoader 即可

类的加载过程

JVM将类的加载分为三个步骤:Load, Link, Initialize

Java ClassLoader

Load – 装载

装载的过程就是查找和导入Class文件

负责找到二进制字节码并加载至JVM中,JVM通过类名、类所在的包名、ClassLoader完成类的加载。因此,标识一个被加载了的类:类名 + 包名 + ClassLoader实例ID。

Link – 链接

分为验证, 准备, 解析三个步骤

  • 验证 – 确保类的加载的正确性
  • 准备 – 为类的静态变量分配内存,并将其初始化为默认值
  • 解析 – 把类中的符号引用转换为直接引用

Initialize – 初始化

初始化,为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。在Java中对类变量进行初始值设定有两种方式:

  • 声明类变量是指定初始值
  • 使用静态代码块为类变量指定初始值

类的初始化触发:

  1. 实例化对象的时候
  2. 访问类的静态变量
  3. 调用类的静态方法
  4. 反射(class.forName())
  5. 初始化一个类的子类会先初始化父类
  6. JVM启动时标明的启动类,即文件名和类名相同的那个类

双亲委派模型

Java ClassLoader

简单的理解: 就是一个类需要被加载时,类加载器总会把加载委派给父类去加载,一直递归到顶层,也就是说一直是从 Bootstrap ClassLoader 开始加载,当父类无法加载时,再从子类进行加载。

双亲委派模型的优点,在加载一些系统类时,比如 java.lang.Object , 总会由 Bootstrap ClassLoader%JAVA_HOME%/jre/lib/rt.jar 中寻找,保证了使用的Object是正确的,而不会被中间人修改。

线程上下文类加载器

关于SPI机制这里就不作赘述了,主要是因为BootStrap ClassLoader必须委托子类去加载提供的服务,例如JDBC的接口

而线程上下文类加载器就可以解决这个问题,具体方法提供在 java.lang.Thread

getContextClassLoader()setContextClassLoader(ClassLoader cl) 用来获取和设置线程的上下文类加载器, 如果没有对线程上下文加载器进行set操作,会自动继承父类的线程上下文加载器

0x02 ClassLoader

首先,Java类加载分为显式和隐式, 显式加载就是利用Java反射和ClassLoader直接对类进行加载,例如:

Class.forName("com.patrilic.ClassLoader.Test"); 
// 默认初始化类方法

this.getClass().getClassLoader().loadClass("com.patrilic.ClassLoader.Test"); 
// 不初始化类方法

而隐式加载就是调用 Class.Method , 或者new一个新的实例时,也会对类进行加载

ClassLoader提供的一些与类加载相关的方法

方法 说明
getParent() 返回该类加载器的父类加载器。
loadClass(String name) 加载名称为 name的类,返回的结果是 java.lang.Class类的实例。
findClass(String name) 查找名称为 name的类,返回的结果是 java.lang.Class类的实例。
findLoadedClass(String name) 查找名称为 name的已经被加载过的类,返回的结果是 java.lang.Class类的实例。
defineClass(String name, byte[] b, int off, int len) 把字节数组 b中的内容转换成 Java 类,返回的结果是 java.lang.Class类的实例。这个方法被声明为 final的。
resolveClass(Class<?> c) 链接指定的 Java 类。

自定义ClassLoader加载ByteCode

//Test.java

package com.patrilic.classLoader;
import java.util.*;
import java.io.*;
import java.net.*;

public class Test {
	public void RunShell() throws IOException {
		Process p = Runtime.getRuntime().exec("cat /etc/passwd");
		OutputStream os = p.getOutputStream();
		InputStream in = p.getInputStream();
		DataInputStream dis = new DataInputStream(in);
		String disr = dis.readLine();
		while ( disr != null ) {
				System.out.println(disr); disr = dis.readLine(); 
			}
	}
}

// ClassLoaderTest.java

package com.patrilic.ClassLoader;

import java.lang.reflect.Method;

public class ClassLoaderTest extends ClassLoader {

    private static String testClassName = "com.patrilic.classLoader.Test";

    byte[] code = new byte[]{-54, -2, -70, -66, 0, 0, 0, 51, 0, 70, 10, 0, 13, 0, 30, 10, 0, 31, 0, 32, 8, 0, 33, 10, 0, 31, 0, 34, 10, 0, 35, 0, 36, 10, 0, 35, 0, 37, 7, 0, 38, 10, 0, 7, 0, 39, 10, 0, 7, 0, 40, 9, 0, 41, 0, 42, 10, 0, 43, 0, 44, 7, 0, 45, 7, 0, 46, 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, 8, 82, 117, 110, 83, 104, 101, 108, 108, 1, 0, 13, 83, 116, 97, 99, 107, 77, 97, 112, 84, 97, 98, 108, 101, 7, 0, 45, 7, 0, 47, 7, 0, 48, 7, 0, 49, 7, 0, 38, 7, 0, 50, 1, 0, 10, 69, 120, 99, 101, 112, 116, 105, 111, 110, 115, 7, 0, 51, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 9, 84, 101, 115, 116, 46, 106, 97, 118, 97, 12, 0, 14, 0, 15, 7, 0, 52, 12, 0, 53, 0, 54, 1, 0, 15, 99, 97, 116, 32, 47, 101, 116, 99, 47, 112, 97, 115, 115, 119, 100, 12, 0, 55, 0, 56, 7, 0, 47, 12, 0, 57, 0, 58, 12, 0, 59, 0, 60, 1, 0, 23, 106, 97, 118, 97, 47, 105, 111, 47, 68, 97, 116, 97, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109, 12, 0, 14, 0, 61, 12, 0, 62, 0, 63, 7, 0, 64, 12, 0, 65, 0, 66, 7, 0, 67, 12, 0, 68, 0, 69, 1, 0, 29, 99, 111, 109, 47, 112, 97, 116, 114, 105, 108, 105, 99, 47, 99, 108, 97, 115, 115, 76, 111, 97, 100, 101, 114, 47, 84, 101, 115, 116, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 1, 0, 17, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 80, 114, 111, 99, 101, 115, 115, 1, 0, 20, 106, 97, 118, 97, 47, 105, 111, 47, 79, 117, 116, 112, 117, 116, 83, 116, 114, 101, 97, 109, 1, 0, 19, 106, 97, 118, 97, 47, 105, 111, 47, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 1, 0, 19, 106, 97, 118, 97, 47, 105, 111, 47, 73, 79, 69, 120, 99, 101, 112, 116, 105, 111, 110, 1, 0, 17, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 82, 117, 110, 116, 105, 109, 101, 1, 0, 10, 103, 101, 116, 82, 117, 110, 116, 105, 109, 101, 1, 0, 21, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 82, 117, 110, 116, 105, 109, 101, 59, 1, 0, 4, 101, 120, 101, 99, 1, 0, 39, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 80, 114, 111, 99, 101, 115, 115, 59, 1, 0, 15, 103, 101, 116, 79, 117, 116, 112, 117, 116, 83, 116, 114, 101, 97, 109, 1, 0, 24, 40, 41, 76, 106, 97, 118, 97, 47, 105, 111, 47, 79, 117, 116, 112, 117, 116, 83, 116, 114, 101, 97, 109, 59, 1, 0, 14, 103, 101, 116, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109, 1, 0, 23, 40, 41, 76, 106, 97, 118, 97, 47, 105, 111, 47, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109, 59, 1, 0, 24, 40, 76, 106, 97, 118, 97, 47, 105, 111, 47, 73, 110, 112, 117, 116, 83, 116, 114, 101, 97, 109, 59, 41, 86, 1, 0, 8, 114, 101, 97, 100, 76, 105, 110, 101, 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, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 121, 115, 116, 101, 109, 1, 0, 3, 111, 117, 116, 1, 0, 21, 76, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114, 105, 110, 116, 83, 116, 114, 101, 97, 109, 59, 1, 0, 19, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114, 105, 110, 116, 83, 116, 114, 101, 97, 109, 1, 0, 7, 112, 114, 105, 110, 116, 108, 110, 1, 0, 21, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86, 0, 33, 0, 12, 0, 13, 0, 0, 0, 0, 0, 2, 0, 1, 0, 14, 0, 15, 0, 1, 0, 16, 0, 0, 0, 29, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 1, 0, 17, 0, 0, 0, 6, 0, 1, 0, 0, 0, 6, 0, 1, 0, 18, 0, 15, 0, 2, 0, 16, 0, 0, 0, -110, 0, 3, 0, 6, 0, 0, 0, 60, -72, 0, 2, 18, 3, -74, 0, 4, 76, 43, -74, 0, 5, 77, 43, -74, 0, 6, 78, -69, 0, 7, 89, 45, -73, 0, 8, 58, 4, 25, 4, -74, 0, 9, 58, 5, 25, 5, -58, 0, 21, -78, 0, 10, 25, 5, -74, 0, 11, 25, 4, -74, 0, 9, 58, 5, -89, -1, -20, -79, 0, 0, 0, 2, 0, 17, 0, 0, 0, 34, 0, 8, 0, 0, 0, 8, 0, 9, 0, 9, 0, 14, 0, 10, 0, 19, 0, 11, 0, 29, 0, 12, 0, 36, 0, 13, 0, 41, 0, 14, 0, 59, 0, 16, 0, 19, 0, 0, 0, 28, 0, 2, -1, 0, 36, 0, 6, 7, 0, 20, 7, 0, 21, 7, 0, 22, 7, 0, 23, 7, 0, 24, 7, 0, 25, 0, 0, 22, 0, 26, 0, 0, 0, 4, 0, 1, 0, 27, 0, 1, 0, 28, 0, 0, 0, 2, 0, 29};
    @Override
    public Class<?> findClass(String name) throws ClassNotFoundException {
        if (name.equals(testClassName)) {
            // defineClass 将byteCode转换成类, 赋给testClassName
            return defineClass(testClassName, code, 0, code.length);
        }
        return super.findClass(name);
    }
    public static void main(String[] args) {
        // 实例化ClassLoader
        ClassLoaderTest loader = new ClassLoaderTest();

        try {
            // 加载testClassName
            Class testClass = loader.loadClass(testClassName);

            // 创建testClassName类的实例
            Object testInstance = testClass.newInstance();

            // 反射获取RunShell方法
            Method method = testInstance.getClass().getMethod("RunShell");

            // 反射调用RunShell方法
            String result = (String) method.invoke(testInstance);

            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

Java ClassLoader

URLClassLoader

Java ClassLoader

直接看园长的代码,URLClassLoader提供远程加载jar的能力

package com.patrilic.test;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;

/**
 * Creator: yz
 * Date: 2019/12/18
 */
public class TestURLClassLoader {

    public static void main(String[] args) {
        try {
            // 定义远程加载的jar路径
            URL url = new URL("https://javaweb.org/tools/cmd.jar");

            // 创建URLClassLoader对象,并加载远程jar包
            URLClassLoader ucl = new URLClassLoader(new URL[]{url});

            // 定义需要执行的系统命令
            String cmd = "cat /etc/passwd";

            // 通过URLClassLoader加载远程jar包中的CMD类
            Class cmdClass = ucl.loadClass("CMD");

            // 调用CMD类中的exec方法,等价于: Process process = CMD.exec("whoami");
            Process process = (Process) cmdClass.getMethod("exec", String.class).invoke(null, cmd);

            // 获取命令执行结果的输入流
            InputStream           in   = process.getInputStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[]                b    = new byte[1024];
            int                   a    = -1;

            // 读取命令执行结果
            while ((a = in.read(b)) != -1) {
                baos.write(b, 0, a);
            }

            // 输出命令执行结果
            System.out.println(baos.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

Java ClassLoader

原文 
https://patrilic.top/2020/04/06/Java ClassLoader/

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Java ClassLoader

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址