Startup of an JPA powered Application increases load time for ApplicationContext. While you can make things faster by not checking or creating a database scheme, e.g. by setting hibernate.hbm2ddl.auto=none
, this is not the best option.
It is by design that the primary stage is shown after the ApplicationContext is loaded, since it should be able to be dependency injected.
The best practice I recommend is using a splash screen while loading the ApplicationContext. It's a bit tricky, since you have separate Threads, but roughly it looks like this:
Create a splash window
public class Splash {
private static final int SPLASH_WIDTH = 200;
private static final int SPLASH_HEIGHT = 200;
private final Parent parent;
private final Stage stage;
public Splash() {
this.stage = new Stage();
stage.setWidth(SPLASH_WIDTH);
stage.setHeight(SPLASH_HEIGHT);
Label progressText = new Label("Application loading ...");
VBox splashLayout = new VBox();
splashLayout.setAlignment(Pos.CENTER);
splashLayout.getChildren().addAll(progressText);
progressText.setAlignment(Pos.CENTER);
splashLayout.setStyle(
"-fx-padding: 5; " +
"-fx-background-color: white; " +
"-fx-border-width:5; " +
"-fx-border-color: white;"
);
splashLayout.setEffect(new DropShadow());
this.parent = splashLayout;
}
public void show() {
Scene splashScene = new Scene(parent);
stage.initStyle(StageStyle.UNDECORATED);
final Rectangle2D bounds = Screen.getPrimary().getBounds();
stage.setScene(splashScene);
stage.setX(bounds.getMinX() + bounds.getWidth() / 2 - SPLASH_WIDTH / 2.0);
stage.setY(bounds.getMinY() + bounds.getHeight() / 2 - SPLASH_HEIGHT / 2.0);
stage.show();
}
public void hide() {
stage.toFront();
FadeTransition fadeSplash = new FadeTransition(Duration.seconds(0.3), parent);
fadeSplash.setFromValue(1.0);
fadeSplash.setToValue(0.0);
fadeSplash.setOnFinished(actionEvent -> stage.hide());
fadeSplash.play();
}
}
Initialize Application
public class SpringbootJavaFxApplication extends Application {
private ConfigurableApplicationContext context;
class ApplicationContextLoader extends Task<Void> {
private final Stage primaryStage;
ApplicationContextLoader(Stage primaryStage) {
this.primaryStage = primaryStage;
}
@Override
protected Void call() {
ApplicationContextInitializer<GenericApplicationContext> initializer =
context -> {
context.registerBean(Application.class, () -> SpringbootJavaFxApplication.this);
context.registerBean(Stage.class, () -> primaryStage);
context.registerBean(Parameters.class,
SpringbootJavaFxApplication.this::getParameters); // for demonstration, not really needed
};
SpringbootJavaFxApplication.this.context = new SpringApplicationBuilder()
.sources(JavaFxSpringbootDemo.class)
.initializers(initializer)
.run(getParameters().getRaw().toArray(new String[0]));
return null;
}
}
@Override
public void start(Stage primaryStage) {
var splash = new Splash();
splash.show();
final ApplicationContextLoader applicationContextLoader = new ApplicationContextLoader(primaryStage);
applicationContextLoader.stateProperty().addListener((observableValue, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
context.publishEvent(new StageReadyEvent(primaryStage));
splash.hide();
}
});
new Thread(applicationContextLoader).start();
}
@Override
public void stop() {
this.context.close();
Platform.exit();
}
}