打破双亲委派机制实现热更新(同时使用自定义类加载器对class文件加密)

这里通过自定义加载分别实现了对class文件加密和打破双亲委派机制的热更新(用于练习和学习)

代码结构

源代码

package com.myspringboot.jvm.loader;


import java.io.*;

/**
* 带加密功能的自定义加载器(打破双亲委派机制实现热加载
*/
public class MyClassLoaderWithEncription extends ClassLoader {

private String pathName;

public MyClassLoaderWithEncription(String pathName) {
this.pathName = pathName;
}

@Override
protected Class<?> findClass(String className) throws ClassNotFoundException {
try (FileInputStream fis = new FileInputStream(new File(pathName));
ByteArrayOutputStream baos = new ByteArrayOutputStream();) {
byte[] buffer = new byte[1024];
int size = 0;

while ((size = fis.read(buffer)) != -1) {
baos.write(getNewBuffer(buffer), 0, size);
}
baos.flush();
byte[] bytes = baos.toByteArray();

return defineClass(className, bytes, 0, bytes.length);
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(className); //throws ClassNotFoundException
}

public static void main(String[] args) throws Exception {
loadsWithEnc();
reloads();
}

private static void loadsWithEnc() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//加密后的.classs文件的输出位置
MyClassLoaderWithEncription myClassLoader = new MyClassLoaderWithEncription("C:\\Users\\Administrator\\Desktop\\User.classs");
//编译后的.class文件的存放位置
myClassLoader.encFile("C:\\Users\\Administrator\\Desktop\\User.class");
Class clazz = myClassLoader.findClass("com.myspringboot.jvm.loader.User");
MyClassLoaderWithEncription myClassLoader1 = new MyClassLoaderWithEncription("C:\\Users\\Administrator\\Desktop\\User.classs");
Class clazz1 = myClassLoader1.findClass("com.myspringboot.jvm.loader.User");
System.out.println(clazz);
System.out.println(clazz == clazz1);

System.out.println(myClassLoader.getClass().getClassLoader());
System.out.println(myClassLoader.getParent());
System.out.println(clazz.getClassLoader());
System.out.println(clazz.newInstance().getClass().getClassLoader());
}

//将加密后的数据写到.classs文件中
private void encFile(String name) {
try (FileInputStream fis = new FileInputStream(new File(name));
FileOutputStream fos = new FileOutputStream(new File(pathName));) {
byte[] buffer = new byte[1024];
int size = 0;

while ((size = fis.read(buffer)) != -1) {
fos.write(getNewBuffer(buffer), 0, size);
}
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

//加密算法,其实就是对每个数取反了一下
private byte[] getNewBuffer(byte[] buffer) {
for (int i = 0; i < buffer.length; i++) {
buffer[i] = (byte) ~buffer[i];
}
return buffer;
}

// 可以将下面两端代码分别放开和注释掉,会得到不同的结果
private static void reloads() throws IllegalAccessException, InstantiationException, ClassNotFoundException {
for (int i = 0; i < 100; i++) {
MyClassLoaderWithEncription myClassLoader = new MyClassLoaderWithEncription("C:\\Users\\Administrator\\Desktop\\User.classs");
myClassLoader.encFile("C:\\Users\\Administrator\\Desktop\\User.class");

//调用loadClass方法,遵循双亲委派,实际为复用自己的父加载器就是AppClassLoader
Class clazz = myClassLoader.loadClass("com.myspringboot.jvm.loader.User");
System.out.println(clazz.newInstance().getClass().getClassLoader());

//调用findClass方法,打破双亲委派,实现热更新,加载器为MyClassLoaderWithEncription
//Class clazz = myClassLoader.findClass("com.myspringboot.jvm.loader.User");
//clazz.newInstance().getClass().getClassLoader();
}
}
}