Статьи

События CDI в приложении Swing для разделения пользовательского интерфейса и обработки событий

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

Что хорошо в CDI, так это то, что он представляет собой нечто большее, чем просто инъекционный механизм. Вдобавок к этому он предоставляет также элегантный и мощный механизм передачи событий. Эту функцию можно красиво сочетать с Swing для создания приложения с графическим интерфейсом на основе шаблона MVC.

Действительно возможно эффективно объединить CDI и среду Swing, чтобы быстро и с четкой структурой создать приложение с графическим интерфейсом Java. Оставайтесь с нами, чтобы узнать, как …

Прежде всего, эталонная реализация CDI, называемая Weld, распространяется также в виде отдельной библиотеки. Вы можете добавить его в свой проект и начать его использовать. Единственное отличие от стандартного способа запуска приложения заключается в том, что вам необходимо запустить контейнер Weld, который так же прост, как этот вкладыш:

1
2
3
4
5
import org.jboss.weld.environment.se.StartMain;
...
  public static void main(String[] args) {  
    StartMain.main(args);
  }

Чтобы добавить Weld в приложение maven, просто добавьте эту зависимость: org.jboss.weld.se:weld-se:2.2.9.Final . Чтобы выполнить код вашего приложения, вы должны поместить его в метод, который наблюдает событие ContainerInitialized:

1
2
3
public void start(@Observes ContainerInitialized startEvent) {
  // code which would be usually in the main() method
}

В приведенном выше методе вы можете инициализировать свое приложение, построить и отобразить графический интерфейс и дождаться событий Swing.

И тут начинается интересная часть. Я буду использовать механизм событий CDI для реализации связывания между компонентами Swing и моделью, используя шаблон наблюдателя. Идея состоит в том, чтобы запускать пользовательские события всякий раз, когда должно происходить обновление данных, а не изменять данные напрямую. Контроллер наблюдает инициированные события и выполняет действия на основе данных события. Затем действия манипулируют моделью данных и отправляют уведомления для просмотра обновлений данных. Смотрите следующую диаграмму:

Свинг-CDI-MVC

Цикл MVC начинается с прослушивателей действий Swing, которые составляют объект действия и генерируют его как событие CDI. Слушатель действия не привязан к какому-либо коду контроллера — контроллер привязан к событию с использованием механизма CDI. Это полностью отделяет код GUI от бизнес-логики. Следующий фрагмент отвечает на событие нажатия кнопки и отправляет действие для добавления значения в счетчик:

1
2
3
4
5
6
7
8
9
@ApplicationScoped
class MainFrame extends javax.swing.JFrame {
  @Inject Event<ChangeValueAction> changeValueAction;
...
  void addButtonActionPerformed(java.awt.event.ActionEvent evt) {
    changeValueAction.fire(ChangeValueAction.plus(getValue()));
  }
...
}

Здесь нужно помнить, что наблюдатели событий CDI будут создаваться как новые объекты для любого инициируемого события вместе со всеми зависимостями. Я использовал @ApplicationScoped для MainFrame, чтобы гарантировать, что весь код работает на одном и том же экземпляре.

Здесь нужно упомянуть одну вещь: чтобы CDI работал, экземпляр MainFrame должен быть создан CDI, а не напрямую использовать его конструктор. Это достигается путем внедрения его в уже существующий компонент — например, тот, который наблюдает событие ContainerInitialized, генерируемое при запуске.

Механизм CDI отправляет событие любому методу-наблюдателю, который прослушивает событие этого типа. Мы создаем приложение контроллера и помещаем код в метод наблюдателя, например так:

1
2
3
4
5
6
7
public class Application {
...
  public void updateValueWhenChangeValueAction(@Observes final ChangeValueAction action) {
  ... // controller action
  }
...
}

Наконец, контроллер обновляет модель и запускает обновление представления при необходимости. Если мы пойдем дальше, мы можем запустить событие обновления из контроллера, которое будет наблюдаться представлением, в данном случае компонентом MainFrame. Или даже построить модель, которая автоматически запускает события CDI при обновлении. Таким образом, контроллер и представление будут полностью отделены и будут реагировать только на события — события графического интерфейса, передаваемые в направлении от представления к контроллеру, и события обновления данных, передаваемые из контроллера / модели в представление. Таким образом, механизм событий CDI очень удобен для построения Приложение MVC Swing с View отделено от бизнес-логики. Это можно сделать, запустив приложение в контейнере Weld CDI (1 строка кода), запустив действия от слушателей Swing (2 строки кода) и наблюдая за действиями (один метод в любом классе с поддержкой CDI). Действия принимают форму компонента данных, который сам по себе не слишком много строк кода.