Статьи

Уроки, извлеченные из CDI в Swing

Похоже, что я погрузился в CDI, я изучил классическое использование Java EE. Затем я использовал CDI в своем любимом проекте, чтобы посмотреть, как его можно использовать в приложениях Swing. Эта статья подводит итог, какие уроки я извлек из этого далеко.

Примечание: в этой статье предполагается, что вы немного знакомы с CDI; если нет, пожалуйста, прочитайте мои предыдущие статьи о CDI (CDI обзорная часть 1 и часть 2 ) или прочитайте документацию .

В моих последних попытках Swing (я скорее веб-разработчик), я подумал, что было бы неплохо сконфигурировать большинство компонентов пользовательского интерфейса в Spring, а не кодировать параметры вручную. При этом я использовал традиционный подход к настройке XML, поскольку использование аннотаций связывало бы мой код с Spring JAR, что мне не нравится. С CDI, однако, я могу жить с привязкой моего кода к стандартному API: я могу изменить реализацию. Это похоже на разработку сервлета, в котором вы ссылаетесь на API сервлета, а затем запускаете его в любом контейнере, который хотите.

Я кодировал и кодировал таким образом, смотрел на сделанные вещи, подвергал рефакторингу, а затем кодировал еще немного, как тогда, когда это личный проект, и у вас нет сроков выполнения. Затем я понял, что могу выучить некоторые уроки, которые мог бы найти хорошее применение в будущем. Вот они, в произвольном порядке.

Правильная банка

Получение всех правильных CDI JAR для вашего проекта Java SE может быть довольно сложной задачей. Чтобы облегчить вашу боль, Weld предоставляет вам универсальный JAR-файл, точно названный weld-se. Не спорьте, что он раздутый и переупакованный, просто используйте его, вы сэкономите много боли. Для пользователей Maven это конфигурация:

<project>
...
<dependencies>
<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-se</artifactId>
<version>1.0.1-Final</version>
</dependency>
</dependencies>
</project>

Она легкий регистратор

Все известные мне каркасы журналов (JDK, Commons Logging, Log4J и SLF4J) позволяют довольно легко объявить свой регистратор классов:

private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);

Во всяком случае, большую часть времени, чтобы использовать это, вы либо:

  • создать шаблон в вашей IDE. Тем не менее, настройка шаблонов в каждой вашей IDE может быть довольно утомительной. Примечание для себя: предложите функцию, где такие шаблоны (и предпочтения) могут быть сохранены в Интернете и доступны всем моим экземплярам IDE.
  • скопируйте строку откуда-то, затем вставьте ее туда, где вам нужно. Затем вы меняете класс для регистрации. Честно говоря, и хотя это не добавляет моей легенде (как раз наоборот), это то, что я делаю все время

И делая последнее все время, я забываю сменить зарегистрированный класс. Глупо, не правда ли? Возможно, но еще более глупо то, что я должен написать это в первую очередь: класс — это мой контекст, он должен быть выведен. Угадайте, что, Weld делает это, с небольшой помощью JAR сварного регистратора (он использует SLF4J, так что будьте осторожны или используйте код в качестве шаблона для использования другой платформы):

@Inject
private Logger logger;

Я действительно не думаю, что это может быть проще! Единственный недостаток — вы теряете постоянную: для проектов, которыми я сейчас занимаюсь, я могу с этим жить. Чтобы использовать эту функцию, просто добавьте следующую зависимость в POM:

<project>
...
<dependencies>
<dependency>
<groupId>org.jboss.weld</groupId>
<artifactId>weld-logger</artifactId>
<version>1.0.1-Final</version>
</dependency>
</dependencies>
</project>

Помните инициализацию

CDI может, конечно, внедрить ваши зависимости. Но внедрение ваших зависимостей не собирает их на экране. К счастью, CDI также предоставляет вам хук, чтобы сделать это: это область аннотации @PostConstruct. Давайте рассмотрим очень простой пример:

public class MyPanel extend JPanel {

@Inject
private JCheckBox checkbox;

@PostConstruct
public void afterPropertiesSet() {

setLayout(new FlowLayout());

add(checkbox);
}
}

Метод afterPropertiesSet () вызывается после внедрения всех зависимостей и играет роль, ранее выполненную в конструкторе.

Примечание: да, метод назван в честь старого метода Spring, определенного в интерфейсе InitializingBean , начиная с версии 1. Теперь его можно заменить, указав атрибут init-method компонента в файле определения компонентов XML или используя @PostConstruct аннотаций.

(Не) расти в количестве

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

  • это увеличивает вашу кодовую базу и, таким образом, увеличивает сложность разработки
  • в JVM от Sun загрузка большего количества классов означает, что вы будете использовать больше пространства PermGen и с большей вероятностью столкнетесь с OutOfMemoryError

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

public class ComponentFactory {

@Produces
public JButton getButton() {

return new JButton();
}
}

Используйте точку впрыска

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

