Spring Boot Service always null after #Autowire in JavaFX Controller Class - java

I am currently developing an application that uses Spring Boot and JavaFX. I want to store email and password in H2 database. With a JUnit test I have already checked if the database is running and I have access to the Spring Boot repository. This works. However, I need to access the Spring Boot service from the JavaFX controller to be able to control the logic from the GUI and after I autowire the Spring Boot service in this class, a NullPointerException comes.
JavaFXController
#Component
#FxmlView("start.fxml")
#Service
public class JavaFXController {
#FXML private Label startLabel;
#FXML private Label settingsLabel;
#FXML private TextField emailAmazonMerchOnDemand;
#FXML private PasswordField passwordAmazonMerchOnDemand;
private Stage stage;
private Scene scene;
#Autowired
private AmazonMerchOnDemandService amazonMerchOnDemandService;
#FXML
public void openSettingsSceneFromStartScene() throws IOException {
Parent root = FXMLLoader.load(getClass().getClassLoader().getResource("settings.fxml"));
stage = (Stage)(startLabel.getScene().getWindow());
scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
#FXML
public void saveLogins() {
String emailAmazon = emailAmazonMerchOnDemand.getText();
String passwordAmazon = passwordAmazonMerchOnDemand.getText();
AmazonMerchOnDemand amazonMerchOnDemandLogin = new AmazonMerchOnDemand();
amazonMerchOnDemandLogin.setId(1L);
amazonMerchOnDemandLogin.setEmail(emailAmazon);
amazonMerchOnDemandLogin.setPassword(passwordAmazon);
amazonMerchOnDemandService.saveLogin(amazonMerchOnDemandLogin);
}
}
JavaFXApplication
#SpringBootApplication
public class JavaFXApplication extends Application {
private ConfigurableApplicationContext applicationContext;
#Override
public void init() throws Exception {
String[] args = getParameters().getRaw().toArray(new String[0]);
this.applicationContext = new SpringApplicationBuilder().sources(PrintOnDemandSellerViewApplication.class).run(args);
}
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader();
URL xmlUrl = getClass().getClassLoader().getResource("start.fxml");
loader.setLocation(xmlUrl);
loader.setController(applicationContext.getBean(JavaFXController.class));
Parent root = loader.load();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
#Override
public void stop() throws Exception {
this.applicationContext.close();
Platform.exit();
}
}
AmazonMerchOnDemandService
#Service
#Component
public class AmazonMerchOnDemandService {
public static WebDriver driver;
private static int amountOfSales;
#Autowired
private AmazonMerchOnDemandRepository amazonMerchOnDemandRepository;
public void saveLogin(AmazonMerchOnDemand amazonMerchOnDemand) {
amazonMerchOnDemandRepository.save(amazonMerchOnDemand);
}
public void chromeDriverSetUp() {
System.setProperty("webdriver.chrome.driver", "C:\\Program Files (x86)\\chromedriver.exe");
driver = new ChromeDriver();
driver.manage().window().maximize();
}
public void login() {
String email = amazonMerchOnDemandRepository.findById(1L).get().getEmail();
System.out.println("Email: " + email);
String password = amazonMerchOnDemandRepository.findById(1L).get().getPassword();
System.out.println("Password: " + password);
}
}
AmazonMerchOnDemandRepository
#Repository
public interface AmazonMerchOnDemandRepository extends CrudRepository <AmazonMerchOnDemand, Long> {
}
PrintOnDemandSellerViewApplicationTests
#SpringBootTest(classes = JavaFXApplication.class)
class PrintOnDemandSellerViewApplicationTests {
#Autowired
private AmazonMerchOnDemandRepository amazonMerchOnDemandRepository;
#Autowired
private AmazonMerchOnDemandSampleData amazonMerchOnDemandSampleData;
#BeforeEach
public void setupData() {
amazonMerchOnDemandSampleData.createAmazonMerchOnDemandLogin();
}
#Test
public void testHasAmazonMerchOnDemandLoginRightPasswordAndRightEmail() {
assertTrue(amazonMerchOnDemandRepository.existsById(1L));
Optional<AmazonMerchOnDemand> amazonMerchOnDemandOptional = amazonMerchOnDemandRepository.findById(1L);
assertFalse(amazonMerchOnDemandOptional.isEmpty());
AmazonMerchOnDemand amazonMerchOnDemandLogin = amazonMerchOnDemandOptional.get();
assertEquals("password", amazonMerchOnDemandLogin.getPassword());
assertEquals("test#gmail.com", amazonMerchOnDemandLogin.getEmail());
}
}
NullPointerException
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:77)
at jdk.internal.reflect.GeneratedMethodAccessor37.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at javafx.base#19/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:275)
at javafx.fxml#19/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:84)
at javafx.fxml#19/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1854)
... 29 more
Caused by: java.lang.NullPointerException
at com.example.printondemandsellerview.JavaFXController.saveLogins(JavaFXController.java:53)
... 40 more
I have tried several solutions from StackOverflow:
Dependency Injection in JavaFX
Spring Data Autowire in FXML Controller
Unfortunately, none of the solutions could help me. Do you have any other suggestions as to what the problem might be?

