Producesアノテーションでのスコープ管理
【前提の設定】
pom.xmlとApplicationの設定は前回のエントリと同じです。
【インジェクションするクラス】
import java.io.Serializable; import java.time.LocalDateTime; public class CreatedDate implements Serializable { private final LocalDateTime dateTime; private final String name; public CreatedDate() { dateTime = null; name = null; } public CreatedDate(final String name, final LocalDateTime dateTime) { this.name = name; this.dateTime = dateTime; } public LocalDateTime getDateTime() { return dateTime; } public String getName() { return name; } }
名前と時刻を持つクラスです。
【Producesのクラス】
import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.Dependent; import javax.enterprise.context.RequestScoped; import javax.enterprise.context.SessionScoped; import javax.enterprise.inject.Produces; import javax.inject.Named; @Dependent public class ScopeProduces { public static final String NONE = "none"; public static final String DEPENDENT = "dependent"; public static final String REQEUST = "request"; public static final String SESSION = "session"; public static final String APPLICATION = "application"; @Produces @Named(NONE) public CreatedDate getNoneScope() { output(NONE); return createDate(NONE); } @Produces @Named(DEPENDENT) @Dependent public CreatedDate getDependentScope() { output(DEPENDENT); return createDate(DEPENDENT); } @Produces @Named(REQEUST) @RequestScoped public CreatedDate getRequestScope() { output(REQEUST); return createDate(REQEUST); } @Produces @Named(SESSION) @SessionScoped public CreatedDate getSessionScope() { output(SESSION); return createDate(SESSION); } @Produces @Named(APPLICATION) @ApplicationScoped public CreatedDate getApplicationScope() { output(APPLICATION); return createDate(APPLICATION); } private void output(final String label) { System.out.println(label + ":" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"))); } private CreatedDate createDate(final String label) { return new CreatedDate(label, LocalDateTime.now()); } }
同じクラスをインジェクションするのでNamedアノテーションで分けるようにしています。
Producesアノテーションと各スコープアノテーションをつけてメソッドを作成します。
各メソッドではラベルと作成した時刻を標準出力しています。
【リソースクラス】
import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.core.Response; @RequestScoped @Path("scope") public class GetScope { @Inject @Named(ScopeProduces.NONE) private CreatedDate none; @Inject @Named(ScopeProduces.DEPENDENT) private CreatedDate dependent; @Inject @Named(ScopeProduces.REQEUST) private CreatedDate request; @Inject @Named(ScopeProduces.SESSION) private CreatedDate session; @Inject @Named(ScopeProduces.APPLICATION) private CreatedDate application; @GET public Response view() { return Response.ok(createLabels()).build(); } private String createLabels() { StringBuilder builder = new StringBuilder(); List<CreatedDate> dates = new ArrayList<>(); dates.add(none); dates.add(dependent); dates.add(request); dates.add(session); dates.add(application); builder.append("<html>"); builder.append("<head><title>TEST</title></head>"); builder.append("<body>"); builder.append("<table>"); dates.forEach(v -> { builder.append("<tr>"); builder.append("<td>"); builder.append(v.getClass().getName()); builder.append("</td>"); builder.append("<td>"); builder.append(v.getName()); builder.append("</td>"); builder.append("<td>"); builder.append(v.getDateTime().format(DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.nnnnnnnnn"))); builder.append("</td>"); builder.append("</tr>"); }); builder.append("</table>"); builder.append("</body>"); builder.append("</html>"); return builder.toString(); } }
ScopeProducesクラスで定義したものをすべてインジェクションさせて、
インジェクションされたクラス名をHTMLとして出力しています。
【実行結果】
[画面]
作成したリソースにアクセスします。
jp.glory.cdi.produces.scope.CreatedDate none 2015/10/31 10:03:52.444000000 jp.glory.cdi.produces.scope.CreatedDate dependent 2015/10/31 10:03:52.445000000 jp.glory.cdi.produces.scope.CreatedDate$Proxy$_$$_WeldClientProxy request 2015/10/31 10:03:52.458000000 jp.glory.cdi.produces.scope.CreatedDate$Proxy$_$$_WeldClientProxy session 2015/10/31 10:03:52.472000000 jp.glory.cdi.produces.scope.CreatedDate$Proxy$_$$_WeldClientProxy application 2015/10/31 10:03:52.473000000
上記のような結果が返ってくると思います。
スコープアノテーションなし、Dependenスコープの場合は作成したクラス名が出ています。
他のスコープについてはProxyのクラス名が出力されています。
もう一度同じURLにアクセスします。
jp.glory.cdi.produces.scope.CreatedDate none 2015/10/31 10:03:55.923000000 jp.glory.cdi.produces.scope.CreatedDate dependent 2015/10/31 10:03:55.923000000 jp.glory.cdi.produces.scope.CreatedDate$Proxy$_$$_WeldClientProxy request 2015/10/31 10:03:55.924000000 jp.glory.cdi.produces.scope.CreatedDate session 2015/10/31 10:03:52.472000000 jp.glory.cdi.produces.scope.CreatedDate application 2015/10/31 10:03:52.473000000
今度はSessionスコープ、Applicationスコープは作成したクラス名が出ています。
以降、何度か更新してもRequestスコープだけがProxyが出力さています。
スコープアノテーションがある場合は作成したタイミングだけProxyが帰ってきて、
以降はインジェクションしたクラスが帰ってくるようです。
ためしに別のブラウザでアクセスしてみます。
jp.glory.cdi.produces.scope.CreatedDate none 2015/10/31 10:28:48.104000000 jp.glory.cdi.produces.scope.CreatedDate dependent 2015/10/31 10:28:48.105000000 jp.glory.cdi.produces.scope.CreatedDate$Proxy$_$$_WeldClientProxy request 2015/10/31 10:28:48.105000000 jp.glory.cdi.produces.scope.CreatedDate$Proxy$_$$_WeldClientProxy session 2015/10/31 10:28:48.106000000 jp.glory.cdi.produces.scope.CreatedDate application 2015/10/31 10:03:52.473000000
Requestスコープ、SessionスコープがProxy経由になっています。
それぞれRequesetスコープ、Sessionスコープ、Applicationスコープがスコープ通り、
スコープなし、Dependentスコープは毎回オブジェクトが生成されているのがわかります。
[ログ]
つづいて、System.out.printlnで出力した内容を確認してみます。
Producesアノテーションをつけたメソッド内で出力しているので、
メソッドが呼び出されたタイミングで出力されます。
まずは初回アクセスのログです。
none:2015/10/31 10:03:52 dependent:2015/10/31 10:03:52 request:2015/10/31 10:03:52 session:2015/10/31 10:03:52 application:2015/10/31 10:03:52
すべてのメソッドでログが出力されているのがわかります。
つづいて2回目以降アクセスのログです。
none:2015/10/31 10:03:55 dependent:2015/10/31 10:03:55 request:2015/10/31 10:03:55
Sessionスコープ、Applicationスコープが出力されていません。
続いて別ブラウザでアクセした時のログです。
none:2015/10/31 10:28:48 dependent:2015/10/31 10:28:48 request:2015/10/31 10:28:48 session:2015/10/31 10:28:48
Sessionスコープのものが出力されているのがわかります。
【まとめ】
Producesアノテーションを経由した場合でもスコープ通りにCDIで管理されているようです。
ただし、スコープで管理する場合、作成されたタイミングだけProxy経由になるようです。
Proxy経由で問題になることはあまりないと思うので、気にしなくても大丈夫そうです。
もしかしたら、コアな部分では影響があるかもしれないので、覚えておくと良さそうです。
Producesアノテーションをつけたメソッドは生成のタイミングで呼ばれるようです。
なので、オブジェクトの生成に時間がかかるものはメソッド内に書かないほうが良さそうです。
できれば、Applicationスコープとして定義するか
キャッシュの仕組みを作りキャッシュから取得すると良さそうです。