Mam klasę konfiguracyjną definiującą dwie fasole w zależności od wybranego profilu, z nadpisanymi metodami konfiguracji:

@Configuration
class MyConfig {
    @Profile("profile")
    @Bean
    MyBean myBean(MyBeanProperties properties) {
        return new MyBean(properties);
    }

    @Profile("!profile")
    @Bean
    MyBean myBean(MyBeanProperties properties, AdditionalProperties addProps) {
        MyBean result = new MyBean(properties);
        result.addAdditionalProperties(addProps);
        return result;
    }
}

I klasę, która automatycznie łączy MyBean z nim

@Service
class MyService {
     MyBean autowiredBean;
     private MyService(MyBean bean) { this.autowiredBean = bean; }
}

Teraz, kiedy uruchamiam kontekst Spring, komunikat kończy się niepowodzeniem

Parametr 0 konstruktora w com.example.MyServce wymagał komponentu bean typu „com.example.MyBean”, którego nie można znaleźć.

Jak to możliwe? Jasno określam fasolkę wiosenną, więc powinna być obecna podczas tworzenia kontekstu.

4
daniu 19 grudzień 2019, 12:39
Możesz utworzyć dwa różne pliki konfiguracyjne i określić profile na poziomie klasy. Przykład dostępny na github github.com/naveenkulkarni029/categories-api @Profile zastosowany do Poziom kontrolera jako przykład
 – 
Naveen Kulkarni
19 grudzień 2019, 13:57

3 odpowiedzi

Najlepsza odpowiedź

Powodem tego jest fakt, że Spring uważa, że te komponenty bean mają tę samą nazwę ze względu na nazwę metody konfiguracji, więc nie tworzy ich instancji (chociaż w każdym aktywnym profilu należy utworzyć tylko jedną). To zadziała dobrze:

@Configuration
class MyConfig {
    @Profile("profile")
    @Bean
    MyBean myBean(MyBeanProperties properties) {
        return new MyBean(properties);
    }

    @Profile("!profile")
    @Bean
    // note different method name
    MyBean otherBean(MyBeanProperties properties, AdditionalProperties addProps) {
        MyBean result = new MyBean(properties);
        result.addAdditionalProperties(addProps);
        return result;
    }
}

Nie znalazłem nigdzie wyjaśnienia tego zachowania, więc opublikowałem to pytanie, na które odpowiedziano samodzielnie.

W prawdziwym przypadku wydarzyło się to dla mnie, co za WebClient, który został utworzony z rejestracją klienta w jednym profilu i bez jednego w drugim (ponieważ żaden nie był potrzebny do utworzenia filtra wymiany).

5
daniu 19 grudzień 2019, 12:39
Miałem dokładnie ten sam problem, bardzo dziękuję. To naprawdę dziwne zachowanie i być może nie udokumentowane i nie zarejestrowane jako ostrzeżenie do wiosny. Wygląda na to, że jeśli więcej niż jedna metoda tego samego typu ma taką samą nazwę (niezależnie od liczby/rodzaju argumentów), wiosna ignoruje te @beans.
 – 
p_champ
6 maj 2020, 06:34

Dzieje się tak, gdy dwa ziarna są zdefiniowane z tą samą nazwą metody i jeden z nich ma zostać pominięty na podstawie jakiegoś warunku (w tym przypadku na podstawie profilu). W tym przypadku „myBean” jest definiowany dwukrotnie z różnymi profilami.

Sposób, w jaki klasa konfiguracyjna jest analizowana, polega na przejściu przez wszystkie metody beanMethods w tej klasie i dodaniu odpowiedniej definicji ziarna. Iteracja odbywa się w kolejności, w jakiej beanMethods są zdefiniowane w klasie konfiguracyjnej. Tu jest link do kodu.

W zależności od kolejności, w jakiej te komponenty bean są zdefiniowane w klasie konfiguracji, jeśli pierwszy zdefiniowany komponent bean ma zostać pominięty na podstawie adnotacji profilu, nazwa beanMethod zostanie dodana do listy beanMethod, które należy pominąć. Tu jest link do kodu.

Teraz, gdy napotka drugie ziarno o tej samej nazwie, widzi, że ta nazwa metody ziarnowej jest już obecna na liście metod „do pominięcia”, a zatem pomija ziarno, nawet jeśli nie ma nieodłącznego warunku (takiego jak profil) spowodowałoby to pominięcie. Tu jest link do kodu.

Zauważysz, że jeśli zmienisz kolejność ziaren i użyjesz tego samego profilu do uruchomienia, który wcześniej nie działał, fasola zostanie podniesiona.

Używanie unikalnych nazw beanMethod w klasie konfiguracji byłoby najlepszym sposobem na uniknięcie tego scenariusza.

3
blazarprime 13 maj 2021, 22:11
Bardzo dobre śledzenie problemu. Dzięki @blazarprime
 – 
Samer Makary
21 czerwiec 2021, 17:53

Doszedłem do wniosku, dlaczego zmiana nazw metod pozwoliła na załadowanie kontekstu aplikacji:

Problem polega na tym, że pojemnik wiosenny wymaga, aby wszystkie ziarna miały unikalną nazwę, jak opisano tutaj https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#nazwa-fasoli

Każde ziarno ma jeden lub więcej identyfikatorów. Te identyfikatory muszą być unikalne w kontenerze, w którym znajduje się ziarno.

Kiedy nie używasz konfiguracji XML, myślę, że jedynym sposobem na posiadanie tej samej nazwy metody fasoli jest nadanie unikalnej nazwy fasolom w adnotacji @Bean(NAME), zobacz to, aby uzyskać szczegółowe informacje Spring-bean o tej samej nazwie metody, ale innym kwalifikatorze nie udało się załadować

0
p_champ 6 maj 2020, 06:49
Tak, ale pytanie brzmi, dlaczego Spring próbuje na początek stworzyć obie fasole, biorąc pod uwagę, że jedna z nich nie jest w aktywnym profilu.
 – 
daniu
6 maj 2020, 17:22
Myślę, że dzieje się to podczas kontroli, a nie podczas faktycznego ładowania klasy, jeśli kontrola się nie powiedzie, ignoruje te ziarna
 – 
p_champ
6 maj 2020, 17:27