目标构建两个简单的ClassLoader
任务:
- 生成类字节码。
- 读取类字节码并加载该类。
- 远程读取类字节码并加载该类。
生成类的字节码
- 工具使用:
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
- 代码实现:
package com.miracle.study.loader;
import javassist.*;
import java.io.IOException;
/**
* @author Miracle
* @date 2021/4/21 17:26
*/
public class GenClassUtils {
public static final String CLASS_NAME = "test.go";
public static final String METHOD_NAME = "run";
/**
* 生成类的字节码
* @return
* @throws CannotCompileException
* @throws IOException
*/
public static byte[] genClass() throws CannotCompileException, IOException {
var pool = ClassPool.getDefault();
// 判断是否已存在对应的class
var testClass = pool.getOrNull(CLASS_NAME);
// 如果有的话,需要进行解冻,不然无法修改
if (testClass != null){
// 解冻class
testClass.defrost();
}
// 创建一个class
var ctClass = pool.makeClass(CLASS_NAME);
// 创建一个方法,返回值、方法名、参数、应用的class
var ctMethod = new CtMethod(CtClass.voidType, METHOD_NAME, new CtClass[]{}, ctClass);
// 设置方法的修饰符,公开、私有。。。
ctMethod.setModifiers(Modifier.PUBLIC);
// 设置方法体
ctMethod.setBody("{System.out.println(\"SUCCESS\");}");
// 将方法添加入类中
ctClass.addMethod(ctMethod);
// 将类转换为字节码
return ctClass.toBytecode();
}
}
读取类字节码并加载该类
- 实现思路:
- 继承ClassLoader,重写findClass方法。
- 读取类字节码,并且使用ClassLoader加载它defineClass(类名称, 类字节码, 读取起始位置,读取终止位置)。
- 代码实现:
package com.miracle.study.loader;
import javassist.*;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
/**
* @author Miracle
* @date 2021/4/21 17:00
*/
public class LocalLoaderTest extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name == GenClassUtils.CLASS_NAME){
try {
// 本地生成类字节码
var classByteCode = GenClassUtils.genClass();
// 使用父类方法加载类
return defineClass(name, classByteCode, 0, classByteCode.length);
} catch (CannotCompileException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
// 双亲委派查找class
return super.findClass(name);
}
@Test
public void test() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
var classLoader = new LocalLoaderTest();
// 找到类的数据
var clazz = classLoader.loadClass(GenClassUtils.CLASS_NAME);
// 利用反射创建类的对象
var test = clazz.getConstructor().newInstance();
// 利用反射调用方法
test.getClass().getMethod(GenClassUtils.METHOD_NAME).invoke(test);
}
}
远程读取类字节码并加载该类
- 实现思路:
- 在自实现的classLoader中利用TCP/IP通信获取到类字节码
- 其余步骤实现与上一个案例一致
- 代码实现:
package com.miracle.study.loader;
import javassist.CannotCompileException;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* @author Miracle
* @date 2021/4/21 17:25
*/
public class NetLoaderTest extends ClassLoader {
private byte[] classBytes;
public NetLoaderTest(){
connection();
}
/**
* 连接远程服务器获取类字节码
*/
private void connection(){
// 进行TCP/IP远程连接
try (var socket = new Socket("localhost", 8080)){
// 读取所有的类字节码
classBytes = socket.getInputStream().readAllBytes();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (name == GenClassUtils.CLASS_NAME){
// 使用父类方法加载类
return defineClass(name, classBytes, 0, classBytes.length);
}
// 双亲委派查找class
return super.findClass(name);
}
/**
* 构建一个远程服务用于发送类字节码(需要先启动)
* @throws IOException
* @throws CannotCompileException
*/
@Test
public void serverTest() throws IOException, CannotCompileException {
var serverSocket = new ServerSocket(8080);
var classByteCode = GenClassUtils.genClass();
while (true){
try (var socket = serverSocket.accept()){
var socketOutPutStream = socket.getOutputStream();
socketOutPutStream.write(classByteCode);
socketOutPutStream.flush();
}
}
}
@Test
public void test() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
var netClassLoader = new NetLoaderTest();
// 找到类的数据
var clazz = netClassLoader.findClass(GenClassUtils.CLASS_NAME);
// 利用反射创建类的对象
var test = clazz.getConstructor().newInstance();
// 利用反射调用方法
test.getClass().getMethod(GenClassUtils.METHOD_NAME).invoke(test);
}
}