インジェクションするオブジェクトの切り替え
スレテオタイプアノテーションを使い、
インジェクションするオブジェクトの切り替えを行います。
【前提条件】
[環境]
- JDK 1.7.0_07
- Glassfish 3.1.2.2
- PostgreSQL 9.1(JDBC:postgresql-9.1-901.jdbc4.jar)
【サンプルコード】
[前提となるソース]
ベースとなるソースです。
[ページ]
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html"> <f:view> <h:form id="searchForm"> <h:dataTable value="#{userListPage.getUserList()}" var="user"> <h:column> <f:facet name="header"> ユーザID </f:facet> <h:outputText value="#{user.userId}" /> </h:column> <h:column> <f:facet name="header"> ユーザ名 </f:facet> <h:outputText value="#{user.name}" /> </h:column> </h:dataTable> </h:form> </f:view> </html>
[ManagedBean]
package jp.glory.ui.impl; import java.util.List; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.inject.Named; import jp.glory.application.UserApplication; import jp.glory.infrastructure.persistence.entity.UserData; @Named(value = "userListPage") @RequestScoped public class UserListPage { @Inject private UserApplication applicaiton = null; /** * @return userList */ public List<UserData> getUserList() { return applicaiton.findAllList(); } }
[アプリケーションインターフェイス]
package jp.glory.application; import java.util.List; import jp.glory.infrastructure.persistence.entity.UserData; public interface UserApplication { List<UserData> findAllList(); }
[アプリケーションクラス]
package jp.glory.application.impl; import java.util.List; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; import jp.glory.application.UserApplication; import jp.glory.infrastructure.persistence.entity.UserData; @Stateless public class UserApplicationImpl implements UserApplication { @PersistenceContext(unitName = "blog") private EntityManager manager = null; @Override public List<UserData> findAllList() { final TypedQuery<UserData> query = manager.createNamedQuery("findAll", UserData.class); return query.getResultList(); } }
[Entity]
package jp.glory.infrastructure.persistence.entity; import java.io.Serializable; import java.sql.Timestamp; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.JoinColumns; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.Table; @Entity @Table(name = "userdata") @NamedQueries({ @NamedQuery(name = "findAll", query = "SELECT u FROM UserData u"), @NamedQuery(name = "findByUserId", query = "SELECT u FROM UserData u WHERE u.userId = :userId"), }) public class UserData implements Serializable { private static final long serialVersionUID = 6528642302239465812L; @Id @Column(name = "user_id") private String userId = null; private String name = null; @Column(name = "last_update") private Timestamp lastUpdate = null; @OneToMany(cascade = CascadeType.ALL) @JoinColumns({ @JoinColumn(name = "auth_user_id", referencedColumnName = "user_id") }) private List<UserAuthority> authorityList = null; // アクセサメソッドは省略 }
<?xml version="1.0" encoding="UTF-8"?> <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://docs.jboss.org/cdi/beans_1_0.xsd"> </beans>
[ステレオタイプアノテーション]
切り替え対象となる明示するための
アノテーションを作成します。
package jp.glory.common.annotation; import java.lang.annotation.ElementType; 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 @Stereotype @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface SampleMock { }
javax.enterprise.inject.Alternativeアノテーションを使用します。
Alternativeアノテーションをつけたクラスは
CDIのインジェクション対象から外れます。
インジェクション対称にする場合はbeans.xmlに設定を追記します。
ステレオタイプアノテーションなので、
javax.enterprise.inject.Stereotypeアノテーションをつけます。
TargetアノテーションにはTYPEを指定します。
RetentionアノテーションにはRUNTIMEを指定します。
[切り替え先のクラス]
サンプルではUserApplicationインターフェイスの
サブクラスのスタブクラスを作成します。
package jp.glory.test.application.stub; import java.util.ArrayList; import java.util.List; import jp.glory.application.UserApplication; import jp.glory.common.annotation.SampleMock; import jp.glory.infrastructure.persistence.entity.UserData; @SampleMock public class UserApplicationStub implements UserApplication { @Override public List<UserData> findAllList() { final List<UserData> dataList = new ArrayList<>(); dataList.add(createStubData("STUB0001", "NAME0001")); dataList.add(createStubData("STUB0002", "NAME0002")); dataList.add(createStubData("STUB0003", "NAME0003")); dataList.add(createStubData("STUB0004", "NAME0004")); dataList.add(createStubData("STUB0005", "NAME0005")); return dataList; } private UserData createStubData(final String userId, final String name) { final UserData returnData = new UserData(); returnData.setUserId(userId); returnData.setName(name); return returnData; } }
作成したSampleMockアノテーションをつけています。
それ以外は画面に表示するためのデータを作成する処理を行っているだけです。
[beans.xml]
Alternativeの設定を有効にするためには
beans.xmlを書き換えます。
<?xml version="1.0" encoding="UTF-8"?> <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://docs.jboss.org/cdi/beans_1_0.xsd"> <alternatives> <stereotype>jp.glory.common.annotation.SampleMock</stereotype> </alternatives> </beans>
alternativesタグで指定したものが有効になります。
サンプルでは作成したアノテーションをstereotypeタグで定義しています。
[最後に]
実際の開発では連携先のシステムがある場合、
ローカルではスタブクラスを使い、テスト/本番環境では本物のクラスを使う
というニーズは良くあると思います。
(DDDで言うところの腐敗防止層です)
Alternativeアノテーションを使えば、
容易に切り替えができます。
beans.xml内部に直接書かずに
環境ごとに変更する箇所だけ別のXMLファイルに切り出したいのですが、
そういった機能はないようです。