型解決
静的な型解決 @Qualifiler
動的な型解決 @Produces
★複数の実装クラスの曖昧さを解決する方法
1.カスタムの限定子(@Qualifier)タイプを定義して関係づける
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
@Qualifier
@Retention(RUNTIME)
@Target({METHOD, FIELD, PARAMETER, TYPE})
public @interface MyQualifier {}
@Inject
@MyQualifier
CustomerDAO customerDAO;
或いは
@Inject
public SomeClass(@MyQualifier CustomerDAO customerDAO) {
this.customerDAO = customerDAO;
}
@MyQualifier
public class JPACustomerDAOImpl implements CustomerDAO {...}
public class XmlCustomerDAOImpl implements CustomerDAO {...}
2.標準の限定子@Newを用いて、クラス名で関係づける
※Deprecated in CDI 1.1. @Dependent scoped beans instead
import javax.enterprise.inject.New;
@Inject
@New(JPACustomerDAOImpl.class)
CustomerDAO customerDAO;
public class JPACustomerDAOImpl implements CustomerDAO {...}
public class XmlCustomerDAOImpl implements CustomerDAO {...}
3.標準の限定子@Namedを用いて、名前(文字列)で関係づける
import javax.inject.Named;
@Inject
@Named("jpaDao")
CustomerDAO customerDAO;
@Named("jpaDao")
public class JPACustomerDAOImpl implements CustomerDAO {...}
@Named("xmlDao")
public class XmlCustomerDAOImpl implements CustomerDAO {...}
4.標準の@Alternativeを用いて、beans.xmlから指定する
import javax.enterprise.inject.Alternative;
@Inject
CustomerDAO customerDAO;
@Alternative
public class JPACustomerDAOImpl implements CustomerDAO {...}
@Alternative
public class XmlCustomerDAOImpl implements CustomerDAO {...}
beans.xmlに明示がない場合:JPACustomerDAOImpl(プロダクション用)
beans.xmlに明示がある場合:XmlCustomerDAOImpl(単体テストモック用)
@Inject
CustomerDAO customerDAO;
public class JPACustomerDAOImpl implements CustomerDAO {...}
@Alternative
public class XmlCustomerDAOImpl implements CustomerDAO {...}
<?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://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<alternatives>
<class>com.example.dao.JPACustomerDAOImpl</class>
</alternatives>
</beans>
サンプル(テスト用)
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
@Alternative
public @interface Mock {}
★@Producesについて
サンプル1
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.Instance;
import javax.inject.Inject;
import javax.inject.Named;
@Named(value = "xxx")
@SessionScoped
public class MyManagedBean implements Serializable {
@Inject
@MyRandom
Instance<Integer> randomNumber;
@Inject
@MyMaxNumber
private int maxNumber;
@PostConstruct
public void reset() {
...
this.number = randomNumber.get();
}
}
import java.util.Random;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;
@ApplicationScoped
public class Generator implements Serializable {
private Random random = new Random(System.currentTimeMillis());
private int maxNumber = 100;
@Produces
@MyRandom
public int nextNumber() {
return this.random.nextInt(maxNumber);
}
@Produces
@MyaxNumber
public int getMaxNumber() {
return this.maxNumber;
}
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD)
public @interface MyMaxNumber / MyRandom {
}
サンプル2
@Named(value = "xxx")
public class MyManagedBean {
@Inject
@MyQualifier
private IService service;
...
}
@SessionScoped
@Named("generator")
public class Generator implements Serializable {
private int serviceType;
public void changeService(int value) {
this.serviceType = value;
}
@Produces
@MyQualifier
public IService getService() {
IService service = null;
switch (this.serviceType) {
case 0:
service = new AService();
break;
case 1:
service = new BService();
break;
default:
break;
}
return service;
}
}
サンプル3
@SessionScoped
public class MessageSenderFactory implements Serializable {
@Produces
@MessageTransport(MessageTransportType.EMAIL)
public MessageSender getEmailMessageSender(){
return new EmailMessageSender();
}
...
}
@Inject
@MessageTransport(MessageTransportType.EMAIL)
private MessageSender messageSender;
public enum MessageTransportType {
EMAIL, SMS;
}
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD})
public @interface MessageTransport {
MessageTransportType value();
}
サンプル4
@Path("/charge")
public class ChargeResource {
@Inject
@Credit
private ChargeService service;
...
}
@RequestScoped
public class PaymentStrategy {
private PaymentType type;
@Credit
@Produces
public ChargeService getStrategy(AService a, BService b) {
switch (type) {
case APayment: return a;
case BPayment: return b;
...
}
...
}
}
★Disposeについて
サンプル1
@Inject
private Connection conn; // ConnectionImpl
public class ConnectionFactory {
@Produces
@MyConnection
public Connection createConnection() {
conn.connect();
return conn;
}
public void closeConnection(
@Disposes @MyConnection Connection conn) {
conn.close();
}
}
@Inject
@MyConnection
private Connection connection;
サンプル2
import org.apache.ibatis.session.SqlSession;
@Stateless
public class MyService {
@Inject
private SqlSession session;
public List<XXX> doSomething() {
...
}
}
@Produces
@RequestScoped
public SqlSession openSession() {
...
}
// リクエスト毎にクローズされる
public void closeSession(@Disposes SqlSession sqlSession) {
sqlSession.close();
}
<alternatives>
<stereotype>xxx.cdi.stereotype.Mock</stereotype>
</alternatives>
補足 @Specializesについて
@Dependent
public class DefaultLocaleResolver implements LocaleResolver {
@Override
public Locale getLocale() {
return Locale.getDefault();
}
}
@ApplicationScoped
@Specializes
public class TestLocalResolver extends DefaultLocaleResolver {
@Override
public Locale getLocale() {
return Locale.ENGLISH;
}
}
★Qualifierの数を削減
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE, METHOD, PARAMETER})
public @interface Transport {
TransportType type() default TransportType.STANDARD;
或いは
TransportType value();
}
public enum TransportType {
JSON, SOAP, STANDARD;
}
@Transport(type=TransportType.JSON)
//@Transport(TransportType.JSON)
public class MyImpl implements MyInterface {
@Inject
@Transport(type=TransportType.JSON)
//@Transport(TransportType.JSON)
private MyInterface service;
改善
@Inject @Named("persistent")
MessageSession session;
@Named("persistent")
public class PersistentMessageSession implements MessageSession {...}
或いは
@EJB(beanName="TransientMessageSession")
MessageSession session;
@Stateless
@Local(MessageSession.class)
public class TransientMessageSession implements MessageSession {...}
↓
@Qualifier
@Retention(RUNTIME)
@Target({FIELD, TYPE})
public @interface MySession {
Type value();
enum Type{
TRANSIENT, PERSISTENT
}
}
@Inject @Session(MySession.Type.TRANSIENT)
MessageSession session;
@Session(MySession.Type.TRANSIENT)
public class TransientMessageSession implements MessageSession {...}