如何配置Spring Boot与Swing应用程序

26

我希望使用spring-boot-starter-data-jpa功能来创建一个非Web应用程序。 在52.4文档中说:

您希望作为业务逻辑运行的应用程序代码可以实现为CommandLineRunner,并作为@Bean定义放入上下文中。

我的AppPrincipalFrame看起来像:

@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();
            }
        }
    });
}

引导应用程序类看起来像:

@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);
  }
}

但并不起作用。有没有人有关于这个的例子?

进行了编辑,并添加了异常。

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 

敬礼。


“不起作用”的具体表现是什么?有异常吗?日志呢? - Dave Syer
@DaveSyer 是的,已添加了摘要异常。 - Dapaldo
Spring调用了您的构造函数,但失败了。看起来构造函数将被调用两次(一次是由Spring创建您的@Component,另一次是在其自己的run()方法中)。这有点奇怪,也许您并没有打算其中之一? - Dave Syer
不确定,但它只发生在我扩展swing JComponent时,如果将其移除,则像普通的autowired类一样正常工作。我尝试传递JVM参数-Djava.awt.headless=true,但没有起作用。 - Dapaldo
2
我认为我已经解决了。Application类也必须继承自JFrame。 - Dapaldo
我开发了一个基于Spring Boot和Swing的GUI应用程序,用于自己的目的。你可以在https://github.com/raydac/ravikoodi-server上查看。 在开发过程中,主要是确保Swing操作在Swing线程中调用。 - Igor Maznitsa
4个回答

41

你提出了这个问题已经有一段时间了,但我在迁移一个旧项目时遇到了同样的问题,并想出了另一种更清晰的方法来解决它。

你可以使用new SpringApplicationBuilder(Main.class).headless(false).run(args);代替SpringApplication.run(Application.class, args);,无需让你的Application类继承自JFrame。因此代码可能如下所示:

@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);
}

1
谢谢您的回复,@Rumal。我会尝试您的解决方案。 - Dapaldo
1
对于.headless(false)的注释点赞。我一直在遇到java.awt.HeadlessException错误,花了一些时间才发现这是由于Spring Boot应用程序引起的。 - axiopisty
即使我遇到了相同的异常,我也在使用Spring Boot应用程序与Swings集成。我已经尝试了几乎所有的选项,但都没有起作用。你有解决方案吗? - Renukeswar

17

Swing应用程序必须放置在Swing事件队列中。不这样做是一个严重的错误。

所以正确的方法:

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);
    });
}

此外,我们可以只使用@SpringBootApplication注解。

@SpringBootApplication
public class SwingApp extends JFrame {

参考我的Spring Boot Swing集成教程,获取完整的可工作示例。


我正在使用Swing工作遗留系统,我习惯于使用Spring Boot。我想将它们结合起来。EDT的东西是我难以掌握的。我找到了几个swing + spring boot示例,但它们没有使用EDT进行swing,或者它们以不同的方式进行操作。例如这个 https://github.com/kemitix/spring-boot-awt-gui-demo。此外,您的示例已经通过容器创建了对SwingApp的引用,因此只有setVisible(true)在EDT中运行,据我所见。也许这是正确的。 - Avec
2
有一种方法可以测试我们是否在EDT中:EventQueue.isDispatchThread()。我已经测试过它,对于我的示例它返回true - Jan Bodnar

6
另一个简单而优雅的解决方案是禁用无头属性,如此处所示。您需要在创建/显示JFrame之前执行此操作。
System.setProperty("java.awt.headless", "false"); //Disables headless

因此,对我有效的方法是:

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);
});

MyClass.class中的注释(我不清楚它们是否起到任何作用):

@SpringBootApplication
@EnableWebMvc

0

我发现解决方案非常简单(先运行Spring服务器,然后再运行Swing)

//@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,再调用 Spring 服务器运行

//@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);
    }
}

请原谅我的无知,但这个答案与其他答案有何不同? - Abra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接