2017-04-13 5 views
0

J'ai une application JavaFX + Spring Boot. Lorsque j'essaie de charger un nouveau FXML avec son contrôleur, cela fonctionne très bien, mais quand je le charge une deuxième ou une troisième fois, il me donne la même instance et n'en crée pas une nouvelle.FXML Loader ne crée pas de nouvelle instance

Mon chargeur FXML comme suit:

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(); 
} 

Chargement nouvelle FXML:

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

Et quand je tente de l'ajouter dans ma liste de contrôleur chargé, les ID sont les mêmes, que le hachage codes, ce qui signifie que c'est la même instance. Ainsi, il n'en a pas créé un nouveau.

enter image description here

Peut-être que je charge mes FXMLs mal ou tout simplement l'usine de contrôleur Spring-Boot fonctionne bizarre dans ce cas. Aucune suggestion?

+0

Quelle est la portée de 'context', et comment' getBean' est-il implémenté? S'il n'y a qu'une seule instance de 'context' et qu'elle renvoie toujours le même contrôleur, alors il n'y en aura plus qu'une seule. – Itai

+1

N'est-ce pas exactement ce que le printemps est censé faire avec ses haricots singleton ??? – fabian

Répondre

2

La ligne

loader.setControllerFactory(context::getBean); 

charge le FXMLLoader pour obtenir le contrôleur du contexte d'application de printemps: en interne, le FXMLLoader va maintenant faire quelque chose comme

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

Le comportement de getBean dépend de la façon dont vous configuré le bean pour cette classe, mais par défaut il aura une portée "singleton". Cela signifie qu'il va créer une instance de la classe et retourner la même instance à chaque fois que getBean(...) est invoqué avec un argument correspondant à ce bean (par exemple, la classe du bean).

Vous voulez certainement une nouvelle instance de la classe de contrôleur chaque fois que vous chargez le fichier FXML, vous devez donc configurer le bean pour avoir une portée "prototype". Avec une portée "prototype", une nouvelle instance sera créée à chaque fois.

Le mécanisme pour définir la portée du grain dépend de la façon dont vous configurez votre contexte d'application, mais avec config basées sur les annotations vous feriez quelque chose comme

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

Avec config Java que vous feriez

@Configuration 
public class ApplicationConfig { 

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

    // ... 
} 

et si vous utilisez encore l'ancien schéma à base de configuration (XML), vous feriez (si je me souviens bien)

<beans> 

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

    <!-- ... --> 

</beans> 
+0

Merci! Je viens d'ajouter mon contrôleur à ma classe ApplicationConfig avec des beans, j'ai changé pour la portée du prototype et maintenant tout fonctionne très bien! – Kefirchiks