Java 17下如何让自定义类加载器接管后续类加载?
问题原因
你遇到的问题核心是Java类加载的双亲委派模型:当你用自定义类加载器加载com.mycompany.app.Test时,自定义加载器会先委托父加载器(即加载App的系统类加载器)尝试加载Test类。如果Test类存在于系统类路径中,父加载器会成功加载它,此时Test类的定义类加载器是系统类加载器,而非你创建的自定义加载器。当Test类中实例化Test2时,JVM会使用加载Test的类加载器(系统类加载器)去查找Test2,而Test2不在系统类路径中,因此抛出NoClassDefFoundError。
解决方案(不修改Test类,仅修改main方法)
你需要自定义类加载器,打破双亲委派模型中对Test类的委托逻辑,让自定义加载器优先加载Test类。这样Test的定义类加载器就是你的自定义加载器,后续加载Test2时会使用这个能找到Test2的加载器。
具体修改步骤
- 在
App类中定义一个继承URLClassLoader的自定义类加载器,重写loadClass方法,对Test类跳过双亲委派、优先自行加载:
static class CustomClassLoader extends URLClassLoader { public CustomClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); } @Override protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // 仅对Test类优先自行加载,其他类遵循双亲委派 if ("com.mycompany.app.Test".equals(name)) { synchronized (getClassLoadingLock(name)) { Class<?> loadedClass = findLoadedClass(name); if (loadedClass == null) { loadedClass = findClass(name); } if (resolve) { resolveClass(loadedClass); } return loadedClass; } } return super.loadClass(name, resolve); } }
- 在main方法中,用这个
CustomClassLoader替换原来的URLClassLoader:
// 替换原ClassLoader创建代码 ClassLoader classLoader = new CustomClassLoader(classPaths, App.class.getClassLoader());
修改后,Test类会被自定义加载器加载,当Test中实例化Test2时,JVM会使用该自定义加载器从指定jar中查找并加载Test2,test.ex()即可正常执行。
备选方案(需调整类路径)
如果允许将Test类从系统类路径中移除,放到自定义加载器指向的jar包或路径中,原有的URLClassLoader会因为父加载器找不到Test类,自行加载Test,同样能解决问题。但该方案需要调整类文件存放位置,若你要求仅修改main方法,优先选择自定义类加载器方案。
内容的提问来源于stack exchange,提问作者Runekn
相关产品推荐
相关产品推荐

