FXML Loader не создает новый экземпляр

У меня есть приложение JavaFX + Spring Boot. Когда я пытаюсь загрузить новый FXML с его контроллером, он работает нормально, но когда я загружаю его второй или третий раз, он дает мне тот же экземпляр и не создает новый.

Мой загрузчик FXML выглядит следующим образом:

public <T> T loadAndGetController(String fxmlPath) throws IOException {
    FXMLLoader loader = new FXMLLoader();
    loader.setControllerFactory(context::getBean); // Spring now FXML Controller Factory
    loader.setLocation(getClass().getResource(fxmlPath));
    loader.setResources(ResourceBundle.getBundle("language/lang",
            new Locale(languageController.getLanguage().getValue(), languageController.getLanguage().toString())));
    loader.load();
    return loader.getController();
}

Загрузка нового FXML:

TabController tabController = (TabController) StageManager.loadAndGetController(FXMLViews.TAB.getFxmlFile());

И когда я пытаюсь добавить его в свой список загруженных контроллеров, идентификаторы совпадают с хэш-кодами, что означает, что это один и тот же экземпляр. Таким образом, он не создал новый.

введите здесь описание изображения

Возможно, я неправильно загружаю свои FXML, или в этом случае просто фабрика контроллеров Spring-Boot работает странно. Какие-либо предложения?


person Kefirchiks    schedule 13.04.2017    source источник
comment
Какова область действия context и как реализовано getBean? Если есть только один экземпляр context и он всегда возвращает один и тот же контроллер, то он всегда будет только один.   -  person Itai    schedule 13.04.2017
comment
Разве это не то, что весна должна делать со своими одноэлементными бобами???   -  person fabian    schedule 13.04.2017


Ответы (1)


Линия

loader.setControllerFactory(context::getBean);

инструктирует FXMLLoader получить контроллер из контекста приложения Spring: внутренне FXMLLoader теперь будет делать что-то вроде

Class controllerClass = Class.forName(classNameFromFXMLFile);
Object controller = context.getBean(controllerClass);

Поведение getBean зависит от того, как вы настроили bean-компонент для этого класса, но по умолчанию он будет иметь область действия "singleton". Это означает, что он создаст один экземпляр класса и будет возвращать один и тот же экземпляр каждый раз, когда getBean(...) вызывается с сопоставлением аргумента с этим компонентом (например, классом компонента).

Вам почти наверняка нужен новый экземпляр класса контроллера каждый раз, когда вы загружаете файл FXML, поэтому вам следует настроить bean-компонент так, чтобы он имел область «прототип». С областью действия «прототип» каждый раз будет создаваться новый экземпляр.

Механизм определения области действия bean-компонента зависит от того, как вы настраиваете контекст своего приложения, но с конфигурацией на основе аннотаций вы должны сделать что-то вроде

@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class FarTabController { /* ... */ }

С конфигурацией на основе Java вы бы сделали

@Configuration
public class ApplicationConfig {

    @Bean
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    public FarTabController farTabController() {
        return new FarTabController() ;
    }

    // ...
}

и если вы все еще используете старую конфигурацию на основе схемы (XML), вы бы сделали (если я правильно помню)

<beans>

    <bean class="my.package.FarTabController" scope="prototype" />

    <!-- ... -->

</beans>
person James_D    schedule 13.04.2017
comment
Благодарю вас! Я только что добавил свой контроллер в свой класс ApplicationConfig с bean-компонентами, перешел к области прототипа, и теперь все работает отлично! - person Kefirchiks; 13.04.2017