You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java 17反射调用外部Jar报错:无法访问URLClassLoader.addURL

问题

我有一段用于加载外部Jar文件中定义方法的代码,在基于Spring Boot 2.7.10的Web应用里可在运行时调用这些外部方法。该代码在Java 11下运行正常,但升级到Java 17后出现如下错误:

Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected void java.net.URLClassLoader.addURL(java.net.URL) accessible: module java.base does not "opens java.net" to unnamed module @221af3c0

代码实现如下:

private static void addToClassPath(String jarFilePath) {
    try {
        File file = new File(jarFilePath);
        if (!(file.exists() && file.canRead())) {
            throw new UdfDirectoryNotAccessibleException(jarFilePath);
        }
        URL url = file.toURI().toURL();
        URLClassLoader urlClassLoader = (URLClassLoader) UdfInvoker.class.getClassLoader();
        Class urlClass = URLClassLoader.class;
        Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});

        // error here since java 17
        method.setAccessible(true);
        method.invoke(urlClassLoader, new Object[] { url });
    } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException ex) {
        throw new UdfFailedLoadingException(jarFilePath, ex.getMessage(), ex);
    } catch (MalformedURLException ex) {
        throw new UdfJarFileNotAccessibleException(jarFilePath, ex.getMessage(), ex);
    }
}

要求修改代码兼容Java 17,或提供同时兼容Java 11和17的替代方案,且:

  • 无法修改Tomcat容器或开发机器JRE的JDK参数;
  • 不能降级到Java 11版本。

解决方案

1. 自定义URLClassLoader(兼容Java 11/17)

Java 17强化了模块系统的访问控制,URLClassLoader的addURL方法无法再通过反射突破限制。可以自定义类加载器继承URLClassLoader,直接暴露addURL方法:

public class CustomJarClassLoader extends URLClassLoader {
    public CustomJarClassLoader(URL[] urls, ClassLoader parent) {
        super(urls, parent);
    }

    @Override
    public void addURL(URL url) {
        super.addURL(url);
    }
}

修改原加载逻辑,用自定义类加载器加载外部Jar:

private static ClassLoader addToClassPath(String jarFilePath) {
    try {
        File file = new File(jarFilePath);
        if (!(file.exists() && file.canRead())) {
            throw new UdfDirectoryNotAccessibleException(jarFilePath);
        }
        URL url = file.toURI().toURL();
        // 以当前类加载器为父加载器,创建自定义类加载器
        CustomJarClassLoader classLoader = new CustomJarClassLoader(new URL[]{url}, UdfInvoker.class.getClassLoader());
        return classLoader;
    } catch (MalformedURLException ex) {
        throw new UdfJarFileNotAccessibleException(jarFilePath, ex.getMessage(), ex);
    }
}

后续调用外部Jar方法时,需使用该自定义类加载器加载目标类:

ClassLoader customLoader = addToClassPath("path/to/your.jar");
Class<?> udfClass = customLoader.loadClass("com.example.YourUdfClass");
Object udfInstance = udfClass.getDeclaredConstructor().newInstance();
Method method = udfClass.getDeclaredMethod("yourTargetMethod");
method.invoke(udfInstance);

2. 利用Spring工具类简化加载(适配Spring Boot场景)

作为Spring Boot应用,可借助PathMatchingResourcePatternResolver快速定位外部Jar资源,创建独立类加载器:

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.ClassUtils;

private static ClassLoader loadExternalJar(String jarFilePath) {
    try {
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        Resource jarResource = resolver.getResource("file:" + jarFilePath);
        ClassLoader parentClassLoader = ClassUtils.getDefaultClassLoader();
        return new URLClassLoader(new URL[]{jarResource.getURL()}, parentClassLoader);
    } catch (IOException ex) {
        throw new UdfJarFileNotAccessibleException(jarFilePath, ex.getMessage(), ex);
    }
}

3. 核心思路说明

两种方案本质都是不修改原有系统类加载器,而是创建独立的类加载器隔离加载外部Jar,既绕过了Java 17的模块访问限制,还能避免外部Jar与应用内部类的冲突问题。

内容的提问来源于stack exchange,提问作者Bằng Rikimaru

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 00:52:35