spring-bootjavafxfxgl

Is there a way to launch FXGL with SpringBoot?


I need to launch application with fxgl animations with springboot to utilize it's functionality.

I dont really know how to do it.

I tried putting initialization into different init() methods of the inheritor of the GameApplication.

I tried it like this:

    @Override
    protected void onPreInit() {
        context = SpringApplication.run(getClass(), savedArgs);
        context.getAutowireCapableBeanFactory().autowireBean(this);
        super.onPreInit();
    }

Or tried to mimic web version:

public class App extends Application {
    private ConfigurableApplicationContext applicationContext;

    @Override
    public void init() {
        String[] args = getParameters().getRaw().toArray(new String[0]);
        this.applicationContext = new SpringApplicationBuilder()
                .sources(DiplomaBaseApplication.class)
                .run(args);
    }

    @Override
    public void stop() {
        this.applicationContext.close();
        Platform.exit();
    }

    @Override
    public void start(Stage stage) {
        GameApplication gameApplication = new SimulationApplication();
        GameApplication.embeddedLaunch(gameApplication);
    }
}
@SpringBootApplication
public class DiplomaBaseApplication {

    public static void main(String[] args) {
        Application.launch(App.class, args);
    }

}

Using javafx-weaver-spring-boot-starter.

The application started, but spring initialization ended to early end spring features and beans doesn't work:

22:08:38.879 [JavaFX Application Thread] INFO  Engine               - FXGL-11.17 (16.07.2021 15.46) on WINDOWS (J:11.0.10 FX:16)
22:08:38.879 [JavaFX Application Thread] INFO  Engine               - Source code and latest versions at: https://github.com/AlmasB/FXGL
22:08:38.880 [JavaFX Application Thread] INFO  Engine               -              Join the FXGL chat at: https://gitter.im/AlmasB/FXGL
22:08:39.418 [FXGL Background Thread 1 ] INFO  FXGLApplication      - FXGL initialization took: 0,325 sec

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.6.7)

2022-05-13 22:08:39.900  INFO 31948 --- [lication Thread] o.s.boot.SpringApplication               : Starting application using Java 11.0.10 on DESKTOP-2DKK20I with PID 31948 (started by pro56 in C:\Users\pro56\Desktop\Course)
2022-05-13 22:08:39.903  INFO 31948 --- [lication Thread] o.s.boot.SpringApplication               : No active profile set, falling back to 1 default profile: "default"
2022-05-13 22:08:39.985  INFO 31948 --- [lication Thread] o.s.boot.SpringApplication               : Started application in 0.425 seconds (JVM running for 1.813)
22:08:40.056 [FXGL Background Thread 1 ] INFO  FXGLApplication      - Game initialization took: 0,024 sec
22:08:40.852 [FXGL Background Thread 2 ] INFO  UpdaterService       - Your current version:  11.17
22:08:40.852 [FXGL Background Thread 2 ] INFO  UpdaterService       - Latest stable version: 17.1


