Java 9,与ClassLoader.getSystemClassLoader的兼容性问题
下面的代码将jar文件添加到构建路径中,它在Java8中运行良好。但是,它在Java9中抛出异常,该异常与转换到URLClassLoader有关。有没有办法解决这个问题?一个最佳的解决方案将对其进行编辑,使其与Java8和Java9一起工作Java 9,与ClassLoader.getSystemClassLoader的兼容性问题,java,classloader,java-9,urlclassloader,Java,Classloader,Java 9,Urlclassloader,下面的代码将jar文件添加到构建路径中,它在Java8中运行良好。但是,它在Java9中抛出异常,该异常与转换到URLClassLoader有关。有没有办法解决这个问题?一个最佳的解决方案将对其进行编辑,使其与Java8和Java9一起工作 private static int AddtoBuildPath(File f) { try { URI u = f.toURI(); URLClassLoader urlClassLoader = (URLClas
private static int AddtoBuildPath(File f) {
try {
URI u = f.toURI();
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
Class<URLClassLoader> urlClass = URLClassLoader.class;
Method method = urlClass.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(urlClassLoader, u.toURL());
} catch (NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException | MalformedURLException | IllegalAccessException ex) {
return 1;
}
return 0;
}
private static int AddtoBuildPath(文件f){
试一试{
uriu=f.toURI();
URLClassLoader URLClassLoader=(URLClassLoader)ClassLoader.getSystemClassLoader();
类urlClass=URLClassLoader.Class;
方法Method=urlClass.getDeclaredMethod(“addURL”,URL.class);
方法setAccessible(true);
调用(urlClassLoader,u.toURL());
}catch(NoSuchMethodException | SecurityException | IllegalArgumentException | InvocationTargetException | MalformedURLException | IllegalAccessException ex){
返回1;
}
返回0;
}
您遇到了这样一个事实。正如ClassLoader::getSystemClassLoader
的返回类型所示,这是一个实现细节,尽管有大量代码依赖它
根据注释判断,您正在寻找一种在运行时动态加载类的方法。在Java9中,这不能通过附加到类路径来完成
<>你应该考虑为此创建一个新的类加载器。这还有一个额外的优点,即您可以去掉新类,因为它们没有加载到应用程序类装入器中。如果您是针对Java 9进行编译,您应该仔细阅读-它们为您提供了加载全新模块图的清晰抽象。指向。答案是正确的:
Class.forName("nameofclass", true, new URLClassLoader(urlarrayofextrajarsordirs));
这里提到的注意事项也很重要:
注意事项:
java.util.ServiceLoader使用线程的ClassLoader上下文thread.currentThread().setContextClassLoader(specialloader)
DriverManager并不尊重调用类的“ClassLoader”,而不是线程的ClassLoader。直接使用Class.forName(“drivername”,true,新的URLClassLoader(urlarrayofextrajarsordirs).newInstance()创建驱动程序
激活使用线程的类加载器上下文(对javax.mail很重要)
不久前,我在这个问题上遇到了一些问题,很多人都使用了与问题中类似的方法
private static int AddtoBuildPath(File f)
在运行时将路径动态添加到类路径。问题中的代码在多个方面可能是错误的样式:1)假设ClassLoader.getSystemClassLoader()
返回一个URLClassLoader
是一个未记录的实现细节,2)使用反射将addURL
公开可能是另一个
动态添加类路径的更简洁的方法
如果您需要通过“class.forName”使用额外的类路径URL进行类加载,那么一个干净、优雅且兼容(Java 8到10)的解决方案如下:
object ClassloaderHelper {
def getURLs(classloader: ClassLoader) = {
// jdk9+ need to use reflection
val clazz = classloader.getClass
val field = clazz.getDeclaredField("ucp")
field.setAccessible(true)
val value = field.get(classloader)
value.asInstanceOf[URLClassPath].getURLs
}
}
val classpath =
(
// jdk8
// ClassLoader.getSystemClassLoader.asInstanceOf[URLClassLoader].getURLs ++
// getClass.getClassLoader.asInstanceOf[URLClassLoader].getURLs
// jdk9+
ClassloaderHelper.getURLs(ClassLoader.getSystemClassLoader) ++
ClassloaderHelper.getURLs(getClass.getClassLoader)
)
1) 通过扩展URL类加载器编写自己的类加载器,使用公共addURL
方法
public class MyClassloader extends URLClassLoader {
public MyClassloader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public void addURL(URL url) {
super.addURL(url);
}
}
2) 声明类加载器的(单例/应用程序范围)对象
private final MyClassloader classLoader;
并通过
classLoader = new MyClassloader(new URL[0], this.getClass().getClassLoader());
注意:系统类加载器是父类。通过classLoader
加载的类知道哪些类可以通过this.getClass().getClassLoader()
加载,但不能反过来加载
3) 在需要时添加其他类路径(动态):
4) 通过singleton类加载器实例化对象或应用程序
cls = Class.forName(name, true, classLoader);
注意:由于类加载器尝试在加载类之前委托给父类加载器(以及将父类委托给其父类),您必须确保要加载的类对父类加载器不可见,以确保它是通过给定的类加载器加载的。为了更清楚地说明这一点:如果您在系统类路径上有ClassPathB
,然后将ClassPathB
和一些ClassPathA
添加到您的自定义类加载器中,t如果ClassPathB
下的类将通过系统类加载器加载,而ClassPathA
下的类对它们来说是未知的。但是,如果从系统类路径中删除ClassPathB
,则这些类将通过自定义classloader
加载,然后用户将知道ClassPathA下的类B类的
5)您可以考虑通过< /P>将类加载程序传递给线程。
setContextClassLoader(classLoader)
如果线程使用getContextClassLoader
,如果您只是想读取当前类路径,例如,因为您想使用与当前类路径相同的类路径启动另一个JVM,则可以执行以下操作:
object ClassloaderHelper {
def getURLs(classloader: ClassLoader) = {
// jdk9+ need to use reflection
val clazz = classloader.getClass
val field = clazz.getDeclaredField("ucp")
field.setAccessible(true)
val value = field.get(classloader)
value.asInstanceOf[URLClassPath].getURLs
}
}
val classpath =
(
// jdk8
// ClassLoader.getSystemClassLoader.asInstanceOf[URLClassLoader].getURLs ++
// getClass.getClassLoader.asInstanceOf[URLClassLoader].getURLs
// jdk9+
ClassloaderHelper.getURLs(ClassLoader.getSystemClassLoader) ++
ClassloaderHelper.getURLs(getClass.getClassLoader)
)
默认情况下,$AppClassLoader类中的最终字段不能通过反射访问,需要向JVM传递一个额外的标志:
--add-opens java.base/jdk.internal.loader=ALL-UNNAMED
我找到了这个,为我工作
字符串pathSeparator=Syste.getProperty(“path.separator”);
字符串[]classPathEntries=System.getProperty(“java.class.path”).split(路径分隔符)
从网站上我得到了一个运行在Java 8中的spring boot应用程序。我的任务是将它升级到Java 11版本
面临的问题:
import java.net.URL;
/**
* This class has been created to make the code compatible after migration to Java 11
* From the JDK 9 release notes: "The application class loader is no longer an instance of
* java.net.URLClassLoader (an implementation detail that was never specified in previous releases).
* Code that assumes that ClassLoader.getSytemClassLoader() returns a URLClassLoader object will
* need to be updated. Note that Java SE and the JDK do not provide an API for applications or
* libraries to dynamically augment the class path at run-time."
*/
public class ClassLoaderConfig {
private final MockClassLoader classLoader;
ClassLoaderConfig() {
this.classLoader = new MockClassLoader(new URL[0], this.getClass().getClassLoader());
}
public MockClassLoader getClassLoader() {
return this.classLoader;
}
}
import java.net.URL;
import java.net.URLClassLoader;
public class MockClassLoader extends URLClassLoader {
public MockClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public void addURL(URL url) {
super.addURL(url);
}
}
原因:java.lang.ClassCastException:jdk.internal.loader.ClassLoaders$AppClassLoader(在模块:java.base中)无法转换为java.net.URLClassLoader(在模块:java.base中)
使用的方法:
import java.net.URL;
/**
* This class has been created to make the code compatible after migration to Java 11
* From the JDK 9 release notes: "The application class loader is no longer an instance of
* java.net.URLClassLoader (an implementation detail that was never specified in previous releases).
* Code that assumes that ClassLoader.getSytemClassLoader() returns a URLClassLoader object will
* need to be updated. Note that Java SE and the JDK do not provide an API for applications or
* libraries to dynamically augment the class path at run-time."
*/
public class ClassLoaderConfig {
private final MockClassLoader classLoader;
ClassLoaderConfig() {
this.classLoader = new MockClassLoader(new URL[0], this.getClass().getClassLoader());
}
public MockClassLoader getClassLoader() {
return this.classLoader;
}
}
import java.net.URL;
import java.net.URLClassLoader;
public class MockClassLoader extends URLClassLoader {
public MockClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public void addURL(URL url) {
super.addURL(url);
}
}
创建一个类:
import java.net.URL;
/**
* This class has been created to make the code compatible after migration to Java 11
* From the JDK 9 release notes: "The application class loader is no longer an instance of
* java.net.URLClassLoader (an implementation detail that was never specified in previous releases).
* Code that assumes that ClassLoader.getSytemClassLoader() returns a URLClassLoader object will
* need to be updated. Note that Java SE and the JDK do not provide an API for applications or
* libraries to dynamically augment the class path at run-time."
*/
public class ClassLoaderConfig {
private final MockClassLoader classLoader;
ClassLoaderConfig() {
this.classLoader = new MockClassLoader(new URL[0], this.getClass().getClassLoader());
}
public MockClassLoader getClassLoader() {
return this.classLoader;
}
}
import java.net.URL;
import java.net.URLClassLoader;
public class MockClassLoader extends URLClassLoader {
public MockClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public void addURL(URL url) {
super.addURL(url);
}
}
创建另一个类:
import java.net.URL;
/**
* This class has been created to make the code compatible after migration to Java 11
* From the JDK 9 release notes: "The application class loader is no longer an instance of
* java.net.URLClassLoader (an implementation detail that was never specified in previous releases).
* Code that assumes that ClassLoader.getSytemClassLoader() returns a URLClassLoader object will
* need to be updated. Note that Java SE and the JDK do not provide an API for applications or
* libraries to dynamically augment the class path at run-time."
*/
public class ClassLoaderConfig {
private final MockClassLoader classLoader;
ClassLoaderConfig() {
this.classLoader = new MockClassLoader(new URL[0], this.getClass().getClassLoader());
}
public MockClassLoader getClassLoader() {
return this.classLoader;
}
}
import java.net.URL;
import java.net.URLClassLoader;
public class MockClassLoader extends URLClassLoader {
public MockClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public void addURL(URL url) {
super.addURL(url);
}
}
现在在主类的当前线程中设置它(就在应用程序的开头)
希望此解决方案适用于您的!!!参考Edi的解决方案,此解决方案适用于我:
public final class IndependentClassLoader extends URLClassLoader {
private static final ClassLoader INSTANCE = new IndependentClassLoader();
/**
* @return instance
*/
public static ClassLoader getInstance() {
return INSTANCE;
}
private IndependentClassLoader() {
super(getAppClassLoaderUrls(), null);
}
private static URL[] getAppClassLoaderUrls() {
return getURLs(IndependentClassLoader.class.getClassLoader());
}
private static URL[] getURLs(ClassLoader classLoader) {
Class<?> clazz = classLoader.getClass();
try {
Field field = null;
field = clazz.getDeclaredField("ucp");
field.setAccessible(true);
Object urlClassPath = field.get(classLoader);
Method method = urlClassPath.getClass().getDeclaredMethod("getURLs", new Class[] {});
method.setAccessible(true);
URL[] urls = (URL[]) method.invoke(urlClassPath, new Object[] {});
return urls;
} catch (Exception e) {
throw new NestableRuntimeException(e);
}
}
}
公共最终类独立类加载器扩展URLClassLoader{
私有静态最终类加载器实例=新独立类加载器();
/**
*@return实例
*/
公共静态类装入