18
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

WildFlyのクラスローディング

Last updated at Posted at 2018-12-28

#はじめに

特に要望はありませんでしたが、WildFlyをワイルドに動かしてみるの続きの記事を書きます。今回はクラスローディングについてです。今回は2018年12月時点で最新の WildFly-15.0.0.Final を使用します。

#WildFly のクラスローディング

WildFly に含まれる jar はモジュールという考え方で管理されています。モジュールには静的モジュールと動的モジュールの2種類があります。

##モジュールの種類

1.静的モジュール
 modules ディレクトリ配下にインストールされた jar を指します。ユーザーが作成したカスタムモジュールの jar を置くこともできます。
2.動的モジュール
 war や ear ファイル内にある jar やクラスを指します。

アプリケーションから静的モジュールを使用する場合、アプリケーション内にデプロイメント記述子ファイルjboss-deployment-structure.xmlを置き、このモジュールを使いますよという宣言である依存関係を記述する必要があります。

ただし、静的モジュールの中には暗黙的依存関係を持つモジュール1があり、これらはjboss-deployment-structure.xmlに記述しなくても勝手にロードされます。

##クラスローディングの優先順位
クラス(モジュール)のロードには優先順があり、同じパッケージの同名クラスがあった場合は先にロードされた優先順位が高いものが使用されます。

各モジュールは以下の順番2でロードされます。

  1. 暗黙的依存関係の静的モジュール1(System Dependencies)
  2. ユーザーが jboss-deployment-structure.xml に設定した静的モジュール(User Dependencies)
  3. 動的モジュール(Local Resource)

##本当かどうか確認してみる
疑り深いので本当かどうか試してみます。

###確認用JSPの作成
暗黙的依存関係のモジュールの中のorg.apache.log4jを使って確認します。まず下記のような jsp を用意します。

logger.jsp
<%@page contentType="text/html; charset=UTF-8" %>
<html>
<body>
<%
Object logger = org.apache.log4j.Logger.getLogger("test");
out.print(logger.getClass().getName());
out.print("<br>");
out.print(logger.toString());
%>
</body>
</html>

###確認用にせクラスの作成
以下のような偽の Logger クラスを作成、コンパイルして classloadTestInModules.jar に固めます。これを静的モジュールとして modules ディレクトリ配下にインストールします。

Logger.java
package org.apache.log4j;
public class Logger {

	public static String getLogger(String name) {
	//org.apache.log4j.Loggerクラスの替わりに文字列moduleを返す。
		return "module";
	}
}

次に少しだけ変更して、同様にコンパイルして classloadTestInWar.jar に固めます。これは動的モジュールとしてアプリケーション(war)に含めます。

Logger.java
package org.apache.log4j;
public class Logger {

	public static String getLogger(String name) {
	//org.apache.log4j.Loggerクラスの替わりに文字列warを返す。
		return "war";
	}
}

###静的モジュールのインストール
作成した classloadTestInModules.jar を modules ディレクトリ以下にインストールします。

以下のディレクトリを作成します。
 wildfly-15.0.0.Final/modules/system/layers/base/test
 wildfly-15.0.0.Final/modules/system/layers/base/test/test
 wildfly-15.0.0.Final/modules/system/layers/base/test/test/main

下記に classloadTestInModules.jar を置きます。
 wildfly-15.0.0.Final/modules/system/layers/base/test/test/main

module.xmlを以下の内容で作成します。test.test がこのモジュールの名前になります。

module.xml
<?xml version="1.0" encoding="UTF-8"?>
<module name="test.test" xmlns="urn:jboss:module:1.7">
    <resources>
        <resource-root path="classloadTestInModules.jar"/>
    </resources>
</module>

次に wildfly-15.0.0.Final/standalone/deployments にある適当なアプリケーション helloworld.warWEB-INF/libclassloadTestInWar.jar war 直下に logger.jsp を置きます。デプロイメントマーカーファイル helloworld.war.dodeploy を作成してデプロイさせて準備完了です。

###暗黙的依存関係のモジュールが使用される?

ブラウザでhttp://localhost:8080/helloworld/logger.jspにアクセスします。

以下のように表示されました。ちゃんと Logger クラスのオブジェクトが戻ってきていますので、暗黙的依存関係のモジュールのクラスが返ってきたことが分かります。

org.apache.log4j.Logger
org.apache.log4j.Logger@62af7a58

想定通りに動きました:clap:

###ユーザーが置いた静的モジュールが使用される?

暗黙的依存関係のモジュールではなくユーザーが指定したモジュールを使いたいという設定を入れてみます。helloworld.war/META-INF にデプロイメント記述子ファイル jboss-deployment-structure.xml を以下の内容で作成します。

jboss-deployment-structure.xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
    <deployment>
		<exclusions>
			<module name="org.apache.log4j" />
		</exclusions>
		<dependencies>
			<module name="test.test"/>
		</dependencies>
    </deployment>
</jboss-deployment-structure>

<dependencies>は使いたい静的モジュールの名前です。<exclusions>は使いたくない暗黙的依存関係のモジュールの名前になります。ここで再度、helloworld.war.dodeploy ファイルを作成してデプロイしなおし http://localhost:8080/helloworld/logger.jsp にアクセスします。

以下のように表示されました。 module という内容の String 型が戻ってきていますので、インストールした test.test モジュールのクラスが使用されたことが分かります。

java.lang.String
module

想定通りに動きました:clap:

###動的モジュールが使用される?

次に動的モジュールのみ使用するように設定します。 jboss-deployment-structure.xml を以下の内容に変更します。暗黙的依存関係モジュールを除外する設定のみ残します。

jboss-deployment-structure.xml
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
    <deployment>
		<exclusions>
			<module name="org.apache.log4j" />
		</exclusions>
    </deployment>
</jboss-deployment-structure>

再度、helloworld.war.dodeploy ファイルを作成してデプロイしなおし http://localhost:8080/helloworld/logger.jsp にアクセスします。

以下のように表示されました。 war という内容の String 型が戻ってきていますので、WEB-INF/lib にある動的モジュール classloadTestInWar.jar のクラスが使用されたことが分かります。

java.lang.String
war

想定通りに動きました:clap:

#まとめ

WildFlyがクラスをロードする優先順位を意識していないと、作成したアプリケーションの中に暗黙的依存関係のモジュールと同じ物が含まれていた場合、バージョンの違などによる思わぬエラーに悩まされることがありますのでご注意下さい。

#参考資料

  1. 暗黙的依存関係のモジュール一覧 ⇒ 2.3. Which are the implicit module dependencies?
    第6章 JBoss EAP クラスローディング 2

  2. クラスローディングの優先順 ⇒ 1.3. Class Loading Precedence

18
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?