Solution

  • I'm not exactly sure why this did not work for you. I tried it and it worked fine for me.

    This warning is generated:

    Unsupported JavaFX configuration: classes were loaded from 'unnamed module @307f6b8c'
    

    I guess that is just how fxgl works, it runs JavaFX off the classpath in an unsupported configuration by default. I see from the FXGL documentation that it can be used as a module, so I guess in that configuration it will run a supported configuration using JavaFX modules rather than off the classpath. However, SpringBoot isn't currently coded to use the modulepath (that will happen with SpringBoot 3, I believe). So, for now, it is probably best to run everything in the unsupported configuration as demonstrated by this example.

    Although in the example I placed a main method in the DemoSpringApplication for testing, the actual main class to run is not that one, it is instead the BasicSpringGameApp which extends the FXGL GameApplication.

    The BasicSpringGameApp will call the SpringApplication static method to run the Spring application (which will create an instance of the Spring application).

    The Spring application is separate from the FXGL GameApplication (which is internally separate from the JavaFX Application), so all of those things have different instances (just one of each), applying a separation of concerns.

    fxgl screenshot

    src/main/java/com/example/glboot/BasicSpringGameApp.java

    Extends the FXGL GameApplication. The FXGL GameApplication will (internally) launch a JavaFX application. This example will also run a spring application so that spring services will be available. The spring context is autowired into the GameApplication class so that spring services are available for use within the GameApplication class. The use of a Spring inject service is demonstrated by making calls to the UserService, which is a Spring service.

    package com.example.glboot;
    
    import com.almasb.fxgl.app.GameApplication;
    import com.almasb.fxgl.app.GameSettings;
    import com.almasb.fxgl.dsl.FXGL;
    import javafx.scene.control.Label;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
    import org.springframework.boot.WebApplicationType;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.context.ConfigurableApplicationContext;
    
    public class BasicSpringGameApp extends GameApplication {
    
        private ConfigurableApplicationContext springContext;
    
        @Autowired
        UserService userService;
    
        @Override
        protected void initSettings(GameSettings settings) {
            settings.setWidth(200);
            settings.setHeight(150);
            settings.setTitle("Game App");
    
            springContext =
                    new SpringApplicationBuilder(DemoSpringApplication.class)
                            .web(WebApplicationType.NONE)
                            .run();
    
            springContext
                    .getAutowireCapableBeanFactory()
                    .autowireBeanProperties(
                            this,
                            AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE,
                            true
                    );
        }
    
        @Override
        protected void initGame() {
            String welcomeText =
                    "hello, " + userService.getUsername() + "\n" + userService.getWelcomeMessage();
    
            FXGL.entityBuilder()
                    .at(50, 50)
                    .view(new Label(welcomeText))
                    .buildAndAttach();
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    src/main/resources/DemoSpringApplication.java

    SpringBoot application, configures and starts up a spring services via the SpringBoot framework.

    package com.example.glboot;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class DemoSpringApplication {
        public static void main(String[] args) {
            SpringApplication.run(DemoSpringApplication.class, args);
        }
    }
    

    src/main/java/com/example/glboot/UserService.java

    An example user information spring service with some injected values from the spring context and application configuration properties.

    package com.example.glboot;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserService {
    
        @Value("${user.name}")
        private String username;
    
        @Value("${welcome.message}")
        private String welcomeMessage;
    
        public String getUsername() {
            return username;
        }
    
        public String getWelcomeMessage() {
            return welcomeMessage;
        }
    }
    

    src/main/resoruces/application.properties

    Spring configuration properties.

    welcome.message=Welcome to FXGL Boot
    

    pom.xml

    Maven project file.

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.6.7</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <groupId>com.example</groupId>
        <artifactId>glboot</artifactId>
        <version>1.0-SNAPSHOT</version>
        <name>glboot</name>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-controls</artifactId>
                <version>18.0.1</version>
            </dependency>
            <dependency>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-fxml</artifactId>
                <version>18.0.1</version>
            </dependency>
            <dependency>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-media</artifactId>
                <version>18.0.1</version>
            </dependency>
    
            <dependency>
                <groupId>com.github.almasb</groupId>
                <artifactId>fxgl</artifactId>
                <version>17</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.openjfx</groupId>
                        <artifactId>*</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.9.0</version>
                    <configuration>
                        <source>18</source>
                        <target>18</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    </project>
    

    console output

    /Users/js732745/Library/Java/JavaVirtualMachines/openjdk-18.0.1.1/Contents/Home/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=59335:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/js732745/dev/glboot/target/classes:/Users/js732745/.m2/repository/org/openjfx/javafx-controls/18.0.1/javafx-controls-18.0.1.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-controls/18.0.1/javafx-controls-18.0.1-mac.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-graphics/18.0.1/javafx-graphics-18.0.1.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-graphics/18.0.1/javafx-graphics-18.0.1-mac.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-base/18.0.1/javafx-base-18.0.1.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-base/18.0.1/javafx-base-18.0.1-mac.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-fxml/18.0.1/javafx-fxml-18.0.1.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-fxml/18.0.1/javafx-fxml-18.0.1-mac.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-media/18.0.1/javafx-media-18.0.1.jar:/Users/js732745/.m2/repository/org/openjfx/javafx-media/18.0.1/javafx-media-18.0.1-mac.jar:/Users/js732745/.m2/repository/com/github/almasb/fxgl/17/fxgl-17.jar:/Users/js732745/.m2/repository/com/github/almasb/fxgl-core/17/fxgl-core-17.jar:/Users/js732745/.m2/repository/com/gluonhq/attach/audio/4.0.9/audio-4.0.9.jar:/Users/js732745/.m2/repository/com/github/almasb/fxgl-io/17/fxgl-io-17.jar:/Users/js732745/.m2/repository/com/gluonhq/attach/storage/4.0.9/storage-4.0.9.jar:/Users/js732745/.m2/repository/com/gluonhq/attach/util/4.0.9/util-4.0.9.jar:/Users/js732745/.m2/repository/com/github/almasb/fxgl-entity/17/fxgl-entity-17.jar:/Users/js732745/.m2/repository/com/github/almasb/fxgl-scene/17/fxgl-scene-17.jar:/Users/js732745/.m2/repository/com/github/almasb/fxgl-gameplay/17/fxgl-gameplay-17.jar:/Users/js732745/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.13.2/jackson-annotations-2.13.2.jar:/Users/js732745/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.13.2.1/jackson-databind-2.13.2.1.jar:/Users/js732745/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.13.2/jackson-core-2.13.2.jar:/Users/js732745/.m2/repository/com/gluonhq/attach/lifecycle/4.0.9/lifecycle-4.0.9.jar:/Users/js732745/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib/1.5.32/kotlin-stdlib-1.5.32-modular.jar:/Users/js732745/.m2/repository/org/jetbrains/annotations/13.0/annotations-13.0.jar:/Users/js732745/.m2/repository/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.21/kotlin-stdlib-common-1.6.21.jar:/Users/js732745/.m2/repository/org/springframework/boot/spring-boot-starter/2.6.7/spring-boot-starter-2.6.7.jar:/Users/js732745/.m2/repository/org/springframework/boot/spring-boot/2.6.7/spring-boot-2.6.7.jar:/Users/js732745/.m2/repository/org/springframework/spring-context/5.3.19/spring-context-5.3.19.jar:/Users/js732745/.m2/repository/org/springframework/spring-aop/5.3.19/spring-aop-5.3.19.jar:/Users/js732745/.m2/repository/org/springframework/spring-beans/5.3.19/spring-beans-5.3.19.jar:/Users/js732745/.m2/repository/org/springframework/spring-expression/5.3.19/spring-expression-5.3.19.jar:/Users/js732745/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.6.7/spring-boot-autoconfigure-2.6.7.jar:/Users/js732745/.m2/repository/org/springframework/boot/spring-boot-starter-logging/2.6.7/spring-boot-starter-logging-2.6.7.jar:/Users/js732745/.m2/repository/ch/qos/logback/logback-classic/1.2.11/logback-classic-1.2.11.jar:/Users/js732745/.m2/repository/ch/qos/logback/logback-core/1.2.11/logback-core-1.2.11.jar:/Users/js732745/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.17.2/log4j-to-slf4j-2.17.2.jar:/Users/js732745/.m2/repository/org/apache/logging/log4j/log4j-api/2.17.2/log4j-api-2.17.2.jar:/Users/js732745/.m2/repository/org/slf4j/jul-to-slf4j/1.7.36/jul-to-slf4j-1.7.36.jar:/Users/js732745/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar:/Users/js732745/.m2/repository/org/springframework/spring-core/5.3.19/spring-core-5.3.19.jar:/Users/js732745/.m2/repository/org/springframework/spring-jcl/5.3.19/spring-jcl-5.3.19.jar:/Users/js732745/.m2/repository/org/yaml/snakeyaml/1.29/snakeyaml-1.29.jar:/Users/js732745/.m2/repository/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar com.example.glboot.BasicSpringGameApp
    
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::                (v2.6.7)
    
    2022-05-13 15:54:30.381  INFO 32226 --- [           main] com.example.glboot.BasicSpringGameApp    : Starting BasicSpringGameApp using Java 18.0.1.1 on C02ZX2NFMD6T with PID 32226 (/Users/js732745/dev/glboot/target/classes started by js732745 in /Users/js732745/dev/glboot)
    2022-05-13 15:54:30.384  INFO 32226 --- [           main] com.example.glboot.BasicSpringGameApp    : No active profile set, falling back to 1 default profile: "default"
    2022-05-13 15:54:30.772  INFO 32226 --- [           main] com.example.glboot.BasicSpringGameApp    : Started BasicSpringGameApp in 0.675 seconds (JVM running for 0.988)
    2022-05-13 15:54:30.835  WARN 32226 --- [JavaFX-Launcher] javafx                                   : Unsupported JavaFX configuration: classes were loaded from 'unnamed module @307f6b8c'
    15:54:31.118 [JavaFX Application Thread] INFO  Engine               - FXGL-17 (31.12.2021 18.16) on MAC (J:18.0.1.1 FX:18.0.1)
    15:54:31.119 [JavaFX Application Thread] INFO  Engine               - Source code and latest versions at: https://github.com/AlmasB/FXGL
    15:54:31.119 [JavaFX Application Thread] INFO  Engine               -       Ask questions and discuss at: https://github.com/AlmasB/FXGL/discussions
    15:54:31.119 [JavaFX Application Thread] INFO  Engine               -              Join the FXGL chat at: https://gitter.im/AlmasB/FXGL
    15:54:31.534 [FXGL Background Thread 1 ] WARN  FXGL.DefaultMenu     - FXGLDefaultMenu is not designed for resolutions < 800x600
    15:54:31.686 [FXGL Background Thread 1 ] INFO  FXGLApplication      - FXGL initialization took: 0.399 sec
    15:54:31.757 [FXGL Background Thread 4 ] INFO  FXGLApplication      - Game initialization took: 0.005 sec
    15:54:32.035 [FXGL Background Thread 2 ] INFO  UpdaterService       - Your current version:  17
    15:54:32.036 [FXGL Background Thread 2 ] INFO  UpdaterService       - Latest stable version: 17.1