Related

how to configure javafx with springboot with more than 1 window?

I have problem.
This is my main:
#SpringBootApplication
public class MyAppSpringApplication extends Application {
public static ConfigurableApplicationContext springContext;
private FXMLLoader fxmlLoader;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception {
fxmlLoader.setLocation(getClass().getResource("/sample.fxml"));
Parent root = fxmlLoader.load();
stage.setTitle("Sample app");
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
#Override
public void stop() throws Exception {
springContext.stop();
}
#Override
public void init() throws Exception {
springContext = SpringApplication.run(MyAppSpringApplication.class);
fxmlLoader = new FXMLLoader();
fxmlLoader.setControllerFactory(springContext::getBean);
}
}
And my first window (sample.fxml) with samplecontroller and sampleservice works ok. But i create another dish-builder.fxml with their contoller and service, but when i try to use my service there, it doesnt work because of null in dishbuilderservice (albo doesnt work sampleservice in that new controller). I heard that i shound also use that:
public static ConfigurableApplicationContext springContext;
but i have no idea how should i use it. Sorry for my weak knowledge and english.
#Controller
public class DishBuilderController implements Initializable {
#Autowired
DishBuilderService dishBuilderService;
#Autowired
SampleService sampleService;
private void somefun(){
sampleService.somefunInService(); //here sampleService and
every other service has null.
}
Here is the moment when i open new dishBuilder window (its in SampleController):
#FXML
void addNoweOknoClicked(ActionEvent event) {
try {
Stage stage = (Stage)anchorPane.getScene().getWindow();
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("/dish-builder.fxml"));
AnchorPane root = fxmlLoader.load();
stage.setTitle("Sample app");
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}catch (IOException e){
e.printStackTrace();
}
}
When you load dish-builder.fxml you are not setting the controller factory on the FXMLLoader. This means the FXMLLoader is simply creating the controller by calling its no-arg constructor. Since the controller is not a spring-managed bean, spring cannot inject any components into it.
You need to set the controller factory, as you do when you load sample.fxml, so that the FXMLLoader will ask Spring to retrieve the controller from the application context.
A couple of points that are not strictly relevant to your question:
There is no need to expose the ApplicationContext as a public static field. You can inject it into any spring-managed beans that need access to it
It is not recommended to re-use FXMLLoaders. Therefore there's no point in making the FXMLLoader an instance variable.
The #Controller annotation is intended for web controllers in a Spring MVC application. These are quite different to controllers in the JavaFX sense. You should use a generic #Component annotation for JavaFX controllers.
In the event that you were to reload an FXML file, you would need a new controller instance. This means that if the controller is managed by Spring, it needs to have PROTOTYPE scope, instead of the default SINGLETON scope.
So you need:
#SpringBootApplication
public class MyAppSpringApplication extends Application {
private ConfigurableApplicationContext springContext;
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) throws Exception {
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setControllerFactory(springContext::getBean);
fxmlLoader.setLocation(getClass().getResource("/sample.fxml"));
Parent root = fxmlLoader.load();
stage.setTitle("Sample app");
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
#Override
public void stop() throws Exception {
springContext.stop();
}
#Override
public void init() throws Exception {
springContext = SpringApplication.run(MyAppSpringApplication.class);
}
}
Then your SampleController should look like
#Component
#Scope("prototype")
public class SampleController {
#Autowired
private ConfigurableApplicationContext springContext ;
#FXML
void addNoweOknoClicked(ActionEvent event) {
try {
Stage stage = (Stage)anchorPane.getScene().getWindow();
FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setControllerFactory(springContext::getBean);
fxmlLoader.setLocation(getClass().getResource("/dish-builder.fxml"));
AnchorPane root = fxmlLoader.load();
stage.setTitle("Sample app");
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}catch (IOException e){
e.printStackTrace();
}
}
}
and similarly
#Component
#Scope("prototype")
public class DishBuilderController implements Initializable {
#Autowired
DishBuilderService dishBuilderService;
#Autowired
SampleService sampleService;
private void somefun(){
// this should now work, since the controller is managed by Spring:
sampleService.somefunInService();
}
}

#FXML Annotation not working correctly

I'm currently working on a 'small' project with JavaFX. I used the SceneBuilder to create the first sketch of my GUI. it still needs some adjustment and styling but I wanted to see if it's working so far.
I have 2 hyperlinks on the GUI, if the user clicks one of them the default system-browser should open with a specific URL.
So far I got this:
Main.java:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws MalformedURLException, IOException {
DataBean dataBean= new DataBean(primaryStage);
Controller controller = new Controller(dataBean);
controller.show();
}
public static void main(String[] args) {
launch(args);
}
}
DataBean.java:
public class DataBean {
private Stage primaryStage;
public DataBean(Stage stage) {
primaryStage = stage;
}
public Stage getPrimaryStage() {
return primaryStage;
}
}
TestautomatView.java:
public class TestautomatView implements Initializable {
#FXML
private ComboBox<String> environmentCombo;
#FXML
private Hyperlink crhl;
#FXML
private Hyperlink help;
#Override
public void initialize(URL location, ResourceBundle resources) {
}
private Scene scene;
private BorderPane root;
public TestautomatView() throws MalformedURLException, IOException {
root = FXMLLoader.load(new URL(TestautomatView.class.getResource("Sample.fxml").toExternalForm()));
scene = new Scene(root);
}
public void show(Stage stage) {
stage.setTitle("CrossReport Testautomat");
stage.setScene(scene);
stage.show();
}
public ComboBox<String> getEnvironmentCombo() {
return environmentCombo;
}
public Hyperlink getCrhl() {
return crhl;
}
public Hyperlink getHelp() {
return help;
}
public Scene getScene() {
return scene;
}
}
In my controller I want to set the ActionHandler to the hyperlinks but it's not working because the getters in my view return null.
public class Controller {
private DataBean dataBean;
private TestautomatView view;
public Controller(DataBean databean) throws MalformedURLException, IOException {
this.dataBean = databean;
this.view = new TestautomatView();
setActionHandlers();
}
public void show() throws MalformedURLException, IOException {
view.show(dataBean.getPrimaryStage());
}
private void setActionHandlers() {
// setHyperlink(view.getCrhl(), "www.example.com");
// setHyperlink(view.getHelp(), "www.example2.com");
}
private void setHyperlink(Hyperlink hl, String uri) {
hl.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
//TODO - Open Default Browser
}
});
}
}
When I start my application, I can see the GUI but when I want to add the ActionHandlers I get a NullPointerException.
In the ´Sample.fxml´ file the hyperlinks are children of a HBox
<Hyperlink fx:id="crhl" text="Report" />
<Hyperlink fx:id="help" text="Help" />
But it's not just the hyperlinks even the ComboBox is null when I inspect my app in the debugger.
Where is my mistake?
The problem is that you are creating your controller manually by using new TestautomatView(). It must be created by FXMLLoader for annotations to work. You must also set fx:controller attribute in Sample.fxml to your controller (TestautomatView) fully qualified class name.
Example code:
FXMLLoader fl = new FXMLLoader(new URL(TestautomatView.class.getResource("Sample.fxml").toExternalForm()));
root = fl.load();
TestautomatView controller = fl.getController();
PS: You should rename your TestautomatView to TestautomatController. FXML file is your "view".
As pointed out in another answer, the issue is that you create an instance of TestautomatView "by hand". The default behavior of the FXMLLoader is to create an instance of the controller class specified in the FXML file, and use that instance as the controller. Consequently, you have two instances of TestautomatView: the one you created (and have a reference to), and the one that was created by the FXMLLoader. It is the second one that has the #FXML-annotated fields initialized.
You can change this default behavior by creating an FXMLLoader instance, and setting the controller on it directly. E.g. consider doing:
public class TestautomatView implements Initializable {
#FXML
private ComboBox<String> environmentCombo;
#FXML
private Hyperlink crhl;
#FXML
private Hyperlink help;
#Override
public void initialize(URL location, ResourceBundle resources) {
}
private Scene scene;
private BorderPane root;
public TestautomatView() throws MalformedURLException, IOException {
FXMLLoader loader = new FXMLLoader(TestautomatView.class.getResource("Sample.fxml"));
loader.setController(this);
root = loader.load();
scene = new Scene(root);
}
// etc...
}
Since you are directly setting the controller, you need to remove the fx:controller attribute from the Sample.fxml file for this to work.
You may also be interested in this pattern, which is quite similar (though not exactly the same) as what you are trying to do here.

