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
ApplicationContextas 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 theFXMLLoaderan instance variable. - The
@Controllerannotation 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@Componentannotation 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
PROTOTYPEscope, instead of the defaultSINGLETONscope.
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();
}
}
Comments
Post a Comment