How to configure spring-boot with swing application
Asked Answered
R

4

27

I'd like using spring-boot-starter-data-jpa features to create a non-web aplication. In the 52.4 documentation says:

Application code that you want to run as your business logic can be implemented as a CommandLineRunner and dropped into the context as a @Bean definition.

My AppPrincipalFrame looks like:

@Component
public class AppPrincipalFrame extends JFrame implements CommandLineRunner{

private JPanel contentPane;

@Override
public void run(String... arg0) throws Exception {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                AppPrincipalFrame frame = new AppPrincipalFrame();
                frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

And boot application class looks like:

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {  
  public static void main(String[] args) {
   ApplicationContext context = SpringApplication.run(Application.class, args);
   AppPrincipalFrame appFrame = context.getBean(AppPrincipalFrame.class);
  }
}

But does not work. Anybody have a sample about this?

Edited and exception added.

Exception in thread "main" org.springframework.beans.factory.BeanCreationException:      Error creating bean with name 'appPrincipalFrame'.

Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [es.adama.swing.ui.AppPrincipalFrame]: Constructor threw exception; nested exception is java.awt.HeadlessException 

Regards.

Rothberg answered 4/4, 2014 at 13:20 Comment(6)
What is it that "doesn't work"? Is there an exception? Logs?Christianson
@DaveSyer yes, summarized exception added.Rothberg
Spring called your constructor and if failed. It looks like that constructor is going to be called twice (once by Spring to create your @Component and once in its own run() method). Slightly bizarre, so maybe you didn't intend one of those?Christianson
Not sure but it occurs when I extends of swing JComponent only, if it is removed works fine like a normal autowired class. I've tried to pass JVM argument -Djava.awt.headless=true, but didn't work.Rothberg
I think I've solved. The Application class must also inherit from JFrameRothberg
I have developed a spring boot swing GUI based application for own purposes, you can take a look at github.com/raydac/ravikoodi-server main thing during development spring boot and Swing to be sure that swing actions are called in swing threadCasiecasilda
A
42

It's been a while since you posted the question, but I run out with the same problem in a old project that I'm migrating and figured a different way, I think that more clear, to make the things work.

Instead of using SpringApplication.run(Application.class, args); you can use: new SpringApplicationBuilder(Main.class).headless(false).run(args); and it isn't necessary to make toy Application class extends from JFrame. Therefore the code could look like:

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application {  
    public static void main(String[] args) {
    ConfigurableApplicationContext context = new SpringApplicationBuilder(Application.class).headless(false).run(args);
    AppPrincipalFrame appFrame = context.getBean(AppPrincipalFrame.class);
}
Alten answered 3/5, 2015 at 0:22 Comment(3)
+1 for the note about .headless(false). I was getting a java.awt.HeadlessException. It took me a while to discover this is because of the spring boot application.Ipa
Even i am running with same exception.I integrated Spring boot application with Swings. i tried all most all options but none of them worked out. Did u get solution for this.Template
On with his head!Rothwell
P
17

The Swing application must be placed on the Swing Event Queue. Not doing so is a serious mistake.

So the correct way to do it:

public static void main(String[] args) {

    ConfigurableApplicationContext ctx = new SpringApplicationBuilder(SwingApp.class)
            .headless(false).run(args);

    EventQueue.invokeLater(() -> {
        SwingApp ex = ctx.getBean(SwingApp.class);
        ex.setVisible(true);
    });
}

In addition, we can just use the @SpringBootApplication annotation.

@SpringBootApplication
public class SwingApp extends JFrame {

See my Spring Boot Swing integration tutorial for a full working example.

Pig answered 28/4, 2017 at 16:19 Comment(2)
I am working a legacy system with Swing and I am used to work with Spring Boot. I would like to combine them. The EDT stuff is something I am struggeling to grasp. I found several swing + spring boot samples but they are not using EDT for swing or they are doing it in different ways. Like this one i.e. github.com/kemitix/spring-boot-awt-gui-demo. Also your example gets an reference to SwingApp allready created by the container so only the setVisible(true) runs in EDT as I see it. That may be right however.Interpolate
There is a method to test if we are in the EDT: EventQueue.isDispatchThread(). I have tested it and it returns true for my example.Pig
T
6

Another simple and elegant solution is to disable headless property as shown here. You will have to do it though, right before you create/show the JFrame.

System.setProperty("java.awt.headless", "false"); //Disables headless

So, the thing that worked for me:

SpringApplication.run(MyClass.class, args);
System.setProperty("java.awt.headless", "false");
SwingUtilities.invokeLater(() -> {
    JFrame f = new JFrame("myframe");
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setVisible(true);
});

Annotations in MyClass.class (I am not aware if they play any role):

@SpringBootApplication
@EnableWebMvc
Trying answered 19/1, 2019 at 9:10 Comment(0)
R
0

I have found the solution very simple (spring server run first, swing run later)

//@Component
public class SwingApp extends JFrame {
vv...
}

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext ctx = new SpringApplicationBuilder(Application.class)
                .headless(false).run(args);

        SwingApp sw = new SwingApp();
        sw.setVisible(true);    
    }
}

swing run first, call spring server run later

//@Component
public class SwingApp extends JFrame {
    
    vv...

    private void jButtonRunSpringServer(java.awt.event.ActionEvent evt){
        String [] args = {"abc","xyz"};
        Application.runSpringServer(args);
    }
}


@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SwingApp sw = new SwingApp();
        sw.setVisible(true);
    }

    public static void runSpringServer(String[] args) {
        /*
        ConfigurableApplicationContext ctx = new SpringApplicationBuilder(Application.class)
                .headless(false).run(args);
        */
        SpringApplication.run(Application.class, args);
    }
}
Rodomontade answered 17/11, 2020 at 6:12 Comment(1)
Pardon my ignorance, but how is this answer different from any of the other answers?Underwood

© 2022 - 2024 — McMap. All rights reserved.