Można określić controller factory dla FXMLLoader
. Fabryka kontrolerów jest funkcją, która odwzorowuje klasę kontrolera na obiekt (prawdopodobnie, ale niekoniecznie instancję tej klasy), która będzie używana jako kontroler.
Więc jeśli chcesz Wiosna tworzyć instancje kontroler dla ciebie, to może być tak proste, jak:
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...
// ...
a teraz FXMLLoader
będą tworzyć instancje kontrolerowi celem Class<?> c
wywołując context.getBean(c);
.
Tak, na przykład można mieć konfigurację:
@Configuration
public class AppConfig {
@Bean
public MyService service() {
return new MyServiceImpl();
}
@Bean
@Scope("prototype")
public SomeController someController() {
return new SomeController();
}
// ...
}
z
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(...);
}
}
Jeśli nie używasz ramy DI, a chcesz zrobić zastrzyk "ręcznie" , możesz to zrobić, ale wymaga to sporo refleksji. Poniżej przedstawiono w jaki sposób (i daje wyobrażenie o ile brzydki praca Wiosna robi dla Ciebie!):
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();
// ...
a potem po prostu zrobić
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(...);
}
}
Wreszcie może chcesz sprawdzić afterburner.fx, która jest bardzo lekką (we wszystkich najlepszych metodach) architekturą DI specyficzną dla JavaFX. (Wykorzystuje podejście oparte na konwencji, w którym dopasowujesz nazwy plików FXML do nazw klas kontrolerów i opcjonalnie nazwy plików CSS, a wszystko po prostu działa.)