CDIのProducesアノテーションを使ったインジェクション

【前提条件】

[環境]
  • JDK 1.8.0_66
  • Jersey 2.22.1
  • Doma 2.5.0

【概要】

CDIのProducesアノテーションについてです。
動きがよくわからなかったので、サンプルを作って動かせた結果を書きます。

【pom.xml

CDIであれば他の設定は任意のもので大丈夫だと思います。
今回はDomaのクラスを使っていますが、他の何かしらのライブラリでも代用できます。

<dependencies>
    <dependency>
        <groupId>javax.enterprise</groupId>
        <artifactId>cdi-api</artifactId>
        <version>1.2</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-server</artifactId>
        <version>2.22.1</version>
    </dependency>

    <dependency>
        <groupId>org.seasar.doma</groupId>
        <artifactId>doma</artifactId>
        <version>2.5.0</version>
    </dependency>
</dependencies>

JAX-RSの設定】

今回のサンプルではJAX-RSを使って画面表示します。
(なぜなら結果を確認するのが簡単だから)

[Application]
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/")
public class ApplicationSetting extends Application {

}

【ここから本題】

今回はDomaのDialectインターフェイスをインジェクションさせます。
Dialectはデータベースの方言を表すインターフェイスです。
設定 — Doma 2.0 ドキュメント

今回はbeans.xmlの設定によって切り替えるということをします。
デフォルトをPostgreSQLOracleMySQLステレオタイプ指定というサンプルです。

[ステレオタイプ]

ステレオタイプは下記の二つです。

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.enterprise.inject.Alternative;
import javax.enterprise.inject.Stereotype;

@Alternative
@Inherited
@Stereotype
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface UseOracle {

}
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.enterprise.inject.Alternative;
import javax.enterprise.inject.Stereotype;

@Alternative
@Inherited
@Stereotype
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface UseMySQL {

}

【Producesアノテーション

続いてステレオタイプによって切り替える部分です。

import javax.enterprise.context.Dependent;
import javax.enterprise.inject.Default;
import javax.enterprise.inject.Produces;
import org.seasar.doma.jdbc.dialect.Dialect;
import org.seasar.doma.jdbc.dialect.MysqlDialect;
import org.seasar.doma.jdbc.dialect.OracleDialect;
import org.seasar.doma.jdbc.dialect.PostgresDialect;

@Dependent
public class DialectProduces {

    @Produces
    @Default
    public Dialect getDefaultDialect() {

        return new PostgresDialect();
    }

    @Produces
    @UseMySQL
    public Dialect getMySQLDialect() {

        return new MysqlDialect();
    }

    @Produces
    @UseOracle
    public Dialect getOraDialect() {

        return new OracleDialect();
    }
}

全て戻り値の型はインジェクションしたいオブジェクトの型です。
javax.enterprise.inject.ProducesアノテーションをつけることによりCDIで管理されるようになります。

この書き方の場合はインジェクションされるたびに呼ばれます。
メソッドにスコープアノテーションをつけると挙動が変わります。
(この辺は気が向いたらエントリを書きます。たぶん・・・)

CDIの場合は型でインジェクションする対象が決まるようなので、
DefaultやステレオタイプをつけないとDIできないとデプロイ時にエラーになります。

【結果確認用ソース】

インジェクトされたオブジェクトのクラス名を返却します。

import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import org.seasar.doma.jdbc.dialect.Dialect;

@Path("dialect")
@RequestScoped
public class GetDialect {

    @Inject
    private Dialect dialect;

    @GET
    public Response view() {

        return Response.ok(dialect.getClass().getName()).build();
    }
}

【結果確認】

ブラウザから対象のリソースにアクセスするとPostgreDialectのフルネームが表示されます。
Defaultアノテーションがついたものです。

つづいて、クラスパスの配下に下記のようなMETA-INF/beans.xmlを置きます。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                          http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       bean-discovery-mode="annotated">

    <alternatives>
        <stereotype>jp.glory.cdi.produces.UseMySQL</stereotype>
	<!-- パッケージは適宜変えてください -->
    </alternatives>
</beans>

デプロイし直して再度アクセスすると今度はMysqlDialectのフルネームが表示されます。
ステレオタイプによって切り替えられているのがわかります。

【まとめ】

Producesアノテーションを使ってインジェクションをさせてみました。
通常、スコープアノテーションをつければインジェクションされます。

今回みたいに何かしらのライブラリを使う場合はスコープアノテーションをつけられないので、
Producesアノテーションを使えばインジェクションできます。
ライブラリとの連携周りではかなり重宝するのではないでしょうか?