JavaFX & Spring Boot - NPE

I'm still fighting with my issue. I want to use Spring Framework in order to incject dependencies and I have to use Spring boot to integrate both.
Unfortunately, in first view autowiring is run correctly, but if I go next Stage, I got still only Null Pointer Exception.
Thats main class:
#SpringBootApplication(scanBasePackages = "boxingchallenge")
public class BoxingChallengeApplication extends Application {
public ConfigurableApplicationContext springContext;
private Parent root;
public static Stage stage;
#Override
public void init() throws Exception {
springContext = SpringApplication.run(BoxingChallengeApplication.class);
springContext.getAutowireCapableBeanFactory().autowireBean(this);
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/FXML/start.fxml"));
fxmlLoader.setControllerFactory(springContext::getBean);
root = fxmlLoader.load();
}
#Override
public void start(Stage primaryStage) throws Exception {
stage = primaryStage;
primaryStage.setTitle("Boxing challenge");
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
#Override
public void stop() {
springContext.stop();
}
public static void main(String[] args) {
launch(BoxingChallengeApplication.class, args);
}
}
Here in first controller class autowiring run cool:
#Component
public class Start {
#FXML
public Button loadGame;
#FXML
public Button create;
#Autowired
private Boxer boxer;
public void load(ActionEvent event) {
System.out.println(boxer.getName());
}
//next stage
public void createNew(ActionEvent event) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("/FXML/creator.fxml"));
BoxingChallengeApplication.stage.setScene(new Scene(root));
}
}
Here in second stage, autowiring not working:
#Component
public class Creator {
#FXML
public Button ready;
public TextField nation;
public TextField name;
public Boxer boxer;
/*#Autowired
private ApplicationContext context;*/
#Autowired
public void setBoxer(Boxer boxer) {
this.boxer = boxer;
}
public void createdAndPlay(ActionEvent event) {
if (boxer == null)
System.out.println("boxer is null");
else
System.out.println("Injected correctly");
}
}
Thanks, i hope it's going to finished...
#Jewelsea's comment is correct: you must set the controller factory when you load creator.fxml. If you don't do this, the FXMLLoader will create the controller simply by calling its no-arg constructor, so Spring will know nothing about it and will have no opportunity to inject any dependencies.
To do this, all you need is access to the ApplicationContext in Start, and you can inject "well-known objects", of which the ApplicationContext is an example, into your Spring-managed beans:
#Component
public class Start {
#FXML
public Button loadGame;
#FXML
public Button create;
#Autowired
private Boxer boxer;
#Autowired
private ApplicationContext context ;
public void load(ActionEvent event) {
System.out.println(boxer.getName());
}
//next stage
public void createNew(ActionEvent event) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/FXML/creator.fxml"));
load.setControllerFactory(context::getBean);
Parent root = loader.load();
BoxingChallengeApplication.stage.setScene(new Scene(root));
}
}
As an aside, you almost certainly want a new instance of any controller when you load an FXML file, so you should probably make any controllers prototype scope.

