Produces/Disposeアノテーションでライフサイクル管理

【前提条件】

[環境]
  • JDK 1.8.66
  • Jersey 2.22.1
  • Paraya 4.1.154

【概要】

前々回前回ではProducesアノテーションを使ったインエジェクションについて書きました。
Producesアノテーションを使ったメソッドはオブジェクトをCDI管理にするというものでした。

今回はProducesアノテーションで生成したオブジェクトを破棄するタイミングで
処理を実行させるDisposesアノテーションを見ていきます。

【前提の設定】

pom.xmlとApplicationの設定は前回のエントリと同じです。

【インジェクションするクラス】

import java.io.Serializable;

public class Life implements Serializable {

    private final String label;

    public Life() {
        super();
        this.label = null;
    }

    public Life(String label) {

        this.label = label;
        System.out.println("[" + label + " is created!!]");
    }

    public void call() {

        System.out.println("[" + label + " is called]");
    }

    public void ouputInDestroy() {

        System.out.println("[" + label + " is destroied]");
    }
}

名前を持つクラスです。
コンストラクタ/call/ouputInDestoryでそれぞれ標準出力に名前とメッセージを表示しています。

【Produces/Disposes】

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.Dependent;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.Disposes;
import javax.enterprise.inject.Produces;
import javax.inject.Named;

@Dependent
public class ProducesAndDispose {

    public static final String NONE = "lifeNone";
    public static final String DEPENDENT = "lifeDependent";
    public static final String REQEUST = "lifeRequest";
    public static final String SESSION = "lifeSession";
    public static final String APPLICATION = "lifeApplication";

    @Produces
    @Named(NONE)
    public Life getNonScope() {

        return new Life(NONE);
    }

    @Produces
    @Named(DEPENDENT)
    @Dependent
    public Life getDependentScope() {

        return new Life(DEPENDENT);
    }

    @Named(REQEUST)
    @RequestScoped
    @Produces
    public Life getRequestScope() {

        return new Life(REQEUST);
    }

    @Produces
    @Named(SESSION)
    @SessionScoped
    public Life getSessionScope() {

        return new Life(SESSION);
    }

    @Produces
    @Named(APPLICATION)
    @ApplicationScoped
    public Life getApplicationScopre() {

        return new Life(APPLICATION);
    }

    public void destroyNone(@Named(NONE) @Disposes Life life) {

        life.ouputInDestroy();
    }

    public void destroyDependet(@Named(DEPENDENT) @Disposes Life life) {

        life.ouputInDestroy();
    }

    public void destroyRequest(@Named(REQEUST) @Disposes Life life) {

        life.ouputInDestroy();
    }

    public void destroySession(@Named(SESSION) @Disposes Life life) {

        life.ouputInDestroy();
    }

    public void destroyAppliction(@Named(APPLICATION) @Disposes Life life) {

        life.ouputInDestroy();
    }
}

Namedアノテーションとスコープアノテーションの組み合わせごとに
ProducesアノテーションのメソッドとDisposesアノテーションのメソッドを定義しています。

リクエストスコープの組み合わせ部分だけ抜き出してみます。

    @Named(REQEUST)
    @RequestScoped
    @Produces
    public Life getRequestScope() {

        return new Life(REQEUST);
    }

    public void destroyRequest(@Named(REQEUST) @Disposes Life life) {

        life.ouputInDestroy();
    }

Disposesアノテーションはメソッドのパラメータに対して付与します。
Disposesアノテーションがあると「Disposerメソッド」として登録されます。

Disposerメソッドは対象にクラスにより解決するため、基本的にはクラスに対して1つのようです。
Namedアノテーションをつけた場合は同じクラスでも複数登録できます。

【リソースクラス】

今回もJAX-RSからCDI経由で結果を出力します。

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;

@RequestScoped
@Path("life")
public class LifeCycle {

    @Context
    private HttpServletRequest request;

