JavaFX приложение падает при попытке сделать контроллер бином

Из ответов на вопрос Как обеспечить доступ к бинам для классов, не являющихся точкой входа в приложение (и бинами)? я так и не узнал, как же обеспечить доступ к бинам для тех классов, которые сами бинами не являются. Пришлось делать делать JavaFX-контроллер бином:

@Component
public class PersonOverviewController {

  private final PeopleGateway peopleGateway;

  // ...

  @Autowired
  public PersonOverviewController(PeopleGateway peopleGateway) {
    this.peopleGateway = peopleGateway;
  }

  @FXML
  private void initialize() {

    System.out.println("===");
    System.out.println(peopleGateway);
    // Если инъекция произведена успешно, то это не должен быть null    
  }

  // ...
}

Привело это к тому, что приложение стало падать по непонятной мне причине. Из сообщений об ошибке ясно, что JavaFX то ли потерял контроллер, то ли не может им манипулировать:

Caused by: java.lang.InstantiationException: dev.example.Controllers.PersonOverviewController
    at java.base/java.lang.Class.newInstance(Class.java:598)
    at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:936)
    ... 18 more
Caused by: java.lang.NoSuchMethodException: dev.example.Controllers.PersonOverviewController.<init>()
    at java.base/java.lang.Class.getConstructor0(Class.java:3427)
    at java.base/java.lang.Class.newInstance(Class.java:585)
    ... 19 more

Конфигурация приложения:

@Configuration
public class ApplicationContextConfig {

  @Bean
  public FilesManager filesManager() {
    return new RegularFilesManager();
  }

  @Bean
  public PeopleGateway peopleGateway() { return new PeopleGatewayWithMockData(); }

  @Bean
  public PersonOverviewController personOverviewController() {
    return new PersonOverviewController(this.peopleGateway());
  }
}

Полный код приложения доступен на Bit-bucket-репозитории. Также прилагаю ссылку на код приложения в рабочем состоянии.


Ответы (1 шт):

Автор решения: Ruslan P.

FXMLLoader вроде как не умеет в constructor Injection, и контроллер создать не сможет, да и в данном случае не должен. Для использования "внешнего" DI предусмотрен специальный механизм c ControllerFactory:

FXMLLoader loader = new FXMLLoader();    
loader.setControllerFactory(applicationContext::getBean);

ControllerFactory имеет тип Callback<Class<?>, Object>, в данном случае ссылка на метод, applicationContext::getBean где applicationContext - это спринговый ApplicationContext.

→ Ссылка