クラスパスに動的にJARファイルを追加(Java9以降)
【PR】当サイトはプロモーションが含まれています。
Java9 からはクラス ローダーの階層が変更されており、システムクラスローダー( アプリケーションクラスローダー) はクラス jdk.internal.loader.ClassLoaders$AppClassLoader となりました。この内部クラスはURLClassLoaderを継承ないため、メソッド URLClassLoader#addURL を利用できません。
クラス jdk.internal.loader.ClassLoaders$AppClassLoader のメソッド appendToClassPathForInstrumentation を使用します。
public static void addClassPath(ClassLoader classLoader, File file) throws Exception {
Method method = classLoader.getClass().getDeclaredMethod("appendToClassPathForInstrumentation", String.class);
method.setAccessible(true);
method.invoke(classLoader, file.getPath());
}
JDK内部APIへのアクセスのため、このコードだけでは次の様な例外となります。
java.lang.reflect.InaccessibleObjectException: Unable to make void jdk.internal.loader.ClassLoaders$AppClassLoader.appendToClassPathForInstrumentation(java.lang.String) accessible: module java.base does not "opens jdk.internal.loader" to unnamed module @239963d8 at java.base/java.lang.reflect.AccessibleObject.throwInaccessibleObjectException(AccessibleObject.java:388) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:364) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:312) at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:203) at java.base/java.lang.reflect.Method.setAccessible(Method.java:197)
リフレクションを使用して内部APIにアクセスする場合は、 –add-opens オプションを使用します。無名モジュールの場合は次の様な記述となります。
--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED
Java8 及びそれ以降のバージョンに対応するコードは次の様になります。
/**
* Add the classpath to the parameter ClassLoader.
*
* @param classLoader Collection<File>
* @param ufileCollection Collection<File>
* @return ClassLoader
*/
public static ClassLoader addClassPath(ClassLoader classLoader, Collection<File> fileCollection) throws Exception {
if (getJavaMajorVersion() >= 9) {
// Java9 or later
Method method = classLoader.getClass().getDeclaredMethod("appendToClassPathForInstrumentation", String.class);
method.setAccessible(true);
for (File file : fileCollection) {
method.invoke(classLoader, file.getPath());
}
return classLoader;
} else {
// Java8
URLClassLoader urlClassLoader = null;
if (classLoader instanceof URLClassLoader) {
urlClassLoader = (URLClassLoader)classLoader;
} else {
URL[] urlsDummy = {};
urlClassLoader = URLClassLoader.newInstance(urlsDummy, classLoader);
}
Method method = URLClassLoader.class.getDeclaredMethod("addURL", new Class[] { URL.class });
method.setAccessible(true);
for (File file : fileCollection) {
URL url = file.toURI().toURL();
method.invoke(urlClassLoader, new Object[] { url });
}
return urlClassLoader;
}
}
github: ext/base/runtime/BcRuntimeUtil.java
Java8については次の記事を参照




