Java自定义类加载器踩坑记录

I'm Shendi

这几天要实现热更,需要自定义类加载器

也就是让一个类继承 ClassLoader

class SKClassLoader extends ClassLoader {

}

然后可以使用ClassLoader的一些 protected 修饰的方法

比如我最常用的 defineClass

  • defineClass 可以将字节转换成类

在一开始,我直接使用的defineClass来实现热更,因为一个类加载器对象只能加载一次类,所以每次获取同样的类的时候需要重新new一下对象

代码如下,使用到了自己写的一些工具

class SKClassLoader extends ClassLoader {
    
    public static Class<?> reloadClass(String clazz) throws ClassNotFoundException {
        if (clazz == null) return null;
		else {
			sk = new SKClassLoader();
			clazz = "/".concat(clazz.replace('.', '/').concat(ShendiKitInfo.CLASS_SUFFIX));
			byte[] classData = null;
			try (InputStream input = SKClassLoader.class.getResourceAsStream(clazz)) {
				classData = new byte[input.available()];
				input.read(classData, 0, classData.length);
			} catch (IOException e) {
				Log.printErr("重新加载指定类获取出错: " + clazz + "---" + e.getMessage());
				return null;
			}
			return sk.defineClass(null, classData, 0, classData.length);
		}
    }

}

大概内容就是读取对应class文件成字节流,然后使用defineClass将字节流变为类

这个时候已经可以进行热更

于是我将项目push到了github

然后我写web项目的时候使用到这个类加载器就发现问题了

在普通java项目中可以进行热更,在web项目中不能进行热更

于是我想到了双亲加载机制,以为是父加载器将类加载了,所以子类重新获取的是父类加载的那个类

接着进行了一堆操作,测试了很多次(对ClassLoader使用还是不太熟练)

比如直接使用loadClass方法进行加载类,直接使用findClass加载类

期间错误一大把,解决了一个又出一个....

印象最深刻的是 ClassNotDefFoundException

...

后来通过查阅,在自定义类加载器中实现了 findClass 方法

代码如下

@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		Class<?> c = findLoadedClass(name);
		if (c == null) {
			sk = new SKClassLoader();
			name = "/".concat(name.replace('.', '/').concat(ShendiKitInfo.CLASS_SUFFIX));
			byte[] classData = null;
			try (InputStream input = SKClassLoader.class.getResourceAsStream(name)) {
				classData = new byte[input.available()];
				input.read(classData, 0, classData.length);
			} catch (IOException e) {
				Log.printErr("重新加载指定类获取出错: " + name + "---" + e.getMessage());
				return null;
			}
			return sk.defineClass(null, classData, 0, classData.length);
		}
		return c;
	}

具体内容就是先通过 findLoadedClass 先加载,如果为空则通过defineClass来获取

然后我就通过这个方法来加载类

接着测试

...

在普通Java项目中没有问题,在web项目中错误又出现了....

一个权限异常 Access什么的

后来继续查阅,找到了正确的使用方法

自定义类加载器要加载类则需要实现 findClass 方法,代码就如我上面那样

在使用的过程中使用的是 loadClass 方法

接着测试

代码没有问题,也没有错误

但是奇怪的问题又来了

在web项目中依然无法进行热更.

然后根据双亲加载机制,我输出了获取的类和直接xx.class的HashCode

然后发现两个是不一样的,对的 因为是两个类加载器加载的

于是乎做了一堆没用的操作后放弃了

...

然后发现自己对defineClass方法的理解有误,这个方法是直接通过字节定义类,不会被双亲机制什么的所影响,于是我将代码恢复到最初状态

不纠结web项目能不能热更了

收工

猜你喜欢

转载自blog.csdn.net/qq_41806966/article/details/108497339