Ответ 1
Вы можете указать фабрику контроллера для FXMLLoader
. Контроллер-фабрика - это функция, которая сопоставляет класс контроллера с объектом (предположительно, но не обязательно, экземпляр этого класса), который будет использоваться в качестве контроллера.
Поэтому, если вы хотите, чтобы Spring создавала для вас экземпляры контроллера, это может быть просто:
ApplicationContext context = ... ;
FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml"));
loader.setControllerFactory(context::getBean);
Parent root = loader.load();
SomeController controller = loader.getController(); // if you need it...
// ...
И теперь FXMLLoader
создаст экземпляры контроллера для Class<?> c
, вызвав context.getBean(c);
,
Так, например, у вас может быть конфигурация:
@Configuration
public class AppConfig {
@Bean
public MyService service() {
return new MyServiceImpl();
}
@Bean
@Scope("prototype")
public SomeController someController() {
return new SomeController();
}
// ...
}
с
public class SomeController {
// injected by FXMLLoader:
@FXML
private TextField someTextField ;
// Injected by Spring:
@Inject
private MyService service ;
public void initialize() {
someTextField.setText(service.getSomeText());
}
// event handler:
@FXML
private void performAction(ActionEvent e) {
service.doAction(...);
}
}
Если вы не используете рамки DI, и хотите сделать инъекцию "вручную", вы можете это сделать, но это связано с использованием довольно много размышлений. Следующее показывает, как (и даст вам представление о том, насколько уродливая работа Spring делает для вас!):
FXMLLoader loader = new FXMLLoader(getClass().getResource("path/to/fxml"));
MyService service = new MyServiceImpl();
loader.setControllerFactory((Class<?> type -> {
try {
// look for constructor taking MyService as a parameter
for (Constructor<?> c : type.getConstructors()) {
if (c.getParameterCount() == 1) {
if (c.getParameterTypes()[0]==MyService.class) {
return c.newInstance(service);
}
}
}
// didn't find appropriate constructor, just use default constructor:
return type.newInstance();
} catch (Exception exc) {
throw new RuntimeException(exc);
}
});
Parent root = loader.load();
// ...
а затем просто сделайте
public class SomeController {
private final MyService service ;
public SomeController(MyService service) {
this.service = service ;
}
// injected by FXMLLoader:
@FXML
private TextField someTextField ;
public void initialize() {
someTextField.setText(service.getSomeText());
}
// event handler:
@FXML
private void performAction(ActionEvent e) {
service.doAction(...);
}
}
Наконец, вы можете проверить afterburner.fx, который является очень легким (по самым лучшим образом) JavaFX-специфическим интерфейсом DI. (Он использует подход с привязкой к конфигурации, где вы просто сопоставляете имена файлов FXML с именами классов контроллеров и, возможно, имена файлов CSS, и все просто работает.)