JBoss AS 7では、開発者は起動時間を大幅に短縮できました(ワークステーションで空のappserverが3〜4秒で起動します)。 これは、このアプリケーションサーバーの新しいクラスローディングシステムによって促進されました。 このアプローチにはまだ欠点があります。
この記事では、さまざまな環境でクラスをロードするためのメカニズム、JBoss AS 7で動作する機能、Apache Mavenビルドシステム、およびIntelliJ IDEA IDEとペアリングします。
Javaでのクラスのロード
Java言語仕様に従って、 JVMはクラスを動的にロード、バインド、および初期化します。 クラスの検索とロードは、クラスローダーの階層によって実行されます。
Javaマシンの起動時に、 ブートストラップクラスローダーが使用されます (これは、rt.jarおよびJVM実装の他の部分から基本クラスをロードする役割を果たします)。 将来的には、通常、指定されたCLASSPATHプログラム内を検索するシステムクラスローダーへの委任が行われます。 そして、最も単純なケースでは、これは制限されています。
将来的には、クラスファイルがロードおよび解析され、そのバインディング(クラスの依存関係の検証、準備、および解決)が行われます。 その後、初期化が行われます。 つまり、クラスの説明の読み込み、静的フィールドの初期化、静的初期化子の実行が発生します
必要に応じて「遅延」。 これは、 有効Javaで説明されているstatic-holderを使用した遅延初期化パターンの基礎です。
サーブレットコンテナ、アプリケーションサーバー、OSGiなどの環境では、事態は複雑になります。 クラスローダーの複雑な階層を持ち、許可された環境(たとえば、環境によって提供されるライブラリ)にアクセスできるように設計され、同時に異なる「モジュール」を互いに分離します。
つまり、ロードされた2つのWebアプリケーションには、同じクラスの異なるバージョンが存在する場合があり、それぞれのアプリケーションにのみ表示されます。
当然、クラスが検出されない可能性があります-原則として、クラスの読み込みエラーが発生し、インターセプトされないとJVMがクラッシュします(アプリケーションサーバーのように)。
サーブレットコンテナ(tomcat、jetty)の通常の読み込み構造
このタイプの環境では、クラスローダーの階層はもちろん複雑です。 サーブレットコンテナ自体を実行するように設計されたシステムクラスローダーの後、検索はチェーンのさらに下に委任されます。
コンテナに埋め込まれた各アプリケーションに対して、独自のクラスローダーが作成され、 WEB-INF /クラス内のクラスとWEB-INF / lib内のjarファイルが検索されます(すべてこのアプリケーションのwar内)。
次に、 共通クラスローダーへの委任が行われ、すべてのクライアントに共通のライブラリ(tomcatの場合はCATALINA_HOME / lib内)が検索されます。
アプリケーションサーバー
残りの話は、glassfishアプリサーバーでクラスをロードすることです。 予想どおり、長いブートチェーンの最初のブートストラップクラスローダー 。 その後、制御はパブリックAPIクラスローダーに転送されます。このクラスローダーは、アプリケーションサーバーによってエクスポートされたJava EEスタックおよびその他のAPIのクラス、インターフェース、およびアノテーションを処理します。 次は、Tomcatでよく知られている共通のクラスローダーです 。これは、アプリケーションサーバー全体に共通のライブラリを担当します。 チェーンの次はコネクタクラスローダーです 。これにより、すべてのアプリケーションがJCAにアクセスできます。 その後、制御はlifecyclemodule classloaderに転送されます 。 アプリケーション内のBeanのライフサイクルを制御するクラスをロードするように設計されています。 次のヒーローはapplib classloaderです 。これはcommonに非常に似ていますが、一部のアプリケーショングループで動作します。 そして最後に、 アーカイブクラスローダーがトリガーされ 、WAR / EAR / RAR / EJB-JARなどのクラスを操作します。
クラスを検索およびロードするこのような複雑なシステムにより、クラスローダー自体を単純化し、抽象化レベルにより明確に分割し、ライブラリの重複を回避できます。 主な欠点は、このシステムの非常に遅いことであり、その結果、アプリサーバーの読み込み時間が長くなり、アプリケーションが展開されます。
物語のヒーローJBoss AS 7の機能
JBoss AS 6は、構造がクラスローダーglassfishと非常に似ています。 開始時刻まで-同様に。 バージョン7を根本的に回復するものは何ですか? なぜ彼女は開発者の注目を集めたのですか?
答えは簡単です。非常に迅速なスタートです。 以前は、数秒で起動するアプリケーションサーバーはありませんでした。 AS 6とGlassFishはせいぜい分を始めます。 不幸なglassfishがPermGenで絶えずリロードして死んでしまう開発者にとって、これは単なる救いです。
それでは、問題の技術的な側面に移りましょう。 どのようにしてこのような速度を達成しましたか? 階層型クラスローダーの遅い動作の問題を解決するための基礎は、 JBoss Modulesです。 このプロジェクトは、クラスをロードするための(階層的ではなく) モジュール式のアプローチを目指しています。 検索はモジュールの小さなセット内で実行されます。 問題は、どのモジュールを探すべきかを環境がどのように知ることができるかということです。
残念ながら、依存関係モジュールを明示的にリストすることによってのみ解決されます。 これを行うには、依存関係がリストされている「依存関係」フィールドが、マニフェスト内のデプロイ可能なwarに追加されます。
サーバーはその名前で、このモジュールを記述するxmlを見つけます。それに含まれるjarファイルのセット、およびこのモジュールが依存するモジュール。
Mavenを使用する
mavenを使用してビルドする場合、パッケージを提供するこのアプローチにより、pom.xmlのいくつかの依存関係が提供されていると宣言されます。 たとえば、slf4jを使用すると、次のようになります。
<dependencides> <dependecy> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.6.1</version> <scope>provided</scope> </dependecy> <!-- ... --> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <configuration> <archive> <manifestEntries> <Dependencies>org.slf4j</Dependencies> </manifestEntries> </archive> </configuration> </plugin> <!-- ... --> </plugins> </build>
IntelliJ IDEAでのデバッグ
このIDEの魅力にもかかわらず、多くの場合、不快な欠陥が見つかります。 たとえば、Mavenと対話する場合。 前の段落の設定を使用してプロジェクトをデバッグしようとすると、mavenによって収集されたアーティファクトが正しく機能することがわかります。
また、Ideaからデプロイすると、NoClassDefFoundErrorが飛びます。 これは、IDEAがpom.xmlからのデータを使用せず、そのMANIFEST.MFを生成し、Dependencies句が存在しないことで破綻するためです。
このような独自のマニフェストを作成することで、この動作を回避できます。
Manifest-Version: 1.0
Dependencies: org.slf4j
このような回避策の明らかな欠点は、2つの場所で依存関係情報が重複していることです。