Статьи

Spring: как создать развязанные компоненты Swing

Применимость Spring Framework в контексте Swing, кажется, недооценена, по крайней мере, когда кто-то смотрит в Интернете. Что Spring может предложить в этом контексте? Вместо того, чтобы обсуждать сугубо теоретически, давайте посмотрим на полный, компилируемый пример, шаг за шагом, и сделаем из этого наши выводы.

Давайте сначала посмотрим на исходную структуру приложения Swing, которую мы будем иметь в конце этой статьи:

Первый аспект, который сразу же можно наблюдать из приведенной выше исходной структуры, состоит в том, что у нас есть один класс для каждого из компонентов Swing, с которыми мы будем иметь дело, то есть те файлы выше, которые не выделены — JFrame, JPanel, JTextField, и JButton. У нас также есть класс для ActionListener, который мы добавим к нашему JButton. Все в этих классах, те, которые не выделены, это чистая Java и чистый Swing, там нет ничего странного, каждый из классов расширяет или реализует классы, которые вы ожидаете — JFrame, JPanel, JTextField, JButton и ActionListener. Наконец, есть два специфичных для Spring файла, оба из которых выделены выше. Существует класс, который запускает все приложение, используя классы, специфичные для Spring, и файл конфигурации Spring, который, как мы увидим,соединит все вместе. Когда приложение запускается, результат выглядит следующим образом:

По общему признанию, это очень скромный результат. И в этой части компоненты Swing даже не делают ничего значимого. Суть в том, чтобы просто добавить компоненты Swing в приложение без добавления какого-либо Java-кода, чтобы сделать это возможным. Однако этого более чем достаточно, чтобы проиллюстрировать большинство основных принципов применимости Spring в контексте Swing. Обычно в отсутствие Spring компоненты Swing в нашем приложении будут добавляться друг к другу в коде Java. Вы бы использовали метод «add» в JFrame, чтобы добавить JPanel в JFrame, и вы бы использовали метод «add» в JPanel, чтобы добавить к нему JTextField и JButton. Однако в этом случае такого кода нет. Фактически, ни один из классов не ссылается ни на один из других классов. Все они автономны. Они, на самом деле,отделены друг от друга. Вот JFrame:

package springexample;import java.awt.Dimension;import javax.swing.JFrame;public class MyJFrame extends JFrame {   public void init() {                java.awt.EventQueue.invokeLater(new Runnable() {            public void run() {                setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);                setSize(new Dimension(300, 300));                setVisible(true);            }        });            }} 

Другие классы похожи, предоставляя только тот код, который относится к определенному компоненту Swing, который он определяет. Даже ActionListener отделен. ActionListener не добавляется в JButton в коде вышеупомянутых классов Java. Все изолировано, отделено, изолировано, урезано и автономно.

Все это возможно благодаря тому, что приложение имеет JAR-файлы Spring на своем пути к классам вместе с модулем запуска, который находит файл конфигурации Spring, который, в свою очередь, определяется точно так:

