jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.net.URLClassLoa

今天遇到了个奇怪的问题,部署到新环境后,动态加载类的功能报错。

Exception in thread "main" java.lang.RuntimeException: java.lang.ClassCastException: jdk.internal.loader.ClassLoaders$AppClassLoader (in module: java.base) cannot be cast to java.net.URLClassLoader (in module: java.base)

后来发现新环境的jdk的版本是9。于是查了下资料,得出结论:Java9之后,应用程序和扩展类都不再是 java.net.URLClassLoader 的实例。

于是修改了下加载动态类的代码,这是之前的代码

        File jarFile = new File(jarPath);//jarPath是jar路径
		// 从URLClassLoader类中获取类所在文件夹的方法,jar也可以认为是一个文件夹
		Method method = null;
		try {
			method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
		} catch (NoSuchMethodException | SecurityException e1) {
			e1.printStackTrace();
		}
		// 获取方法的访问权限以便写回
		boolean accessible = method.isAccessible();
		try {
			method.setAccessible(true);
			// 获取系统类加载器
			URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
			URL url = jarFile.toURI().toURL();
			method.invoke(classLoader, url);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			method.setAccessible(accessible);
		}

下面是修改后的代码

File jarFile = new File(jarPath);
ClassLoader classLoader = null;
URL url = null;
try {
	// 获取系统类加载器
	classLoader = ClassLoader.getSystemClassLoader();
	url = jarFile.toURI().toURL();
}catch (Exception e){
	e.printStackTrace();
}finally {
}

if (classLoader instanceof URLClassLoader) {
	System.out.println("DEB: classLoader instanceof URLClassLoader");
	URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
	Class sysclass = URLClassLoader.class;
	try {
		Method method = sysclass.getDeclaredMethod("addURL", URL.class);
		method.setAccessible(true);
		method.invoke(sysloader, url);
	} catch (Exception var5) {
		var5.printStackTrace();
		throw new IllegalStateException(var5.getMessage(), var5);
	}
} else {
	try {
		Field field = classLoader.getClass().getDeclaredField("ucp");
		field.setAccessible(true);
		Object ucp = field.get(classLoader);

		System.out.println("DEB: invoke method!");
		Method method = ucp.getClass().getDeclaredMethod("addURL", URL.class);
		method.setAccessible(true);

		method.invoke(ucp, url);
	} catch (Exception exception) {
		exception.printStackTrace();
		throw new IllegalStateException(exception.getMessage(), exception);
	}
}

改完之后发现请求还是会报错,报错信息如下

java.lang.reflect.InaccessibleObjectException: Unable to make field final jdk.internal.loader.URLClassPath jdk.internal.loader.ClassLoaders$AppClassLoader.ucp accessible: module java.base does not "opens jdk.internal.loader" to unnamed module @6b143ee9

改原因是 Java 9+ 以上版本中不允许使用动态加载类,因为想要使用就需要加两个参数给虚拟机,

重点线-----------------------上面是参考原作者的内容,下面的解决方式是自己实际情况,仅供参考

我使用的是tomcat启动方式,需要修改tomcat bin目录下的catalina.sh文件,加入这两行脚本

JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=java.base/jdk.internal.loader=ALL-UNNAMED"
JDK_JAVA_OPTIONS="$JDK_JAVA_OPTIONS --add-opens=jdk.zipfs/jdk.nio.zipfs=ALL-UNNAMED"

 ok,大功告成。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 这个错误是因为在Java程序中,尝试将一个加载器(classloader)转换为另一个加载器时出现了问题。具体来说,这个错误是因为尝试将一个名为“jdk.internal.loader.classloaders$appclassloader”的加载器转换为“java.net.URLClassLoader”的加载器时出现了问题。 这个错误通常是由于代码中的型转换错误引起的。要解决这个问题,您需要检查代码中的型转换,并确保它们是正确的。您还可以尝试使用不同的加载器来加载您的,以避免这个问题。 ### 回答2: 这个问题看起来是一个型转换的错误。出现这个错误可能的原因是,代码中尝试将一个 JDK 内部的加载器 `jdk.internal.loader.classloaders$appclassloader` 强制转换为标准的 `java.net.URLClassLoader` 型。这两个虽然都是 Java 加载器,但是它们之间有着一些区别和限制。 `jdk.internal.loader.classloaders$appclassloader` 型的加载器是 JDK 内部实现的,通常用于加载应用程序所需的基本和库。这个加载器会尽可能地从本地磁盘或者网络中的已知位置获取文件,以快速地完成加载请求。另一方面,`java.net.URLClassLoader` 型的加载器是 Java 标准库中提供的,它可以通过 URL 地址动态加载远程代码和库。它支持从标准的 `file://`、`http://` 等协议地址获取文件或者 jar 包文件,并将它们加载到 JVM 中。 由于这两个型的加载器有着不同的实现方式和加载机制,所以它们之间并不是互相兼容的。如果我们在代码中尝试将 `jdk.internal.loader.classloaders$appclassloader` 型的加载器强制转换为 `java.net.URLClassLoader` 型,就会触发一个 `ClassCastException` 异常。这是因为 `jdk.internal.loader.classloaders$appclassloader` 型的对象无法转化为 `java.net.URLClassLoader` 型,所以在转换时会抛出异常。 解决这个问题的方法,就是检查代码中的型转换操作是否正确。如果确实需要使用 `java.net.URLClassLoader` 型的加载器,就要使用正确的方式创建或者获取这个对象,而不是尝试强制转换一个不兼容的加载器。如果不需要使用 `java.net.URLClassLoader` 型的加载器,那么就应该避免通过强制转换的方式访问它们的方法和属性。 ### 回答3: 这个问题出现的原因是因为在代码中对加载器进行了强制型转换,但是由于使用了不同的加载器,这个型转换是不合法的。 具体来说,jdk.internal.loader.classloaders$appclassloaderJava 9及以上版本中的加载器,而java.net.URLClassLoaderJava 8及以下版本中的加载器。由于它们不是同一个加载器,因此将一个转换为另一个是不可行的。 解决这个问题的方法是使用 instanceof 运算符来进行型检查,然后再根据不同的加载器做出相应的操作。例如: ClassLoader classLoader = MyClass.class.getClassLoader(); if (classLoader instanceof java.net.URLClassLoader) { java.net.URLClassLoader urlClassLoader = (java.net.URLClassLoader) classLoader; // 对URLClassLoader进行操作 } else if (classLoader instanceof jdk.internal.loader.classloaders.AppClassLoader) { jdk.internal.loader.classloaders.AppClassLoader appClassLoader = (jdk.internal.loader.classloaders.AppClassLoader) classLoader; // 对AppClassLoader进行操作 } else { // 其他型的加载器 } 在这种情况下,我们可以根据实际情况用不同的方式处理不同的加载器。这样就可以避免出现型转换异常的错误。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值