クラスパスに動的に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については次の記事を参照