public class ComponentFactory {

@Produces
public JButton getButton(InjectionPoint ip) {

JButton button = new JButton();

// Do something based on annotations on the parameter
...

return button;
}
}

Именно так создаются регистраторы сварных швов (см. Выше).

Уважай правило горцев

Правление горцев таково: «Может быть только один». Помимо того, что он взят из фильма, который молодежь в основном не знает, он также раскрывает основную истину. Поскольку инъекция должна быть детерминированной, не может быть двух кандидатов на точку инъекции. Использование методов продюсера, как было сказано ранее, столкнет вас с этой проблемой: у CDI будет и класс JButton, и метод продюсера, и он будет громко жаловаться на него, как обычно.

Exception in thread "main" org.jboss.weld.exceptions.DeploymentException: WELD-001409 Injection point has ambiguous dependencies.....
at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:280)
at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:122)
at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:141)
at org.jboss.weld.bootstrap.Validator.validateBeans(Validator.java:331)
at org.jboss.weld.bootstrap.Validator.validateDeployment(Validator.java:317)
at org.jboss.weld.bootstrap.WeldBootstrap.validateBeans(WeldBootstrap.java:399)
at org.jboss.weld.environment.se.Weld.initialize(Weld.java:81)
at org.jboss.weld.environment.se.StartMain.go(StartMain.java:45)
at org.jboss.weld.environment.se.StartMain.main(StartMain.java:57)

Чтобы соответствовать правилу Highlander, вам придется отказаться от класса JButton в качестве кандидата на инъекцию. Для этого я использовал квалификаторы, как для точки внедрения, так и для метода продюсера. Так как я не хотел квалификатор для каждой пары точек внедрения / метода производителя, я сделал его параметризуемым:

@Qualifier
@Target( { FIELD, METHOD })
@Retention(RUNTIME)
public @interface JType {

public Class<? extends JComponent> value();
}

Аннотирование как внедренного атрибута, так и метода продюсера аннотацией JType сделало мой код совместимым с правилом Highlander!

Распространить слово

Хорошая практика, которую я могу порекомендовать, — это создать специальный класс Properties, которому поручено инициализировать ваши метки (и другой для ваших предпочтений и т. Д.), А затем внедрить его во все необходимые клиентские классы. Теперь все классы имеют доступ к вашим интернационализированным ярлыкам. Действительно потрясающе!

Забудьте Java WebStart

Сварочный JAR-анализ несовместим с Java WebStart. Скорее всего, у вас будут такие приятные сообщения об ошибках:

122 [javawsApplicationMain] WARN org.jboss.weld.environment.se.discovery.URLScanner - could not read entries
java.io.FileNotFoundException: http:\xxxxxxxxxx\weld-logger-1.0.0-CR2.jar (La syntaxe du nom de fichier, de répertoire ou de volume est incorrecte)
at java.util.zip.ZipFile.open(Native Method)
at java.util.zip.ZipFile.<init>(Unknown Source)
at java.util.zip.ZipFile.<init>(Unknown Source)
at org.jboss.weld.environment.se.discovery.URLScanner.handleArchiveByFile(URLScanner.java:142)
at org.jboss.weld.environment.se.discovery.URLScanner.handle(URLScanner.java:126)
at org.jboss.weld.environment.se.discovery.URLScanner.scanResources(URLScanner.java:107)
at org.jboss.weld.environment.se.discovery.SEWeldDiscovery.scan(SEWeldDiscovery.java:71)
at org.jboss.weld.environment.se.discovery.SEWeldDiscovery.<init>(SEWeldDiscovery.java:45)
at org.jboss.weld.environment.se.discovery.SEBeanDeploymentArchive$1.<init>(SEBeanDeploymentArchive.java:45)
at org.jboss.weld.environment.se.discovery.SEBeanDeploymentArchive.<init>(SEBeanDeploymentArchive.java:44)
at org.jboss.weld.environment.se.discovery.SEWeldDeployment.<init>(SEWeldDeployment.java:37)
...
at com.sun.javaws.Launcher.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

 

Я надеюсь, что это будет исправлено в следующих выпусках …

Синглтоны НЕ ЗЛО

Я уже дважды сталкивался со странным поведением: переключать кнопки, когда они не должны или состояние было потеряно. После отладки, как сумасшедший, я увидел, что методы @PostConstruct были вызваны намного дальше первоначального вызова. Кажется, мой код вызвал еще одну инъекцию после этого. Чтобы исправить это, аннотируйте свой класс с помощью @Singleton, чтобы разделить экземпляр между несколькими вызовами. Я не исследовал больше, чем это, потому что:

  • Я решил ошибку
  • Я не знаю, почему я не должен использовать синглтоны в приложении Swing

Вывод

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

И, пожалуйста, простите за каламбур, я чувствовал себя веселым из-за этого нашего прекрасного лета: -S

 

Из http://blog.frankel.ch/lessons-learned-from-cdi-in-swing