После первых пяти строк, которые предоставляют заголовок, состоящий из соответствующего пространства имен Spring, давайте посмотрим, что делает каждый из основных элементов выше:

  • Строка 6-9: устанавливает JFrame, указав его свойство title и свойство contentPane. Обратите внимание, что мы также устанавливаем атрибут «init-method», который предоставляет значение, которое является именем метода в классе JFrame, который мы хотим использовать для создания экземпляра класса.
  • Строка 10-19: устанавливает основной JPanel, состоящий из двух JTextFields и другого экземпляра того же JPanel, определенного в строке 20-27. Мы также передаем свойство с именем «axis» и свойство с именем «panelComponents». Оба из них будут обработаны нашим классом JPanel. Первый из них используется для установки свойства «axis» макета, используемого JPanel, для которого установлено BoxLayout. Свойство panelComponents будет заполнено нашими тремя компонентами Swing. Итератор в классе JPanel будет перебирать полученные компоненты, добавляя каждый из них в JFrame. Все это происходит в методе init, который устанавливается в качестве значения атрибута init-method.
  • Строка 20-27: устанавливает нижнюю панель, которая в этом случае предоставляет только JButton. Обратите внимание, что этот JPanel определяется тем же классом, что и класс, используемый для определения главной панели. Итак, мы повторно используем JPanel через конфигурационный файл Spring.
  • Строка 28: устанавливает первый JTextField.
  • Строка 29: устанавливает второй JTextField.
  • Строка 30-37: устанавливает JButton вместе с его свойством «text» и свойством «actionListener». Обратите внимание, что мы не установили свойство «text» JTextFields, что мы могли бы сделать, если бы мы хотели это сделать. Вы можете установить свойство «текст», а также большинство других свойств прямо в файле конфигурации Spring, если вы решили это сделать. Твой выбор.
  • Строка 38: устанавливает ActionListener.
  • Это полный файл, и он в основном довольно читабелен невооруженным глазом, без особых сюрпризов, кроме (если вы новичок в этом) гибкости и мощности, предлагаемых конфигурационным файлом Spring. Теперь давайте посмотрим, что именно в этих классах. JFrame уже показан выше. Ниже следуйте всем другим классам:

    package springexample;import java.awt.Component;import java.util.Iterator;import java.util.List;import javax.swing.BoxLayout;import javax.swing.JPanel;public class MyJPanel extends JPanel {    private List panelComponents;    private int axis;    public void setAxis(int axis) {        this.axis = axis;    }    public void setPanelComponents(List panelComponents) {        this.panelComponents = panelComponents;    }    public void init() {        setLayout(new BoxLayout(this, axis));        for (Iterator iter = panelComponents.iterator(); iter.hasNext();) {            Component component = (Component) iter.next();            add(component);        }    }}
    package springexample;import javax.swing.JTextField;public class MyJTextField extends JTextField {    public void init() {        setText("hello world");    }}
    package springexample;import java.awt.event.ActionListener;import javax.swing.JButton;public class MyJButton extends JButton {    private ActionListener actionListener;    public void setActionListener(ActionListener actionListener) {        this.actionListener = actionListener;    }    public void init() {        this.addActionListener(actionListener);    }}
    package springexample;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import javax.swing.JOptionPane;public class MyActionListener implements ActionListener {    public void actionPerformed(ActionEvent e) {        JOptionPane.showMessageDialog(null, "Hello from Spring!");    }}

    Хотя мы добавили в JButton ActionListener, мы не указали, какой ActionListener. Чтобы изменить ActionListener, вы просто измените строку 38 в файле конфигурации Spring выше. Просто измените атрибут «classname» на что-то другое, и тогда все готово. Ясно, что нет кода Java, который соединяет класс «MyActionListener» с классом «MyJButton». Точно так же нет Java-кода, соединяющего компоненты Swing друг с другом. Для конечного пользователя нет никакой разницы. Для разработчика все, что соединяет что-либо, устанавливается в конфигурационном файле Spring, создавая таким образом отсоединенные компоненты Swing, а также отсоединенное поведение (т. Е. Как показано в ActionListener).

    Наконец, давайте посмотрим на класс запуска, который использует специфичный для Spring код, но не требует пояснений:

    package springexample;import org.springframework.context.support.ClassPathXmlApplicationContext;public class SpringLauncher {    public static void main(String[] args) {        String[] contextPaths = new String[]{"springexample/app-context.xml"};        new ClassPathXmlApplicationContext(contextPaths);    }}  

    Теперь, что мы получаем от всего этого? Действительно ли Spring способствует полной дезинтеграции компонентов Swing до этого низкого уровня? Не обязательно, но возможно. Дело в том, что вы можете отделить столько, сколько хотите и сколько хотите. Все в одном приложении. Но почему вы хотите это сделать? Теперь, когда мы понимаем ЧТО, давайте посмотрим на ПОЧЕМУ. Есть целые книги о ПОЧЕМУ. ПОЧЕМУ « инъекция зависимости»«и» слабая связь «. Одно приятное преимущество состоит в том, что, поскольку ваши компоненты действительно урезаны и отделены друг от друга, их легче тестировать. Ваши тесты JUnit (или другие тесты) будет легче писать, потому что нет обременения от одного класса к другому. А как насчет сантехнического кода, т. е. проводки, которую вы обычно должны поддерживать? Все это теперь находится в конфигурационном файле Spring, ни одного из него нет в коде Java. что файл конфигурации Spring неинвазивен. Вы можете просто перенести его в существующее приложение, написать бин для основного класса, а затем добавить код запуска. Тогда все готово. Итак, интеграция с существующим Swing приложения могут возникать без необходимости изменения этих существующих приложений, если только вы не хотите сделать это, чтобы улучшить соответствие.

    Наконец, еще раз посмотрите на эту JPanel. Это повторно используемый компонент Swing (как и другие используемые здесь компоненты Swing, такие как JTextField, который вы также использовали повторно). Вы использовали его дважды в одном приложении для разных целей. В каждом случае вы имеете большой контроль над макетом JPanel, прямо из файла конфигурации Spring, так что, даже если вы используете один и тот же JPanel, его содержимое может отличаться при каждом его повторном использовании. Также обратите внимание, что мы используем общий объект List. Вместо указания конкретного компонента Swing, который сделал бы JPanel менее пригодным для повторного использования, мы выбрали универсальный объект List, чтобы не ограничивать компоненты, которые он может обрабатывать. Это необходимо учитывать при создании развязанных компонентов Swing и, следовательно,работа в отрыве влияет на ваш процесс проектирования.

    Я надеюсь, что это послужит полезным введением в Spring Framework в контексте Swing. В следующей части мы позволим поведению одного из компонентов Swing (JButton) вызвать что-то, что произойдет с одним из других (одним из JTextFields). Большое спасибо Чеду Вулли, который написал отличный учебник по Swing и Spring, из которого я многому научился. Этот урок был отправной точкой для этого. Другой документ, на который стоит обратить внимание, — Java Programming / Spring Framework от Hyad из Википедии.