Question Comment puis-je injecter des haricots dans une implémentation de périmètre?


J'écris mon propre champ d'application (c'est-à-dire une classe qui implémente org.springframework.beans.factory.config.Scope) et j'ai besoin de quelques haricots injectés. Comment je fais ça?

Contexte: Spring doit d'abord créer tous les beans scope afin de pouvoir définir les beans entrant dans la portée. Mais qu'en est-il des haricots dont j'ai besoin pour créer la portée en premier lieu?


12
2017-08-17 12:20


origine


Réponses:


Je suis venu avec cette solution de contournement qui semble être assez sûre mais je voudrais entendre des commentaires (ou peut-être ma réponse vous donne de meilleures idées):

  1. Définir la portée et lui donner des paramètres (au lieu d'utiliser @Autowired)
  2. Créez un bean "scope configurer":

    public CustomScopeConfigurer {
        @Autowired private Foo foo;
        private CustomScope scope;
    
        public CustomScopeConfigurer( CustomScope scope ) {
            this.scope = scope;
        }
    
        @PostConstruct
        public void initScope() {
            scope.setFoo( foo );
        }
    }
    

    Ce haricot ne doit pas être paresseux.

Raisonnement:

  1. La portée elle-même ne peut pas être utilisée automatiquement car elle est créée avant le premier bean. Bien qu'il puisse être créé plus tard, vous pouvez être sûr qu'il sera créé avant chaque autre bean. Autowiring ne peut donc pas fonctionner de manière fiable.

  2. Le haricot configurer sera créé à côté de tous les autres haricots, mais après la portée. Donc, le démarrage automatique fonctionnera pour cela.

  3. Comme le bean de configuration n'est pas initialisé paresseux, il sera créé avant que le reste de l'application puisse voir le contexte de l'application. Cela signifie que pas de haricots pour la portée (c.-à-haricots avec @Scope("custom")) peut avoir été créé à ce moment-là - la portée ne peut pas être "active", - pourtant, le printemps n’aura pas encore essayé d’y mettre des haricots.

  4. La portée elle-même est généralement créée en tant que constante statique quelque part. C'est pourquoi nous devons le passer en argument au constructeur.


4
2017-08-17 15:03



Vous pouvez le faire très simplement.

Considérez la classe de portée personnalisée ci-dessous:

package com.way2learn;

import java.util.Map;

import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;

public class MyCustomScope implements Scope{

    private Map<String, Object> scope;

    public void setScope(Map<String, Object> scope) {
        this.scope = scope;
    }

    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        checkAndClear();
        Object bean=scope.get(name);
        if(bean==null){
            bean=objectFactory.getObject();
            scope.put(name,bean);
        }
        return bean;
    }
    private void checkAndClear() {
        //Some logic to check condition and clear the scope
    }
    //optional methods
    @Override
    public Object remove(String name) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {

    }

    @Override
    public Object resolveContextualObject(String key) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getConversationId() {
        // TODO Auto-generated method stub
        return null;
    }

}

Il dépend de java.util.Map.

Vous ne pouvez pas utiliser automatiquement @Autowired comme @Autowired l'annotation fonctionne après AutoWiredAnnotationBeanPostProcessor seulement.

Mais les portées personnalisées seront enregistrées avant AutoWiredAnnotationBeanPostProcessor.

Donc, vous pouvez injecter manuellement Map dans MyCustomScope classe comme ci-dessous:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd">

        <util:map key-type="java.lang.String" value-type="java.lang.Object" id="custScopeMap"/>

        <bean id="myCustomScope" class="com.way2learn.MyCustomScope">
            <property name="scope" ref="custScopeMap"/>
        </bean>

        <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
            <property name="scopes">
                <map>
                    <entry key="myScope" value-ref="myCustomScope"/>
                </map>
            </property>
        </bean>

</beans>

Je l'ai essayé. Ça fonctionne bien. Et j'ai trouvé un bug dans Aaron Digulla's répondre i.e.

Considérons le scénario ci-dessous: Spring's CustomScopeConfigurer le haricot sera créé, immédiatement CustomScope bean sera créé et maintenant notre portée personnalisée est prête à être utilisée. Après quelque temps Aaron Digulla's CustomScopeConfigurer sera créé qui initialise toto dans CustomScope. Mais que se passe-t-il si des haricots de portée personnalisés sont créés après CustomScope registration et avant Aaron Digulla's CustomScopeConfigurer création de haricots? Pour ces haricots il n'y aura pas de foo dans le CustomScope haricot.


2
2017-11-29 13:22