2

jar複数の外部jar依存関係を持つ実行可能ファイル (シン) としてパッケージ化された埋め込み Tomcat アプリケーションがあります。

ビルド プロセスは、META-INF/MANIFEST.MFヘッダー フィールドMain-ClassClass-Path(ランタイム依存関係ごとのエントリを含む) を生成します。

シンプルな を使用してアプリケーションを実行したいのですが、Tomcat にこれらの依存 jar をスキャンさせることができませんjava -jar my_app.jar( TLD やSpring などのクラスを検出するため)。@HandlesTypesWebApplicationInitializer

この方法でjarスキャンを構成しています:

StandardJarScanner jarScanner = (StandardJarScanner) ctx.getJarScanner();
jarScanner.setScanBootstrapClassPath(true);
jarScanner.setScanClassPath(true);

すべての jar にはMETA-INFフォルダーがありますが、スキャナーはそれらを完全に無視します。

何か案は?

注: さまざまなアプローチ (fat jar、maven からの実行など) を使用してこれを機能させることができますが、他の Java アプリケーションと同様に、この方法で機能させることに興味があります。

4

1 に答える 1

3

URLClassLoader.getURLS()Tomcat は、クラス ローダー階層 (ボトムアップ)を繰り返し呼び出して、スキャンする jar URL を取得します。

JavaURLClassLoader.getURLS()アプリケーションがjava -jar <executable-jar>

参照:クラスローダはマニフェスト クラスパスでクラス参照をどのようにロードしますか?

前回の投稿では、リフレクションを使用してシステム クラスローダー インスタンスのプライベート フィールドにアクセスすることを提案しましたが、これにはいくつかの問題があります。

  • この加入は、セキュリティ管理者によって禁止される可能性があります
  • ソリューションは実装に依存します

だから私は別の方法を考え出した:

  1. 特定のクラスローダーについて、利用可能なすべてのマニフェストを列挙しますcl.getResources("META-INF/MANIFEST.MF")。これらのマニフェストは、現在のクラス ローダーまたはその上位クラス ローダーによって管理される jar の場合があります。
  2. 親クラスローダについても同じことを行います
  3. (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

于 2016-03-23T17:57:27.520 に答える