14 votes

Spring - enregistrement d'un bean scopé à l'exécution

Je travaille sur une application basée sur Spring qui enregistre une champ d'application personnalisé "tâche . L'idée est que lorsqu'une nouvelle tâche est lancée, Spring devrait fournir axé sur les tâches objets.

La tâche est instanciée dans le runtime. Elle reçoit une certaine configuration sous la forme d'un objet Properties objet. Je veux enregistrer cet objet avec le ApplicationContext mais à l'intérieur de la portée de la tâche afin que tous les beans de cette portée puissent faire référence à la configuration de cette tâche particulière.

Voici l'idée générale en code :

public class MyTask extends SourceTask {
    @Override
    public void start(Map<String, String> props) {
        context = ContextProvider.getApplicationContext();
        // Initialize the scope
        ConnectorTaskScope scope = context.getBean(ConnectorTaskScope.class);
        scope.startNewTask();

        // TODO register the props object in the context

        // get an object which requires the properties and work with it
        context.getBean(SomeScopedBean.class);        
    }
}

Je n'arrive pas à comprendre comment je peux enregistrer un haricot dans la base de données de l'UE. ApplicationContext dont la portée est appropriée.

Merci.

Mise à jour :

Voici un peu plus de code pour expliquer un peu mieux la question. SomeScopedBean devrait faire quelque chose avec la configuration qui lui a été fournie et ressemble à quelque chose comme ceci :

public class SomeScopedBean {
    @Autowire
    public SomeScopedBean (Properties configuration) {
        // do some work with the configuration 
    }
}

L'idée de l'application est qu'elle devrait avoir plusieurs instances de MyTask fonctionnent avec des configurations différentes et chaque tâche a sa propre portée. Dans le cadre de chaque tâche, il devrait y avoir 1 instance de SomeScopedBean initialisé avec la configuration de la tâche.

public class MyApplication {
    public static void main (String[] args) {
        // ...
        Properties config1 = loadConfiguration1();
        Properties config2 = loadConfiguration2();
        MyTask task1 = new MyTask();
        MyTask task2 = new MyTask();
        task1.start(config1);
        task2.start(config2);
        // ...
    }
}

3voto

Nicolas Labrot Points 151

Si je prends votre dernier commentaire :

Ce que je veux, c'est avoir une instance de SomeScopedBean dans chaque scope (dans chaque MyTask). (à l'intérieur de chaque MyTask), mais chacune configurée avec des propriétés de configuration de configuration différentes (qui sont fournies par le cadre de déploiement lorsqu'il instancie chaque tâche,

Et surtout within each MyTask et si elle est limitée à MyTask .

Tu peux :

  • définir SomeScopedBean comme un prototype de haricot
  • créer une usine @Configuration qui instanciera SomeScopedBean avec la configuration des propriétés fournies

D'abord la configuration :

@Configuration
public class SomeScopedBeanFactoryConfiguration {

    @Bean
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    public SomeScopedBean create(Properties configuration) {
        return new SomeScopedBean(configuration);
    }

}

Ensuite, on a câblé automatiquement le SomeScopedBeanFactoryConfiguration en MyTask et crée SomeScopedBean :

public class MyTask extends SourceTask {

    @Autowired
    private SomeScopedBeanFactoryConfiguration  someScopedBeanFactoryConfiguration;

    @Override
    public void start(Map<String, String> props) {
        SomeScopedBean scopedBean = someScopedBeanFactoryConfiguration.create(props);    
    }
}

Remarque : si SomeScopedBean doit être injecté dans plus d'un haricot avec l'attribut task / thread vous pouvez changer sa portée pour celle de votre fil, par exemple :

    @Bean
    @Scope("thread")
    public SomeScopedBean create(Properties configuration) {
        return new SomeScopedBean(configuration);
    }

2voto

StanislavL Points 31343
AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory();

MyTask instance = new MyTask();
beanFactory.autowireBean(instance);
beanFactory.initializeBean(instance, MyTask.class.getCanonicalName());

//for singleton I used
((ConfigurableListableBeanFactory)beanFactory).registerSingleton(MyTask.class.getCanonicalName(), instance);

Dans votre cas, j'enregistrerais un singleton de MyTask Proxy. Le mandataire peut conserver toutes les instances dépendantes de la portée (par exemple dans une carte ou dans un stockage ThreadLocal) et, sur appel, déléguer la logique à la bonne instance à partir de la carte.

UPDATE : En fait, ce n'est pas le haricot MyTask qui est autowire mais un Proxy. Le proxy englobe toutes les méthodes de MyTask. Le Proxy a la même interface que MyTask. Supposons que vous appelez la méthode ProxyMyTask.do(). Le proxy intercepte l'appel, obtient une certaine portée, par exemple le thread actuel dans le cas de l'exemple Tread Scope et obtient de la Map (ou pour Thread Scope du stockage ThreadLocal) l'instance appropriée de MyTask. Enfin, il appelle la méthode do(0 de l'instance MyTask trouvée.

MISE À JOUR 2 : Voir l'exemple http://tutorials.jenkov.com/java-reflection/dynamic-proxies.html Vous pouvez facilement envelopper une interface. Votre logique pour déterminer la portée et retourner l'instance appropriée devrait être dans la méthode

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    return method.invoke(target, args);
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X