How to set 'headless' property in a Spring Boot test?

I am testing using Spring Boot with JavaFX (Based on some excellent YouTube videos that explain this).
To make it work with TestFX, I need to create the context like this:
#Override
public void init() throws Exception {
SpringApplicationBuilder builder = new SpringApplicationBuilder(MyJavaFXApplication.class);
builder.headless(false); // Needed for TestFX
context = builder.run(getParameters().getRaw().stream().toArray(String[]::new));
FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));
loader.setControllerFactory(context::getBean);
rootNode = loader.load();
}
I now want to test this JavaFX application, for this I use:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class MyJavaFXApplicationUITest extends TestFXBase {
#MockBean
private MachineService machineService;
#Test
public void test() throws InterruptedException {
WaitForAsyncUtils.waitForFxEvents();
verifyThat("#statusText", (Text text ) -> text.getText().equals("Machine stopped"));
clickOn("#startMachineButton");
verifyThat("#startMachineButton", Node::isDisabled);
verifyThat("#statusText", (Text text ) -> text.getText().equals("Machine started"));
}
}
This starts a Spring context and replaces the "normal" beans with the mock beans as expected.
However, I now get a java.awt.HeadlessException because this 'headless' property is not set to false like is done during normal startup. How to I set this property during the test?
EDIT:
Looking closer it seems that there are 2 context started, one that the Spring testing framework starts and the one I create manually in the init method, so the application under test is not using the mocked beans. If somebody would have a clue how to get the test context reference in the init() method, I would be very happy.
The #SpringBootTest uses SpringBootContextLoader class as context loader, so the ApplicationContext is load from the method SpringBootContextLoader.loadContext as this:
SpringApplication application = getSpringApplication();
......
return application.run();
When invoke the method application.run(), application configure the system headless property with it's internal headless property.
So, If we want to set 'headless' property in a Spring Boot test, just create a customize specific ContextLoader class extends SpringBootContextLoader class, and override the method getSpringApplication with set the headless property as false, then assign the specific ContextLoader with annotation #ContextConfiguration for #SpringBootTest. The code:
public class HeadlessSpringBootContextLoader extends SpringBootContextLoader {
#Override
protected SpringApplication getSpringApplication() {
SpringApplication application = super.getSpringApplication();
application.setHeadless(false);
return application;
}
}
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment=SpringBootTest.WebEnvironment.NONE)
#ContextConfiguration(loader = HeadlessSpringBootContextLoader.class)
public class ApplicationTests {
#Test
public void contextLoads() {
}
}
The comment from Praveen Kumar pointed in the good direction. When I run the test with -Djava.awt.headless=false, then there is no exception.
To solve the other problem of the 2 Spring contexts, I had to do the following:
Suppose this is your main JavaFx startup class:
#SpringBootApplication
public class MyJavaFXClientApplication extends Application {
private ConfigurableApplicationContext context;
private Parent rootNode;
#Override
public void init() throws Exception {
SpringApplicationBuilder builder = new SpringApplicationBuilder(MyJavaFXClientApplication.class);
builder.headless(false); // Needed for TestFX
context = builder.run(getParameters().getRaw().stream().toArray(String[]::new));
FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));
loader.setControllerFactory(context::getBean);
rootNode = loader.load();
}
#Override
public void start(Stage primaryStage) throws Exception {
Rectangle2D visualBounds = Screen.getPrimary().getVisualBounds();
double width = visualBounds.getWidth();
double height = visualBounds.getHeight();
primaryStage.setScene(new Scene(rootNode, width, height));
primaryStage.centerOnScreen();
primaryStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void stop() throws Exception {
context.close();
}
public void setContext(ConfigurableApplicationContext context) {
this.context = context;
}
}
And for testing, you use this abstract base class (Courtesy of this YouTube video by MVP Java):
public abstract class TestFXBase extends ApplicationTest {
#BeforeClass
public static void setupHeadlessMode() {
if (Boolean.getBoolean("headless")) {
System.setProperty("testfx.robot", "glass");
System.setProperty("testfx.headless", "true");
System.setProperty("prism.order", "sw");
System.setProperty("prism.text", "t2k");
System.setProperty("java.awt.headless", "true");
}
}
#After
public void afterEachTest() throws TimeoutException {
FxToolkit.hideStage();
release(new KeyCode[0]);
release(new MouseButton[0]);
}
#SuppressWarnings("unchecked")
public <T extends Node> T find(String query, Class<T> clazz) {
return (T) lookup(query).queryAll().iterator().next();
}
}
Then you can write a test like this:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class MyJavaFXApplicationUITest extends TestFXBase {
#MockBean
private TemperatureService temperatureService;
#Autowired
private ConfigurableApplicationContext context;
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));
loader.setControllerFactory(context::getBean);
Parent rootNode = loader.load();
stage.setScene(new Scene(rootNode, 800, 600));
stage.centerOnScreen();
stage.show();
}
#Test
public void testTemperatureReading() throws InterruptedException {
when(temperatureService.getCurrentTemperature()).thenReturn(new Temperature(25.0));
WaitForAsyncUtils.waitForFxEvents();
assertThat(find("#temperatureText", Text.class).getText()).isEqualTo("25.00 C");
}
}
This allows to start the UI using mock services.

