2

I am using Spring framework for my java Swing application. I am initializing my MVC objects as spring bean in my application-context.xml in this way, with @Autowired for DI.

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

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

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

It runs successfully without any problem. However by reading at this question, I think I should put every Swing components inside SwingUtilities.invokerLater() for this reason.

Some Swing component methods are labelled "thread safe" in the API specification; these can be safely invoked from any thread. All other Swing component methods must be invoked from the event dispatch thread. Programs that ignore this rule may function correctly most of the time, but are subject to unpredictable errors that are difficult to reproduce.

So my question is, where/how to put my stuff to this Event Dispatch Thread? Currently my main method is just a one liner...

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

UPDATE: I wanna know if this is what I should do?

SwingUtilities.invokeLater(new Runnable() {
  public void run() {
     ApplicationContext context =
        new ClassPathXmlApplicationContext("application-context.xml");
  }
  context.xxxx
  blahblahblah...
});
Community
  • 1
  • 1
gddabe
  • 91
  • 7
  • Whatever solution you are trying you can check if your code is being run in the EDT with `SwingUtilities.isEventDispatchThread()`. – DSquare Mar 17 '14 at 11:22

2 Answers2

0

To run code on the event-dispatch thread, you can do:

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

...or equivalently:

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

You can use either approach in any location where you have code that you need to run on the event-dispatch thread. Just wrap the code inside the anonymous Runnable and you're good to go.

Note that if you have variables defined outside of the anonymous Runnable implementation that you want to access from within it, you'll need to declare them as final. For more information, try the following:

Community
  • 1
  • 1
aroth
  • 54,026
  • 20
  • 135
  • 176
  • In the normal swing app i know how to do that. But in the spring context i dont. Do you mean i should put new ClassPathXmlApplicationContext("application-context.xml"); inside invokerLater? In this way all of my other spring beans will be put inside this thread which is not ideal. – gddabe Mar 17 '14 at 11:17
  • @gd4 if you create the application context on the EDT all beans will created on the EDT, but not sure what you mean by 'put inside this thread'. Objects are accessible to all threads – Nick Holt Mar 17 '14 at 11:24
  • I updated my question a bit at the end. Is that what I should do? – gddabe Mar 17 '14 at 13:13
  • It depends upon what things you are trying to move to the event-dispatch thread. You example code will move creation of the context to the event-dispatch thread. Is that what you want? It seems to me like there would likely be come code in your `com.Controller` and `com.View` classes that you would also want to migrate over. – aroth Mar 17 '14 at 23:46
  • @aroth this is what I am confusing about. To my understanding, if I `new` Swing components, I should do it inside EDT. But in some sense, it seems to me that if I update Swing components, i.e. refresh JTable values, I should do it inside EDT. Which one is correct? – gddabe Mar 19 '14 at 06:26
-1

I had this same question last night, searched everywhere, but could not find and appropriate answer. I racked my brain for a couple of hours, this is what I came up with, and it looks pretty solid.

This solution uses Spring's lookup method injection to lazily load the swing Gui on the EDT.

IOnDemandBeanGetter.java
This is the interface Spring will use for the method injection

public interface IOnDemandBeanGetter {
    public IOnDemandBean getBean();
}

IOnDemandBean.java
Marker interface returned by IOnDemandBeanGetter

public interface IOnDemandBean {}

GuiOnEdtUtility.java
This utility uses an injected IOnDemandBeanGetter to get the bean and load it on the EDT.

public class GuiOnEdtUtility {
    private final IOnDemandBeanGetter beanGetter;

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

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

MainGui.java
This contains the swing components, will be returned by IOnDemandBeanGetter, and loaded on the EDT by GuiOnEdtUtility. You can inject any components, which I did not do for simplicity, but they all must have 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"/>

Update...
This is how MainGui.java would look if it inherited from JFrame, and it works the same

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);
    }
}
chutcher
  • 586
  • 6
  • 11