Статьи

CDI — обзор: часть 2

В предыдущей части CDI мы видели некоторые инъекции, квалификаторы и область применения. Теперь пришло время просмотреть более продвинутые функции.

Производители

Предыдущие примеры не могут разрешить все наши варианты использования. Некоторые из них включают в себя:

  • введение случайных значений
  • внедрение контекстно-зависимого значения
  • в общем, места, где процесс впрыска не может быть сужен до простого new ()

Эти намеки на очень известный шаблон, заводской. Заводы внедрены в JSR-299 как производители.

Давайте рассмотрим простой пример — внедрение соединения из источника данных. Код, который получает соединение, либо создает его с прямым подключением к базе данных, либо извлекает его из пула источников данных. В последнем случае подойдет следующий код:

public @interface FromDataSource {}

public class ConnectionProducer {

@Produces @FromDataSource
public Connection getConnection() throws Exception {

Context ctx = new InitialContext();

// Read the data source name from web.xml
String name = ...

DataSource ds = (DataSource) ctx.lookup(name);

return ds.getConnection();
}
}

Перехватчики

С Java EE 6 вы можете использовать возможности AOP без AOP. Как и в предыдущем примере, использование перехватчиков очень просто. Есть 3 шага. Давайте реализуем простой таймер для целей бенчмаркинга.

Первым шагом является объявление перехватчика. Для этого просто используйте @InterceptorBinding:

@InterceptorBinding
@Retention(RUNTIME)
@Target({METHOD, TYPE})
public @interface Benchmarkable{}

Второй шаг — реализация перехватчика. Он использует аннотацию @Interceptor в сочетании с ранее определенной:

@Benchmarkable @Interceptor</pre>
public class BenchmarkInterceptor {

@AroundInvoke
public Object logPerformance(InvocationContext ic) throws Exception {

long start = System.currentTimeMillis();

Object value = ic.proceed();

System.out.println(System.currentTimeMillis() - start);

return value;
}
}

Примечание:

  • метод, аннотированный @AroundInvoke, возвращает объект
  • он использует параметр типа InvocationContext

Последний шаг — объявить такие перехватчики в WEB-INF / 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://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<interceptors>
<class>ch.frankel.blog.weld.BenchmarkInterceptor</class>
</interceptors>
</beans>

Bean.xml также сообщает контейнеру, как упорядочить перехватчики, если их больше одного.

Есть два других типа перехватчиков, @PostConstruct и @AroundTimeout (для EJB).

Декораторы

Декораторы, думаю , что, реализовать шаблон проектирования Decorator . Они очень похожи на перехватчики с двумя интересными отличиями:

  • декоратор должен реализовывать интерфейс, который он украшает (и все же может быть абстрактным, поэтому он не должен реализовывать методы)
  • декоратор может иметь ссылку на объект, который он украшает. Это делается через инъекцию

Как и перехватчики, они должны быть указаны в файле beans.xml для активации. Давайте возьмем простой пример и создадим интерфейс, контракт которого должен возвращать HTML-представление объекта:

public interface Htmlable {

String toHtml();
}

Теперь мне нужен класс даты, который знает свое представление HTML. Я знаю, что дизайн довольно плох, но терпите меня.

public class HtmlDate extends Date implements Htmlable {

public String toHtml() {

return toString();
}
}

Если я хочу декоратор, который помещает HTML-код в теги <strong>, вот способ:

@Decorator
public class StrongDecorator implements Htmlable {

@Inject @Delegate @Any
private Htmlable html;

public String toHtml() {

return "<strong>" + html.toHtml() + "</strong>";
}
}

Наблюдатели

CDI также реализует шаблон проектирования Observer , таким образом, наконец, обеспечивая простую парадигму разработки на основе событий на платформе Java EE. Основой для этого является тип события. Тип события — это простой POJO.

Observer также является POJO: чтобы метод Observer вызывался при возникновении события, просто добавьте параметр правильного типа события и аннотируйте его с помощью @Observed:

public class EventObserverService {

public void afterPostEvent(@Observes PostEvent event) {

... // Do what must be done
}
}

С другой стороны, источник событий должен иметь атрибут типа javax.enterprise.Event, параметризованный с тем же типом события. Чтобы вызвать событие, вызовите event.fireEvent () с экземпляром события:

public class WeldServlet extends HttpServlet {

@Inject
private Event<PostEvent> event;

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

event.fire(new PostEvent());
}
}

Теперь при отправке запроса POST сервлету будет вызван метод afterPostEvent () службы EventObserverService.

альтернативы

В предыдущей статье я обращался к фиктивному сервису с вызовом установщика и передачей вновь созданного экземпляра «вручную». Это все хорошо в модульном тестировании, но я также хочу управлять интеграционным тестированием. Таким образом, ситуация следующая:

  • на пути к классам есть две реализации одного и того же интерфейса
  • Вы не можете изменить код сервлета (например, добавить квалификатор в атрибут сервиса)

Учитывая детерминистическую природу CDI, вы должны быть в основном тостами. На самом деле, ничто не может быть дальше от истины. Просто используйте аннотацию @Alternative, и CDI будет удобно игнорировать аннотированный класс.

@Report(MAIL) @Alternative
public class MockMailReportServiceImpl implements ReportService {
...
}

Какой смысл тогда создавать его в первую очередь? Запомните неиспользуемый до. Он придет к нам на помощь, так как он принимает теги <alternative>. Эти теги активируют альтернативы.

<?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>ch.frankel.blog.weld.service.MockMailReportServiceImpl</class>
</alternatives>
</beans>

Таким образом, вы можете иметь два beans.xml:

  • в основном пустой стандартный контекст
  • и другой контекст тестирования интеграции, полный альтернатив

Платформа

Логотип шваЛоготип GlassFish v3Эта статья была написана с использованием GlassFish v3, который использует Weld v1.0.1 в качестве платформы. Weld — это эталонная реализация CDI, а также часть структуры Seam.

У меня не было проблем с использованием платформы в целом, но я не мог заставить работать альтернативы, перехватчики и декораторы. Как ни странно, все три должны быть настроены в WEB-INF / beans.xml. Я не знаю, сделал ли я что-то не так или есть ошибка в текущей реализации.

Вывод

Эта статья, состоящая из двух частей, наносится только на поверхность CDI. Тем не менее, ИМХО, это выглядит очень перспективно, и я желаю ему больших успехов.

Чтобы идти дальше:

  • CDI, обзор — часть 1
  • Страница аннотаций Commons (JSR-250) : аннотации Commons содержат аннотацию для DI в Java (@Resource)
  • Страница CDI (JSR-299) : достаточно удивительно, CDI о DI в Java EE
  • Weld в документации : Weld является реализация CDI JBoss , а также эталонная реализация
  • Статья о достоинствах JSR-299 по сравнению с достоинствами JSR-330

 

С http://blog.frankel.ch/cdi-an-overview-part-2