I'm working on a java desktop app using spring boot and javaFx.
it's a crud app so I'm working with mysql database
I want the controllers to be both spring boot and javaFx controllers, and for that I used setControllerFactory()
.
here is my application
Application:
package com.gi.quizui;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
javafx.application.Application.launch(QuizApplication.class, args);
}
}
QuizApplication:
package com.gi.quizui;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ConfigurableApplicationContext;
public class QuizApplication extends Application {
private ConfigurableApplicationContext applicationContext;
@Override
public void init() {
applicationContext = new SpringApplicationBuilder(com.gi.quizui.Application.class).run();
}
@Override
public void start(Stage stage) {
applicationContext.publishEvent(new StageReadyEvent(stage));
}
@Override
public void stop() {
applicationContext.close();
Platform.exit();
}
class StageReadyEvent extends ApplicationEvent {
public StageReadyEvent(Stage stage) {
super(stage);
}
public ConfigurableApplicationContext getAppContext() {
return applicationContext;
}
public Stage getStage() {
return ((Stage) getSource());
}
}
}
and stage initializer:
package com.gi.quizui;
import com.gi.quizui.QuizApplication.StageReadyEvent;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class StageInitializer implements ApplicationListener<StageReadyEvent> {
@Value("classpath:fxml/quiz.fxml")
private Resource quizResource;
public StageInitializer(@Value("${spring.application.ui.title}") String applicationTitle) {
this.applicationTitle = applicationTitle;
}
private String applicationTitle;
@Override
public void onApplicationEvent(StageReadyEvent event) {
Stage stage = event.getStage();
ConfigurableApplicationContext springContext = event.getAppContext();
try {
FXMLLoader fxmlLoader = new FXMLLoader(quizResource.getURL());
fxmlLoader.setControllerFactory(springContext::getBean);
Parent parent = fxmlLoader.load();
stage.setScene(new Scene(parent, 800, 600));
stage.setTitle(applicationTitle);
stage.show();
} catch (IOException e) {
throw new RuntimeException();
}
}
}
I keep getting this error : (edit)
Exception in Application start method
2021-01-16 15:37:15.286 INFO 8652 --- [lication Thread] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2021-01-16 15:37:15.320 INFO 8652 --- [lication Thread] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
2021-01-16 15:37:15.343 INFO 8652 --- [lication Thread] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: java.lang.RuntimeException: javafx.fxml.LoadException:
/C:/Users/FacilOrdi/IdeaProjects/quizManagement/target/classes/fxml/quiz.fxml:7
at com.gi.quizui.StageInitializer.onApplicationEvent(StageInitializer.java:40)
at com.gi.quizui.StageInitializer.onApplicationEvent(StageInitializer.java:16)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:426)
at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:383)
at com.gi.quizui.QuizApplication.start(QuizApplication.java:20)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
... 1 more
Caused by: javafx.fxml.LoadException:
/C:/Users/FacilOrdi/IdeaProjects/quizManagement/target/classes/fxml/quiz.fxml:7
at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2625)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2603)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2466)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2435)
at com.gi.quizui.StageInitializer.onApplicationEvent(StageInitializer.java:35)
... 16 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.gi.controllers.QuizController' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1177)
at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:938)
at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:980)
at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:227)
at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:752)
at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2722)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2552)
... 19 more
controller:
package com.gi.controllers;
import org.springframework.stereotype.Component;
@Component
public class QuizController {
}
I don't understand why it is not working, I've found related questions but I don't get how it works. It would be very helpful if there are other alternatives to do this.
In order to be aware of classes which have annotations such as @Component
, etc., Spring Boot needs to scan through part of the classpath at startup time. By default it will scan the package containing the class you pass to SpringApplicationBuilder
(Application
in your case) and all subpackages.
So with the way you have things set up, it will scan com.gi.quizui
and all subpackages.
Your QuizController
class is not in a sub-package of com.gi.quizui
; it is in com.gi.controllers
. Consequently it won't be found.
The recommendation in the Spring Boot documentation is actually to have the Application
class in the highest-level package that contains classes or resources, which is likely com.gi
in your case. If you move that class to that package, it should fix the issue.
Alternatively, you can override the default behavior using the @ComponentScan
annotation:
package com.gi.quizui;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan ;
@SpringBootApplication
@ComponentScan(basePackages={"com.gi.quizui","com.gi.controllers"})
public class Application {
public static void main(String[] args) {
javafx.application.Application.launch(QuizApplication.class, args);
}
}
A typesafe alternative is to use the basePackageClasses
parameter instead of basePackages
:
package com.gi.quizui;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan ;
import com.gi.controllers.QuizController ;
@SpringBootApplication
@ComponentScan(basePackageClasses={Application.class, QuizController.class})
public class Application {
public static void main(String[] args) {
javafx.application.Application.launch(QuizApplication.class, args);
}
}