web-dev-qa-db-ja.com

春の起動時のパフォーマンスの問題

何千ものクラスを持つかなり大きなアプリケーションにSpringを統合しようとしています。コンポーネントのスキャンが原因で、コンテナの起動に大幅な遅延が発生しています。

「基本パッケージ」で指定されたディレクトリの数は、無関係なディレクトリのスキャンに費やされる時間を減らすために最小限に抑えていますが、初期化のクラスパススキャンの部分には、まだ約1〜2分かかります。

では、スキャンプロセスを最適化する方法はありますか?起動時にクラスパスをスキャンするのではなく、クラスパスの候補をファイルに保存し、コンテナを作成してファイルから取得することを考えましたが、どこから始めればよいか、それが可能かどうかさえわかりません。

アドバイスは大歓迎です。前もって感謝します。


Edit1:自動生成されたxmlファイルからBean定義をロードし、Springをbootstrap時間を9〜10秒に短縮して確認すると、 Springがコンポーネントのクラスパススキャンに使用するリフレクションAPIが、起動遅延の主な原因であることに注意してください。
xmlファイルの生成に関しては、同じ問題のある人に役立つ可能性があるため、ここにコードがあります。

import Java.io.File;
import Java.io.FileNotFoundException;
import Java.io.PrintWriter;
import Java.util.ArrayList;


public class ConfigurationWriter {

    public ArrayList<String> beanDefinitions = new ArrayList<String>();

    public ConfigurationWriter() {

        // the context loaded with old fashioned way (classpath scanning)
        ApplicationContext context = SpringContainerServiceImpl.getInstance().getContext();
        String[] tab = context.getBeanDefinitionNames();
        for (int i = 0; i < tab.length - 6; i++) {
            Class clazz = context.getType(tab[i]);
            String scope = context.isPrototype(tab[i]) ? "prototype" : "singleton";
            String s = "<bean id=\"" + tab[i] + "\" class=\"" + clazz.getName() + "\" scope=\"" + scope + "\"/>";
            beanDefinitions.add(s);
        }
        // Collections.addAll(beanDefinitions, tab);

    }

    @SuppressWarnings("restriction")
    public void generateConfiguration() throws FileNotFoundException {
        File xmlConfig = new File("D:\\dev\\svn\\...\\...\\src\\test\\resources\\springBoost.xml");
        PrintWriter printer = new PrintWriter(xmlConfig);

        generateHeader(printer);

        generateCorpse(printer);

        generateTail(printer);

        printer.checkError();

    }

    @SuppressWarnings("restriction")
    private void generateCorpse(PrintWriter printer) {

        for (String beanPath : beanDefinitions) {
            printer.println(beanPath);
        }

    }

