Статьи

Java EE6 CDI, именованные компоненты и классификаторы

Одним из самых больших обещаний, которые дал Java EE6, было облегчить использование внедрения зависимостей. Они сделали, используя CDI . CDI, обозначающий контексты и внедрение зависимостей для Java EE, предлагает базовый набор для применения внедрения зависимостей в вашем корпоративном приложении.

До CDI в EJB 3 также вводилось внедрение зависимостей, но это было немного базовым. Вы можете добавить EJB (statefull или без сохранения состояния) в другой EJB или Servlet (если ваш контейнер поддерживает это). В Offcourse не каждое приложение нуждается в EJB, поэтому CDI приобретает такую ​​большую популярность.

Для начала я сделал этот пример. Есть интерфейс оплаты и 2 реализации. Оплата наличными и виза.

Я хочу выбрать способ оплаты, который я использую, и при этом использовать тот же интерфейс:

1
2
3
public interface Payment {
    void pay(BigDecimal amount);
}

Вот 2 реализации:

1
2
3
4
5
6
7
8
9
public class CashPaymentImpl implements Payment {
  
    private static final Logger LOGGER = Logger.getLogger(CashPaymentImpl.class.toString());
  
    @Override
    public void pay(BigDecimal amount) {
        LOGGER.log(Level.INFO, "payed {0} cash", amount.toString());
    }
}
1
2
3
4
5
6
7
8
9
public class VisaPaymentImpl implements Payment {
  
    private static final Logger LOGGER = Logger.getLogger(VisaPaymentImpl.class.toString());
  
    @Override
    public void pay(BigDecimal amount) {
        LOGGER.log(Level.INFO, "payed {0} with visa", amount.toString());
    }
}

Чтобы добавить интерфейс, мы используем аннотацию @Inject . Аннотация делает в основном то, что говорит. Он внедряет компонент, который доступен в вашем приложении.

1
@Inject private Payment payment;

Конечно, вы видели, как это происходит за милю, это не сработает. Контейнер имеет 2 реализации нашего интерфейса Payment, поэтому он не знает, какой из них ввести.

Неудовлетворенные зависимости для типа [Payment] с квалификаторами [@Default] в точке внедрения [[field] @Inject private be.styledideas.blog.qualifier.web.PaymentBackingAction.payment]

Поэтому нам нужен какой-то классификатор, чтобы указать, какую реализацию мы хотим. CDI предлагает аннотацию @Named , позволяющую дать имя реализации.

01
02
03
04
05
06
07
08
09
10
@Named("cash")
public class CashPaymentImpl implements Payment {
  
    private static final Logger LOGGER = Logger.getLogger(CashPaymentImpl.class.toString());
  
    @Override
    public void pay(BigDecimal amount) {
        LOGGER.log(Level.INFO, "payed {0} cash", amount.toString());
    }
}
01
02
03
04
05
06
07
08
09
10
@Named("visa")
public class VisaPaymentImpl implements Payment {
  
    private static final Logger LOGGER = Logger.getLogger(VisaPaymentImpl.class.toString());
  
    @Override
    public void pay(BigDecimal amount) {
        LOGGER.log(Level.INFO, "payed {0} with visa", amount.toString());
    }
}

Теперь, когда мы изменим наш код внедрения, мы можем указать, какая реализация нам нужна.

1
@Inject private @Named("visa") Payment payment;

Это работает, но гибкость ограничена. Когда мы хотим переименовать наш параметр @Named, мы должны изменять его везде, где он используется. Также нет поддержки рефакторинга.

Есть лучшая альтернатива с использованием пользовательских аннотаций с использованием аннотации @Qualifier. Давайте немного изменим код.

Прежде всего, мы создаем новые типы аннотаций.

1
2
3
4
5
@java.lang.annotation.Documented
@java.lang.annotation.Retention(RetentionPolicy.RUNTIME)
@javax.inject.Qualifier
public @interface CashPayment {
}
1
2
3
4
5
@java.lang.annotation.Documented
@java.lang.annotation.Retention(RetentionPolicy.RUNTIME)
@javax.inject.Qualifier
public @interface VisaPayment {
}

Аннотация @Qualifier , добавляемая к аннотации, делает эту аннотацию видимой для контейнера. Теперь мы можем просто добавить эти аннотации к нашим реализациям.

01
02
03
04
05
06
07
08
09
10
@CashPayment
public class CashPaymentImpl implements Payment {
  
    private static final Logger LOGGER = Logger.getLogger(CashPaymentImpl.class.toString());
  
    @Override
    public void pay(BigDecimal amount) {
        LOGGER.log(Level.INFO, "payed {0} cash", amount.toString());
    }
}
01
02
03
04
05
06
07
08
09
10
@VisaPayment
public class VisaPaymentImpl implements Payment {
  
    private static final Logger LOGGER = Logger.getLogger(VisaPaymentImpl.class.toString());
  
    @Override
    public void pay(BigDecimal amount) {
        LOGGER.log(Level.INFO, "payed {0} with visa", amount.toString());
    }
}

Единственное, что нам теперь нужно сделать, это изменить наш код инъекции на

1
@Inject private @VisaPayment Payment payment;

Когда мы теперь изменим что-то на наш классификатор, у нас будет хорошая поддержка компилятора и рефакторинга. Это также добавляет дополнительную гибкость для разработки API или языка, специфичного для предметной области .

Ссылка: Java EE6 CDI, именованные компоненты и квалификаторы от нашего партнера JCG Джелле Виктор из блога Styled Ideas .

Статьи по Теме :