Pytanie Jak wprowadzić fasolę do implementacji zakresu?


Piszę swój własny zakres (tj. Klasa, która implementuje org.springframework.beans.factory.config.Scope) i potrzebuję wstrzykniętej fasoli. Jak mogę to zrobić?

Tło: Wiosna musi najpierw utworzyć wszystkie komponenty, aby móc zdefiniować, które komponenty zostaną włączone do zakresu. Ale co z fasolą, której potrzebuję, aby zbudować ten zakres w pierwszej kolejności?


12
2017-08-17 12:20


pochodzenie


Czy Spring skarży się, że ich nie znajdzie beankiedy próbujesz uruchomić aplikację? - nicholas.hauschild
Nie, pola pozostają null. Mogłem zrobić zakres ApplicationContextAware ale jak już powiedziałem, kontekst jest w połowie jego inicjalizacji, więc większość ziaren nie jest ani utworzona, ani w połowie zainicjowana (więc niektóre @Autowired pola są ustawione, inne nie). - Aaron Digulla
Tylko z ciekawości, czy wiesz, czy którykolwiek z istniejących Scope implementacje używają wtrysku w ten sposób ...? Wygląda na to, że próbujesz użyć funkcji frameworku, zanim będzie ona dostępna (widzę, że zakres jest wstępnym warunkiem wstępnym) ... - Less
Zgadzam się, to narożny przypadek i nie, nie widziałem żadnego innego zakresu, który spróbowałby takiego wyczynu. Ale nadal uważam, że przydatne jest dzielenie fasoli między zakresem a appContext. - Aaron Digulla
Strzał w ciemność: spróbuj wdrożyć AopInfrastructureBean w fasoli, którą chcesz wstrzyknąć do swojego niestandardowego zakresu. - Tomasz Nurkiewicz


Odpowiedzi:


Wymyśliłem to obejście, które wydaje się całkiem bezpieczne, ale chciałbym usłyszeć komentarze (a może moja odpowiedź daje lepsze pomysły):

  1. Zdefiniuj zakres i nadaj mu ustawniki (zamiast używać @Autowired)
  2. Utwórz komponent 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 );
        }
    }
    

    Ten komponent bean configure nie może być leniwy.

Rozumowanie:

  1. Sam zakres nie może używać funkcji autowiring, ponieważ jest tworzony przed pierwszym komponentem bean. Choć może być później utworzony, możesz być pewien, że zostanie utworzony przed każdym innym komponentem. W związku z tym nie można wiarygodnie pracować.

  2. Komponent bean configure zostanie utworzony obok wszystkich pozostałych komponentów, ale po zasięgu. Więc autowiring będzie dla niego działał.

  3. Ponieważ komponent bean komendy nie został zainicjowany jako leniwy, zostanie utworzony, zanim reszta aplikacji będzie mogła zobaczyć kontekst aplikacji. Oznacza to, że nie ma fasoli dla zakresu (np @Scope("custom")) mogły zostać stworzone w tym momencie - zakres nie może być "aktywny", jednak -> Wiosna jeszcze nie próbowała włożyć do niego żadnych fasoli.

  4. Sam zakres jest zwykle tworzony gdzieś w stałej statycznej. Dlatego musimy przekazać to jako argument dla konstruktora.


4
2017-08-17 15:03



Czy to rzeczywiście działa? Zakładam, że musisz utworzyć instancję obiektu zasięgu ręcznie, więc w jaki sposób linia 3 pobiera tę instancję i nie tworzy nowego? - Tom Jenkinson
@ TomJenkinson: Dobry połów. Masz rację, musisz przekazać zakres jako argument konstruktora. - Aaron Digulla
@AaronDigulla Może wystąpić ryzyko pojawienia się błędów. Rozważmy następujący scenariusz: Pierwszy komponent bean CustomScopeConfigurer zostanie utworzony, natychmiast zostanie utworzony komponent bean CustomScope, a teraz nasz zakres niestandardowy jest gotowy do użycia. Po pewnym czasie zostanie utworzony CustomScopeConfigurer, który zainicjuje foo w CustomScope. Ale co się stanie, jeśli niektóre niestandardowe komponenty bean zostaną utworzone po rejestracji CustomScope i przed utworzeniem komponentu bean CustomScopeConfigurer? Dla tych ziaren nie będzie foo w CustomScope. Próbowałem znaleźć rozwiązanie tego problemu, ale go nie dostałem. Coutld, spróbuj tego? :) - Jagadeesh
@AaronDigulla Podałem proste rozwiązanie twojego pytania. Proszę śledzić i sugerować mi poprawę. :) - Jagadeesh


Możesz to zrobić bardzo prosto.

Rozważ poniższą niestandardową klasę zasięgu:

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

}

Ma zależność java.util.Map.

Nie możesz korzystać z programu AutoNice za pomocą @Autowired to jako @Autowired Adnotacja działa po AutoWiredAnnotationBeanPostProcessor tylko.

Ale zakresy niestandardowe zostaną zarejestrowane wcześniej AutoWiredAnnotationBeanPostProcessor.

Możesz więc ręcznie wstrzyknąć Map w MyCustomScope Klasa jak poniżej:

<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>

Próbowałem tego. To działa dobrze. I znalazłem błąd w Aaron Digulla's odpowiedź tj.

Rozważmy następujący scenariusz: Najpierw Spring's CustomScopeConfigurer Bean zostanie natychmiast utworzony CustomScope bean zostanie utworzony, a teraz nasz niestandardowy zakres jest gotowy do użycia. Po jakimś czasie Aaron Digulla's CustomScopeConfigurer zostanie utworzony, który inicjuje foo do CustomScope. Ale co się stanie, jeśli zostaną utworzone niestandardowe ziarna o określonym zasięgu CustomScope registration a wcześniej Aaron Digulla's CustomScopeConfigurer tworzenie fasoli? Dla tych ziaren nie będzie foo w CustomScope fasola.


2
2017-11-29 13:22



Domyślam się, że ograniczenie polega na tym, że nie można tego zrobić za pomocą czystej konfiguracji Java; musisz wrócić do pliku konfiguracyjnego XML. Czy to jest poprawne? - Aaron Digulla
Jeśli chodzi o znaleziony błąd, zobacz przyczynę 3 mojej odpowiedzi. Kontekst aplikacji musi być ukończony, zanim komponent bean może zostać umieszczony w zasięgu. Nawet jeśli połączysz komponent o określonym zasięgu, utworzysz serwer proxy. Po utworzeniu kontekstu aktywny będzie kod pomocnika do wybierania zakresów. Więc nie powinno być możliwe / dozwolone połączenie Scope.get() zanim kontekst będzie gotowy. - Aaron Digulla