URLClassLoader.getURLS()
Tomcat は、クラス ローダー階層 (ボトムアップ)を繰り返し呼び出して、スキャンする jar URL を取得します。
JavaURLClassLoader.getURLS()
アプリケーションがjava -jar <executable-jar>
参照:クラスローダはマニフェスト クラスパスでクラス参照をどのようにロードしますか?
前回の投稿では、リフレクションを使用してシステム クラスローダー インスタンスのプライベート フィールドにアクセスすることを提案しましたが、これにはいくつかの問題があります。
- この加入は、セキュリティ管理者によって禁止される可能性があります
- ソリューションは実装に依存します
だから私は別の方法を考え出した:
- 特定のクラスローダーについて、利用可能なすべてのマニフェストを列挙します
cl.getResources("META-INF/MANIFEST.MF")
。これらのマニフェストは、現在のクラス ローダーまたはその上位クラス ローダーによって管理される jar の場合があります。
- 親クラスローダについても同じことを行います
- (2) ではなく (1) のマニフェストの jar のセットを返します。
このメソッドが機能するための唯一の要件は、クラスパス内の jar が返されるためにマニフェストを持っている必要があることです (あまり質問する必要はありません)。
/**
* Returns the search path of URLs for loading classes and resources for the
* specified class loader, including those referenced in the
* {@code Class-path} header of the manifest of a executable jar, in the
* case of class loader being the system class loader.
* <p>
* Note: These last jars are not returned by
* {@link java.net.URLClassLoader#getURLs()}.
* </p>
* @param cl
* @return
*/
public static URL[] getURLs(URLClassLoader cl) {
if (cl.getParent() == null || !(cl.getParent()
instanceof URLClassLoader)) {
return cl.getURLs();
}
Set<URL> urlSet = new LinkedHashSet();
URL[] urLs = cl.getURLs();
URL[] urlsFromManifest = getJarUrlsFromManifests(cl);
URLClassLoader parentCl = (URLClassLoader) cl.getParent();
URL[] ancestorUrls = getJarUrlsFromManifests(parentCl);
for (int i = 0; i < urlsFromManifest.length; i++) {
urlSet.add(urlsFromManifest[i]);
}
for (int i = 0; i < ancestorUrls.length; i++) {
urlSet.remove(ancestorUrls[i]);
}
for (int i = 0; i < urLs.length; i++) {
urlSet.add(urLs[i]);
}
return urlSet.toArray(new URL[urlSet.size()]);
}
/**
* Returns the URLs of those jar managed by this classloader (or its
* ascendant classloaders) that have a manifest
* @param cl
* @return
*/
private static URL[] getJarUrlsFromManifests(ClassLoader cl) {
try {
Set<URL> urlSet = new LinkedHashSet();
Enumeration<URL> manifestUrls =
cl.getResources("META-INF/MANIFEST.MF");
while (manifestUrls.hasMoreElements()) {
try {
URL manifestUrl = manifestUrls.nextElement();
if(manifestUrl.getProtocol().equals("jar")) {
urlSet.add(new URL(manifestUrl.getFile().substring(0,
manifestUrl.getFile().lastIndexOf("!"))));
}
} catch (MalformedURLException ex) {
throw new AssertionError();
}
}
return urlSet.toArray(new URL[urlSet.size()]);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
Tomcat に登録された問題: https://bz.apache.org/bugzilla/show_bug.cgi?id=59226