求助Java 9用户:运行时动态加载Jar包的替代方案
Hey there, I’ve run into exactly this problem when upgrading our legacy Java app to Java 11—super annoying that the old URLClassLoader trick broke overnight! Here are three practical alternatives that have worked for me:
1. 自定义URLClassLoader(最简单且稳定)
Even though the system class loader isn’t a URLClassLoader anymore, the URLClassLoader class itself still exists in Java 9+. You can just create your own instance to handle dynamic Jar loading, which keeps things straightforward and avoids messing with internal JDK APIs.
Example code:
import java.net.URL; import java.net.URLClassLoader; public class DynamicJarLoader { public static void main(String[] args) throws Exception { // 替换成你的Jar文件路径 URL jarUrl = new URL("file:/Users/you/path/to/your.jar"); URL[] jarUrls = {jarUrl}; // 以系统类加载器为父加载器,遵循类加载委托机制 URLClassLoader customLoader = new URLClassLoader(jarUrls, ClassLoader.getSystemClassLoader()); // 加载目标类并使用 Class<?> targetClass = customLoader.loadClass("com.yourpackage.YourClass"); Object instance = targetClass.getDeclaredConstructor().newInstance(); // 调用类方法... } }
Note: If your app uses modules (with module-info.java), make sure the classes in your dynamic Jar are either exported (if it’s a named module) or treat it as an automatic module so your main app can access the classes.
2. 反射访问系统类加载器的内部URLClassPath(临时过渡方案)
If you need to keep adding Jars to the system class loader (instead of using a custom one), you can still do this via reflection—though it’s dependent on JDK internal implementation, so use it only as a short-term fix.
In Java 9+, the system class loader is jdk.internal.loader.ClassLoaders$AppClassLoader, which has an internal ucp field (of type URLClassPath) that handles classpath URLs. You can reflectively access this field and call its addURL method:
Example code:
import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.URL; public class SystemClassLoaderJarAdder { public static void addJarToSystemClasspath(URL jarUrl) throws Exception { ClassLoader systemLoader = ClassLoader.getSystemClassLoader(); // 获取内部的ucp字段 Field ucpField = systemLoader.getClass().getDeclaredField("ucp"); ucpField.setAccessible(true); Object urlClassPath = ucpField.get(systemLoader); // 调用addURL方法 Method addUrlMethod = urlClassPath.getClass().getDeclaredMethod("addURL", URL.class); addUrlMethod.setAccessible(true); addUrlMethod.invoke(urlClassPath, jarUrl); } }
Warning: This will break if Oracle changes the internal structure of the system class loader. For Java 16+, you’ll need to add JVM arguments to allow reflection on internal classes:
--add-opens jdk.base/jdk.internal.loader=ALL-UNNAMED
3. 使用Module Layer(适用于模块化应用)
If you’ve fully migrated to Java modules, using Module Layers is the cleanest, most future-proof way to dynamically load modules/Jars. It aligns with Java’s modular design and avoids any hacky reflection.
Here’s a quick example:
import java.io.File; import java.lang.module.ModuleFinder; import java.lang.module.ModuleLayer; import java.lang.module.Configuration; import java.util.Set; public class ModuleLayerLoader { public static void main(String[] args) throws Exception { // 指定要加载的Jar文件路径 File jarFile = new File("/path/to/your-module.jar"); ModuleFinder moduleFinder = ModuleFinder.of(jarFile.toPath()); // 基于启动模块层创建新的配置 ModuleLayer parentLayer = ModuleLayer.boot(); Configuration config = parentLayer.configuration() .resolve(moduleFinder, ModuleFinder.of(), Set.of("your.module.name")); // 创建新的模块层,使用系统类加载器作为类加载器 ModuleLayer newLayer = parentLayer.defineModulesWithOneLoader(config, ClassLoader.getSystemClassLoader()); // 加载并使用模块中的类 Class<?> targetClass = newLayer.findLoader("your.module.name") .loadClass("com.yourpackage.YourClass"); Object instance = targetClass.getDeclaredConstructor().newInstance(); } }
Bonus: If your Jar isn’t a named module (no module-info.java), it’ll be treated as an automatic module, and you can reference it by its Jar filename (without the .jar suffix).
内容的提问来源于stack exchange,提问作者GlobCoder




