Build a Java desktop application with Spring Boot and JavaFX

Desktop software earns its place when a business needs fixed workstations, fast data entry or offline access. Combining Spring Boot with JavaFX delivers a polished client while keeping business rules and external integrations in testable Java code.

Spring Boot brings Spring’s dependency injection and lifecycle management. JavaFX handles windows, controls and rendering. Together, they separate the interface from services without making the program run a web server or expose unnecessary network endpoints.

This guide walks through a Maven-based project, Spring-managed JavaFX controllers and packaging choices. The examples suit internal tools used across Australia, from metropolitan offices to regional operations.

Set up the Maven project and dependencies

Start with a recent Spring Boot release, JDK 21 and matching OpenJFX. JavaFX is no longer bundled with standard JDK builds, so add org.openjfx:javafx-controls and org.openjfx:javafx-fxml as Maven dependencies. Programmatic layouts need only the controls module.

Use spring-boot-starter, not spring-boot-starter-web: a desktop process does not need an embedded servlet container. The starter provides the Spring context, configuration properties and component scanning used by the UI. Pin and configure javafx-maven-plugin with the application’s main class for mvn javafx:run.

Keep the application class in a root package such as com.example.desktop, with controllers and services beneath it. This lets @SpringBootApplication discover beans without broad component-scan declarations.

Start Spring without opening a web server

Keep JavaFX in charge of startup. The main method calls launch(args), and init() starts Spring with WebApplicationType.NONE. Calling SpringApplication.run() before launch() risks creating a second, unmanaged context.

@SpringBootApplication
public class DesktopApplication extends Application {
    private ConfigurableApplicationContext context;

    @Override
    public void init() {
        context = new SpringApplicationBuilder(DesktopApplication.class)
                .web(WebApplicationType.NONE)
                .run();
    }

    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader loader = new FXMLLoader(
                DesktopApplication.class.getResource("/fxml/main.fxml"));
        loader.setController(context.getBean(MainController.class));

        Parent root = loader.load();
        stage.setScene(new Scene(root, 900, 600));
        stage.setTitle("JavaWhizz Desktop");
        stage.show();
    }

    @Override
    public void stop() {
        if (context != null) {
            context.close();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Closing the context in stop() releases pools and background workers. Keep launch() as the only entry point; SpringApplication.run() should only initialise the Spring context.

Connect Spring beans to JavaFX views

With FXML, the controller must exist before loader.load() resolves the view. Fetch it from Spring and pass it to setController(...). Omit fx:controller from the FXML file, otherwise JavaFX may report a duplicate controller.

Place the view under src/main/resources/fxml. Annotate MainController with @Controller and give it a constructor that accepts a service. Use @FXML for view wiring and event handlers; keep business decisions in services. Unit tests can then supply a fake service without starting JavaFX.

If you build the interface entirely in Java, inject the controller into the view constructor instead. Avoid static singletons and global application state: they make parallel tests brittle and hide missing dependencies. Run slow calls in a background task and update controls on the JavaFX thread.

Model workflows for operational teams

A useful internal tool often starts with a narrow job: searching records, capturing payments or importing a CSV. Keep these jobs behind Spring services, with JDBC or Spring Data repositories beneath them. Controllers should translate UI events into service calls instead of writing SQL.

Add spring-boot-starter-data-jpa for Spring Data and Hibernate, or spring-boot-starter-jdbc for direct SQL. An embedded database simplifies early development; PostgreSQL or MySQL may suit a shared deployment. Use transactions for multi-row updates and schema migrations for predictable changes.

Desktop clients remain practical for Australian teams using managed Windows devices or working beyond reliable metro connectivity. A Sydney support office might run a fixed workstation tool, while an operation around Broome can work through an outage and synchronise later. Offline-first design or safe retry queues matter more than mimicking a browser.

Apply Australian business and language conventions

Format business data for local use. Represent Australian dollars with BigDecimal and currency code AUD, never floating-point values. If the application issues taxable invoices, implement the 10% Australian GST and display the supplier’s ABN; keep tax treatment explicit rather than hard-coded into every screen.

Use dd/MM/yyyy for dates and configure the appropriate IANA zone, such as Australia/Sydney or Australia/Perth. Daylight-saving changes affect Sydney, while Perth uses Western Standard Time year-round. A single hard-coded offset will eventually produce incorrect timestamps.

Language can be friendly without becoming informal. Australian workplace conversation often uses “no worries”, and “arvo” may appear in scheduling discussions. Keep labels plain, using Australian spellings such as “organisation” and “authorisation” when they suit the audience.

Practical release and support recommendations

For distribution, bundle the Java and JavaFX runtime so users need no separate installation. Test native builds on supported Windows and macOS versions, including Apple Silicon. Use javafx-maven-plugin during development; jpackage suits controlled installers.

Separate service logic from controllers and verify that Spring can create the application context. A headless CI job can cover business rules, while a desktop agent can smoke-test window loading and button actions. Log useful diagnostics locally without exposing customer records.

An independent review can help before rollout, particularly when packaging and authentication need extra attention; some teams engage Rewans consulting for that support.