JDK11+环境下如何实现无第三方依赖的运行时动态加载JAR方案
JDK11+ 运行时动态加载JAR的实现方案
错误根因说明
- JDK9及以上版本默认系统类加载器已经从
URLClassLoader改为jdk.internal.loader.ClassLoaders$AppClassLoader,不再继承URLClassLoader,原有基于反射调用addURL的方法原生失效 - 你配置的
java.system.class.loader参数不生效,是因为自定义类加载器放在应用classpath下被默认AppClassLoader加载,JVM初始化时最终的系统类加载器还是原生AppClassLoader,所以会抛出类转换异常 - Spring Boot可执行JAR启动时会使用自带的
LaunchedURLClassLoader加载应用类,会覆盖你自定义的系统类加载器配置
适配方案(无需第三方依赖、无需修改启动参数、无需单独加载单个类)
方案1:Spring Boot场景直接可用
Spring Boot运行时的上下文类加载器本身就是org.springframework.boot.loader.LaunchedURLClassLoader,继承自URLClassLoader,直接反射调用addURL方法即可:
import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Path; public void loadJar(Path path) throws Exception { URL jarUrl = path.toUri().toURL(); ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); Method addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); addUrlMethod.setAccessible(true); addUrlMethod.invoke(contextClassLoader, jarUrl); }
该方案无需修改任何JVM启动参数,兼容Spring Boot 2.x全版本和JDK8~JDK17主流版本,动态添加的JAR会被Spring自动扫描,无需手动加载单个类。
方案2:通用JDK11+场景实现(非Spring Boot也可用)
如果脱离Spring Boot环境使用,可以自定义类加载器替换当前线程上下文类加载器,不需要配置启动参数:
import java.net.URL; import java.net.URLClassLoader; import java.nio.file.Path; public final class DynamicClassLoader extends URLClassLoader { static { ClassLoader.registerAsParallelCapable(); } public DynamicClassLoader(ClassLoader parent) { super(new URL[0], parent); } @Override public void addURL(URL url) { super.addURL(url); } private static final DynamicClassLoader INSTANCE = new DynamicClassLoader(ClassLoader.getSystemClassLoader()); public static DynamicClassLoader getInstance() { return INSTANCE; } public static void loadJar(Path path) throws Exception { getInstance().addURL(path.toUri().toURL()); // 替换上下文类加载器,后续所有类加载逻辑自动识别新增JAR Thread.currentThread().setContextClassLoader(getInstance()); } }
内容的提问来源于stack exchange,提问作者skin27
相关产品推荐
相关产品推荐

