Где разместить SwingUtilities.invokeLater в отдельном Java-приложении с поддержкой Spring?

1

Я использую Spring для моего приложения Java Swing. Я инициализирую мои объекты MVC как весенний боб в моем приложении-context.xml таким образом, с @Autowired для DI.

<bean id="model" class="com.Model"/>

<bean id="view" class="com.View"/>

<bean id="controller" class="com.Controller"/>

Он работает без проблем. Однако, читая этот вопрос, я думаю, что я должен поместить все компоненты Swing в SwingUtilities.invokerLater() по этой причине.

Некоторые методы компонента Swing помечены как "потокобезопасные" в спецификации API; они могут быть безопасно вызваны из любого потока. Все остальные методы компонента Swing должны быть вызваны из потока отправки событий. Программы, которые игнорируют это правило, могут нормально функционировать большую часть времени, но подвержены непредсказуемым ошибкам, которые трудно воспроизвести.

Итак, мой вопрос: где/как помещать мои вещи в эту тему Dispatch Event? В настоящее время мой основной метод - это всего лишь один лайнер...

ApplicationContext context =
    new ClassPathXmlApplicationContext("application-context.xml");

ОБНОВЛЕНИЕ: Я хочу знать, что я должен делать?

SwingUtilities.invokeLater(new Runnable() {
  public void run() {
     ApplicationContext context =
        new ClassPathXmlApplicationContext("application-context.xml");
  }
  context.xxxx
  blahblahblah...
});
Теги:
multithreading
spring
swing

2 ответа

0

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

Это решение использует метод Spring lookup для ленивой загрузки Swing Gui на EDT.

IOnDemandBeanGetter.java
Это интерфейс Spring, который будет использоваться для инъекции метода

public interface IOnDemandBeanGetter {
    public IOnDemandBean getBean();
}

IOnDemandBean.java
Интерфейс маркера, возвращаемый IOnDemandBeanGetter

public interface IOnDemandBean {}

GuiOnEdtUtility.java
Эта утилита использует введенный IOnDemandBeanGetter, чтобы получить компонент и загрузить его на EDT.

public class GuiOnEdtUtility {
    private final IOnDemandBeanGetter beanGetter;

    public GuiOnEdtUtility(IOnDemandBeanGetter beanGetter) {
        this.beanGetter = beanGetter;
    }

    public void createGuiOnEdt(){
        SwingUtilities.invokeLater(()->beanGetter.getBean());
    }
}

MainGui.java
Это содержит компоненты swing, будет возвращен IOnDemandBeanGetter и загружен в EDT с помощью GuiOnEdtUtility. Вы можете вводить любые компоненты, которые я не делал для простоты, но все они должны иметь lazy-init = "true"

public class MainGui implements IOnDemandBean {
    private final JFrame frame = new JFrame();

    public MainGui() {
        System.out.println(SwingUtilities.isEventDispatchThread() 
                           ? "Created On EDT"
                           : "Not created On EDT");
    }

    public void init() {
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(200, 200);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

beans.xml

<bean name="onDemandBeanGetter" class="IOnDemandBeanGetter">
    <lookup-method name="getBean" bean="mainGui"/>
</bean>

<bean name="guiOnEdtUtility" class="GuiOnEdtUtility" init-method="createGuiOnEdt">
    <constructor-arg ref="onDemandBeanGetter"></constructor-arg>
</bean>

<bean name="mainGui" class="MainGui" init-method="init" lazy-init="true"/>

Обновить...
Вот как выглядит MainGui.java, если он унаследовал от JFrame, и он работает одинаково

public class MainGui extends JFrame implements IOnDemandBean {

    public MainGui() {
        System.out.println(SwingUtilities.isEventDispatchThread() 
                           ? "Created On EDT"
                           : "Not created On EDT");
    }

    public void init() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(200, 200);
        setLocationRelativeTo(null);
        setVisible(true);
    }
}
0

Чтобы запустить код в потоке отправки событий, вы можете:

EventQueue.invokeLater(new Runnable() {
    public void run() {
       //your code here
    }
 });

... или эквивалентно:

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
       //your code here
    }
 });

Вы можете использовать любой подход в любом месте, где у вас есть код, который нужно запустить в потоке отправки событий. Просто заверните код внутри анонимного Runnable и вам хорошо идти.

Обратите внимание: если у вас есть переменные, определенные вне анонимной реализации Runnable которой вы хотите получить доступ, вам нужно объявить их final. Для получения дополнительной информации попробуйте следующее:

  • 0
    В обычном приложении свинга я знаю, как это сделать. Но в контексте весны я не. Вы имеете в виду, что я должен поставить новый ClassPathXmlApplicationContext ("application-context.xml"); внутри invokerLater? Таким образом, все мои другие весенние бобы будут помещены в эту нить, которая не идеальна.
  • 0
    @ gd4, если вы создаете контекст приложения в EDT, все компоненты будут создаваться в EDT, но не уверены, что вы подразумеваете под «помещать в этот поток». Объекты доступны для всех потоков
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню