GlassFishのホットデプロイ時間をみてみる(CDIバージョン)

前回EJB(Stateless Session Bean)のホットデプロイ対象を見てみましたが、CDI(旧WebBeans)でsimple bean*1だとどうなるのかみてみました。

試したSimpleBeanは以下のような、メソッドが1つのコードです。

package org.example;

import javax.inject.Named;

@Named
public class HelloService_1 {

    public void sayHello() {
    	System.out.println("Hello!!");
    }

}


以下の表が結果です(EJBも合わせて書きました)

クラス数 時間(SLSB) 時間(simple bean)
1 264ms 293ms
2 276ms 318ms
10 378ms 356ms
50 958ms 455ms
100 1,607ms 593ms
1000 18,208ms 3,646ms

simple beanも数に応じて時間が増えていきますが、EJBより圧倒的に軽い!?。インターセプタは今回利用していないので、使うとなると結果が変わってくるかもしれませんが。
もうちょっと利用機能を増やして次回は測ってみますかな。

追記

simple beanの@Namedを外して測定してみると、ほとんどデプロイ持間は変わりませんでした。もしかすると、CDI(Weldの実装)はデプロイ時の初期化処理はないのでしょうかねぇ...? 名前で引けるように、コンポーネントとして登録されているとは思うのですが。JBoss SeamだとComponentスキャンが行われていて、そこそこ時間がかかってたしなぁ。

さらに追記

@Injectでインジェクションしたsimple beanをgetClass()して標準出力に出してみたところ、エンハンスされていない素のクラスでした。これだと初期化処理は軽いかも。やっぱりsimple beanにインターセプタを組み込んで試してみた。


以下のログ出力インターセプタを示すアノテーションを作成。

package org.example;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.interceptor.InterceptorBinding;

@InterceptorBinding
@Inherited
@Target( { ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Logging {
}

以下のログ出力インターセプタを作成。

package org.example;

import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

@Logging
@Interceptor
public class LoggingInterceptor {

	@AroundInvoke
	public Object logging(InvocationContext ctx) throws Exception {
		System.out.println("before called.");
		try {
			return ctx.proceed();
		} finally {
			System.out.println("after called.");
		}
	}
}

WEB-INF/beans.xmlに作成したインターセプタを登録。

<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">

	<interceptors>
		<class>org.example.LoggingInterceptor</class>
	</interceptors>

</beans>

そして、simple beanに作成したログ出力インターセプタのアノテーションを付与する。

package org.example;

import javax.inject.Named;

@Named
@Logging
public class HelloService { ... }

これでインターセプタが有効となります。beans.xmlの定義は無くても動いていいのにと思ったのですが、複数インターセプタを仕掛けるとなる順序制御が必要になってきたり、インターセプタを外したいといった場合のためにbeans.xmlに定義するようになっている模様です。
さすがにインターセプタがかかると@Injectしたクラスはエンハンスされており、以下のように出力されました。Seamと同じでjavassistでエンハンスされていますね。

情報: class org.example.HelloService_$$_javassist_78

では、これでクラス数を変えてまた測定してみます。

  • 測定方法(書いてなかった)
    • Javaファイルを保存し直して、GlassFishがログ出力するデプロイ時間が測定値とする
    • ばらつきがあるので、何回か保存を試し、落ち着いたところの数字を採用する
クラス数 時間(SLSB) 時間(simple bean) 時間(intercepted simple bean)
1 264ms 293ms 368ms
2 276ms 318ms 377ms
10 378ms 356ms 433ms
50 958ms 455ms 544ms
100 1,607ms 593ms 732ms
1000 18,208ms 3,646ms 4,585ms*2

結果は上記の通り、インターセプタを組み込むコストのためデプロイ時間は増加してます。といっても、EJB(インターセプタなし)と比べるとかなり速い。
EJBのデプロイ時間が遅い理由は、ネーミングサービスの登録と言ったEJB固有の処理が多すぎるのでしょうか。また、EJBは過去の仕様もサポートしているため遅くなっている部分がありそう。まぁコンテナの実装次第ですが、EJB2のデプロイロジックを再利用してると遅そうな気はしますね。定義していないけど内部でローカルインタフェースを作ったりとかしてたりして。

*1:CDIではEJBでもない単なるJavaBeanをコンテナに登録することができます。これをsimple beanと読んでいる模様。

*2:何回か試しているとGlassFishが反応しなくなる現象に遭遇。Seamと同じでリデプロイを繰り返すとPermanent領域を食っていってメモリ不足に陥っているのかも