    @SuppressWarnings("restriction")
    private void generateHeader(PrintWriter printer) {
        printer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        printer.println("<beans xmlns=\"http://www.springframework.org/schema/beans\"");
        printer.println("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
        printer.println("xmlns:context=\"http://www.springframework.org/schema/context\"");
        printer.println("xsi:schemaLocation=\"");
        printer.println("http://www.springframework.org/schema/mvc");
        printer.println("http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd");
        printer.println("http://www.springframework.org/schema/beans");
        printer.println("http://www.springframework.org/schema/beans/spring-beans-3.0.xsd");
        printer.println("http://www.springframework.org/schema/context");
        printer.println("http://www.springframework.org/schema/context/spring-context-3.0.xsd\"");
        printer.println("default-lazy-init=\"true\">");
    }

    @SuppressWarnings("restriction")
    private void generateTail(PrintWriter printer) {
        // printer.println("<bean class=\"com.xxx.frmwrk.spring.processors.xxxBeanFactoryPostProcessor\"/>");
        printer.println("<bean class=\"com.xxx.frmwrk.spring.processors.xxxPostProcessor\"/>");
        printer.println("</beans>");
    }

}

編集2:Spring 5には、コンテキストの初期化を高速化するための重要な最適化セットが含まれています。また、生成することができる興味深い便利な機能が付属していますコンパイル時の候補コンポーネントのインデックス: Spring Context Indexer

37
Mehdi

質問:ディレクトリ内のクラスのいくつ(%)がSpring Beanですか?

回答:私は本当にわかりません(非常に大きなプロジェクトです)。しかし、xmlファイルとプロパティファイルは別々の場所に分離されているため、90〜100%程度だと私は思います。

問題が実際にコンポーネントのスキャンであり、Beanの初期化プロセス自体ではない場合(そして私はそれを疑っています)、考えられる唯一の解決策は、コンポーネントのスキャンではなくSpring XML構成を使用することです。 -(XMLファイルを自動的に作成できます)。

しかし、多くのクラスがあり、90%-100%がBeanである場合、スキャンされたファイルの削減により、10%-0%の最大の改善が見られます。

初期化を高速化する他の方法を試す必要があります。遅延読み込みまたは遅延読み込み関連のテクニックを使用するか、(そしてそれは冗談ではありません)より高速なハードウェアを使用します(スタンドアロンアプリケーションでない場合)。


Spring XMLを生成する簡単な方法は、元のアプリケーションのようにクラスパススキャンを使用する単純なSpringアプリケーションを作成することです。すべてのBeanが初期化された後、Spring ContextのBeanを反復処理し、Beanが重要なパッケージに属しているかどうかを確認し、このBeanのXML構成をファイルに書き込みます。

9
Ralph

注釈付きクラスの自動検出では、現在、指定されたパッケージ内のすべてのクラスをスキャンする必要があり、現在のクラスロードメカニズムの既知の問題である、長時間かかる可能性があります。

Java 9は、ジグソーパズルを支援します。

Java Mark Reinoldによるプラットフォームモジュールシステム要件 http://openjdk.Java.net/projects/jigsaw/spec/reqs/ から:

効率的な注釈検出—実際にすべてのクラスファイルを読み取らずに、特定の注釈が存在するモジュールアーティファクト内のすべてのクラスファイルを識別できる必要があります。実行時にアノテーションが保持されている限り、実行時に、モジュール内のすべてのクラスを列挙せずに、特定のアノテーションが存在するロード済みモジュール内のすべてのクラスを識別できる必要があります。効率を上げるために、この方法で特定の注釈のみを検出可能にする必要があることを指定する必要がある場合があります。 1つの潜在的なアプローチは、各アノテーションが適用される要素の表示とともに、モジュールに存在するアノテーションのインデックスでモジュールの定義を拡張することです。インデックスのサイズを制限するために、それ自体が新しいメタ注釈(@Indexedなど)で注釈された注釈のみが含まれます。

4
Jeroen Borgers

そこでのパフォーマンスについてできることはそれほど多くありませんが、本番環境での起動については気にしないと思いますが、テスト*の起動時間については気にしません。 2つのヒント:

  • Test-appcontextがアプリの最小限必要なコンポーネントのみを使用することを確認します
  • コンポーネントスキャンディレクティブのリストを作成する代わりに、次のような値をコンマ区切りの値とともに使用します:base-package = "com.package.one、com.package.two ..."
4
abalogh

古い質問であることは承知しており、ご覧のとおり、当時の状況は異なっていましたが、うまくいけば、他の人がこの問題を調査するのに役立ちます。

別の質問に対するこの回答によると、The @ComponentScanアノテーションがlazyInitフラグをサポートするようになりました。これは、起動時間の短縮に役立つはずです。

https://stackoverflow.com/a/29832836/4266381

注:編集によって、XMLへの切り替え自体が魔法のように聞こえます。それでも、コードを詳しく見てみると、default-lazy-init="true"。それが本当の理由だったのかな。

3
Xiangming Hu

コンポーネントスキャンの代わりに、Springの Javaベースのコンテナー構成 を使用できます。

XMLベースの構成と比較すると、Javaベースのコンテナ構成はタイプセーフです。

ただし、最初に、コンポーネントのスキャンパスが十分に具体的であり、サードパーティライブラリのクラスが含まれていないかどうかを確認する必要があります。

2
rwitzel

スキャンするディレクトリを減らす以外に頭に浮かぶのは、 遅延Bean初期化 を使用することだけです。豆がたくさんある場合、これが役立つかもしれません

2
Hons