    @Inject
    @Named(ProducesAndDispose.NONE)
    private Life noneLife;

    @Inject
    @Named(ProducesAndDispose.DEPENDENT)
    private Life dependentLife;

    @Inject
    @Named(ProducesAndDispose.REQEUST)
    private Life requestLife;

    @Inject
    @Named(ProducesAndDispose.SESSION)
    private Life sessionLife;

    @Inject
    @Named(ProducesAndDispose.APPLICATION)
    private Life applicationLife;

    @GET
    @Path("create")
    public Response create() {

        System.out.println("================== create start ==================");

        noneLife.outputInCall();
        dependentLife.outputInCall();
        requestLife.outputInCall();
        sessionLife.outputInCall();
        applicationLife.outputInCall();

        System.out.println("================== create e n d ==================");

        return Response.ok("created!").build();
    }

    @GET
    @Path("invalidate")
    public Response invalidate() {

        System.out.println("================== invalidate start ==================");

        request.getSession().invalidate();

        System.out.println("================== invalidate e n d ==================");

        return Response.ok("invalidated!").build();
    }
}

createでそれぞれのoutputInCallメソッドを呼び出しています。
invlidateメソッドではsessionを破棄しています。

【実行結果】

[create]

まずはcreateから実行してみましょう。

情報:   [lifeNone is created!!]
情報:   [lifeDependent is created!!]
情報:   ================== create start ==================
情報:   [lifeNone is called!!]
情報:   [lifeDependent is called!!]
情報:   [lifeRequest is created!!]
情報:   [lifeRequest is called!!]
情報:   [lifeSession is created!!]
情報:   [lifeSession is called!!]
情報:   [lifeApplication is created!!]
情報:   [lifeApplication is called!!]
情報:   ================== create e n d ==================
情報:   [lifeNone is destroied!!]
情報:   [lifeDependent is destroied!!]
情報:   [lifeRequest is destroied!!]

まず注目するのは「create end」のあとの出力です。

この3行のメッセージはLife#ouputInDestroyにより出力されているものです。
スコープアノテーションなし、Dependent、リクエストスコープの3つが出力されているのがわかります。

セッション/アプリケーションスコープは出力されていません。


次に注目するのはスコープごとの出力タイミングです。

スコープアノテーションなしとDependentスコープは
JAX-RSリソースのオブジェクトが生成されたタイミングでコンストラクタが呼びされているようです。
そのあとにJAX-RSリソースのメソッドを実行して、オブジェクトが破棄される。
というログの出力順序になっています。
(スコープなしはDependentスコープになるので同じ動きをしています。たぶん)

その他のスコープはLife#outputInCallメソッドが呼び出される直前に
コンストラクタが実行されているようです。
リクエストスコープだけオブジェクトが破棄されている。
というのがログの順序です。

この辺は使っているWeldの動きによるものでしょうか?
CDIの正式な仕様ではなさそうです。

[invalidate]

続いてはinvalidateの方にアクセスしてみます。

情報:   [lifeNone is created!!]
情報:   [lifeDependent is created!!]
情報:   ================== invalidate start ==================
情報:   ================== invalidate e n d ==================
情報:   [lifeNone is destroied!!]
情報:   [lifeDependent is destroied!!]
情報:   [lifeSession is destroied!!]

色々と出ていますが、注目するの最後の行です。
セッションスコープのLifeオブジェクトのoutputInDestroyメソッドが実行されていますね。

[サーバ停止]

最後にサーバを停止してみましょう。

情報:   [lifeApplication is destroied!!]

アプリケーションスコープのオブジェクトもoutputInDestroyメソッドが実行されているのがわかります。

【まとめ】

Producesアノテーションをつけて生成したオブジェクトは
Disposesアノテーションをつけたメソッドで後処理ができるというのがわかりました。

ProducesとDisposeeはセットで覚えておく方が良さそうです。