JavaFX : Integrating Spring framework with JavaFX app(Incorrect configuration)

I am working on a JavaFX application and I would like to integrate Spring functionality with it. Currently the code compiles without any error, but when I request service layer methods which are tagged as #Transactional and #Service, I get NullPointerException. What am I doing wrong in the Spring configuration is what I dont understand. Here is my code for JavaFX :
Main class :
public class Main extends Application {
private static final SpringFxmlLoader loader = new SpringFxmlLoader();
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getClassLoader().getResource("login.fxml"));
stage.setTitle("APPNAME");
stage.setScene(new Scene(root, 300, 600));
stage.setFullScreen(false);
stage.setMaximized(false);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages = {"packagename"})
public class ApplicationConfiguration {
#Bean
public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
Properties properties = new Properties();
propertySourcesPlaceholderConfigurer.setProperties(properties);
return propertySourcesPlaceholderConfigurer;
}
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasenames("messages", "org.springframework.security.messages");
messageSource.setUseCodeAsDefaultMessage(true);
return messageSource;
}
}
SpringLoader :
public class SpringFxmlLoader {
private static final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
public Object load(String url) {
try (InputStream fxmlStream = SpringFxmlLoader.class
.getResourceAsStream(url)) {
System.err.println(SpringFxmlLoader.class
.getResourceAsStream(url));
FXMLLoader loader = new FXMLLoader();
loader.setControllerFactory(new Callback<Class<?>, Object>() {
#Override
public Object call(Class<?> clazz) {
return applicationContext.getBean(clazz);
}
});
return loader.load(fxmlStream);
} catch (IOException ioException) {
throw new RuntimeException(ioException);
}
}
}
Now in my Controller, I have something like this :
#Component
public class Controller implements Initializable {
#FXML
public TextField usernameField;
#FXML
public PasswordField passwordField;
#FXML
public Button submitButton;
#Autowired
private PersonService personService;
// Now the above personService throws me a NPE.
}
Am I somehow messing with Spring config for JavaFX. Kindly let me know. Thanks a lot. :-)
Update
After changes suggested by James D..I get the following error :
null
Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$152(LauncherImpl.java:182)
at com.sun.javafx.application.LauncherImpl$$Lambda$2/1058634310.run(Unknown Source)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException: inputStream is null.
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2459)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2429)
at tooltank.MainClass.SpringFxmlLoader.load(SpringFxmlLoader.java:28)
at tooltank.MainClass.Main.start(Main.java:15)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$159(LauncherImpl.java:863)
at com.sun.javafx.application.LauncherImpl$$Lambda$57/667705538.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$172(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl$$Lambda$53/767743416.run(Unknown Source)
at com.sun.javafx.application.PlatformImpl.lambda$null$170(PlatformImpl.java:295)
at com.sun.javafx.application.PlatformImpl$$Lambda$55/1195477817.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(PlatformImpl.java:294)
at com.sun.javafx.application.PlatformImpl$$Lambda$54/1403425489.run(Unknown Source)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
at com.sun.glass.ui.gtk.GtkApplication.lambda$null$48(GtkApplication.java:139)
at com.sun.glass.ui.gtk.GtkApplication$$Lambda$43/1429486634.run(Unknown Source)
... 1 more
Process finished with exit code 1
It happens in SpringFXMLLoader.java at following line :
return loader.load(fxmlStream);
You have created a SpringFxmlLoader but you are not using it. You want
SpringFxmlLoader loader = new SpringFxmlLoader();
Parent root = (Parent) loader.load(getClass().getResource("login.fxml").toExternalForm());
instead of using the FXMLLoader directly.
I would actually write the SpringFxmlLoader differently, so that it matched the standard FXMLLoader API a little more closely:
public class SpringFxmlLoader {
private static final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(ApplicationConfiguration.class);
public <T> T load(URL url) {
try {
FXMLLoader loader = new FXMLLoader(url);
loader.setControllerFactory(applicationContext::getBean);
return loader.load();
} catch (IOException ioException) {
throw new RuntimeException(ioException);
}
}
}
Then your start method looks like:
SpringFxmlLoader loader = new SpringFxmlLoader();
Parent root = loader.load(getClass().getResource("login.fxml"));
You might need to tinker with the exact path to get things right, depending on your setup.

Categories