Jak wiosną Autowire Bean typu generycznego <T>?

83

Mam fasolę, Item<T>która jest wymagana do autoprzewodowania w @Configurationklasie.

@Configuration
public class AppConfig {

    @Bean
    public Item<String> stringItem() {
        return new StringItem();
    }

    @Bean
    public Item<Integer> integerItem() {
        return new IntegerItem();
    }

}

Ale kiedy próbuję @Autowire Item<String>, otrzymuję następujący wyjątek.

"No qualifying bean of type [Item] is defined: expected single matching bean but found 2: stringItem, integerItem"

Jak na wiosnę powinienem wpisać rodzajowy autowire Item<T>?

user3374518
źródło

Odpowiedzi:

146

Prostym rozwiązaniem jest aktualizacja do wersji Spring 4.0, ponieważ automatycznie uwzględni ona typy ogólne jako formę @Qualifier, jak poniżej:

@Autowired
private Item<String> strItem; // Injects the stringItem bean

@Autowired
private Item<Integer> intItem; // Injects the integerItem bean

W rzeczywistości możesz nawet automatycznie łączyć zagnieżdżone leki generyczne podczas wstrzykiwania do listy, jak poniżej:

// Inject all Item beans as long as they have an <Integer> generic
// Item<String> beans will not appear in this list
@Autowired
private List<Item<Integer>> intItems;

Jak to działa?

Nowa ResolvableTypeklasa zapewnia logikę rzeczywistej pracy z typami ogólnymi. Możesz go użyć samodzielnie, aby łatwo nawigować i rozwiązywać informacje o typie. Większość metod ResolvableTypesama zwróci wartość ResolvableType, na przykład:

// Assuming 'field' refers to 'intItems' above
ResolvableType t1 = ResolvableType.forField(field); // List<Item<Integer>> 
ResolvableType t2 = t1.getGeneric(); // Item<Integer>
ResolvableType t3 = t2.getGeneric(); // Integer
Class<?> c = t3.resolve(); // Integer.class

// or more succinctly
Class<?> c = ResolvableType.forField(field).resolveGeneric(0, 0);

Sprawdź przykłady i samouczki pod poniższymi linkami.

Mam nadzieję, że to ci pomoże.

Shishir Kumar
źródło
11
To jest niesamowite. Myślałem, że Type Erasure sprawia, że ​​tego rodzaju rzeczy nie są możliwe.
John Strickler,
6
Jak mogę uzyskać fasolę przez rozpoznawanie typu z Beanfactory?
ceram1
@JohnStrickler Myślałem też, że nie da się tego bezpiecznie zrobić. Nie sprawdziłem jeszcze implementacji ResolvableType, ale czy oznacza to, że możemy teraz bezpiecznie uzyskać typ ogólny?
xi.lin
2
Właściwie W niektórych przypadkach można uzyskać typ ogólny w środowisku wykonawczym w Javie. Jeden warunek, musisz mieć podklasę klasy ogólnej. Przykład: public class Parent<T> {}podklasa klasy nadrzędnej public class Child extends Parent<Integer> { }i teraz: Child c = new Child(); System.out.println(((ParameterizedType)c.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);wydrukuje sięclass java.lang.Integer
Michał Przybylak
1
Czy to zadziała z automatyczną konfiguracją komponentów bean skanowania pakietów? Nie sądzę.
Preslav Rachev
12

Jeśli nie chcesz uaktualnić do Spring 4, musisz automatycznie połączyć się według nazwy, jak poniżej:

@Autowired
@Qualifier("stringItem")
private Item<String> strItem; // Injects the stringItem bean

@Autowired
@Qualifier("integerItem")
private Item<Integer> intItem; // Injects the integerItem bean
Rishav Basu
źródło
Czy masz zamiar pracować bezpośrednio z betonem StringItemi IntegerItemklasami?
user3374518
2

Poniżej znajduje się rozwiązanie, które stworzyłem, aby odpowiedzieć na to pytanie:


        List<String> listItem= new ArrayList<>();

        ResolvableType resolvableType = ResolvableType.forClassWithGenerics(List.class, String.class);
        RootBeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setTargetType(resolvableType);
        beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        beanDefinition.setAutowireCandidate(true);

        DefaultListableBeanFactory bf = (DefaultListableBeanFactory) configurableWebApplicationContext.getBeanFactory();

        bf.registerBeanDefinition("your bean name", beanDefinition);
        bf.registerSingleton("your bean name", listItem);
howard791006
źródło
BTW in Spring 5+
howard791006
1

Strategia Spring autowired jest zdefiniowana w pliku konfiguracyjnym (application.xml).

jeśli nie zdefiniowałeś, domyślnie jest to typ, wtrysk sprężyny użyj mechanizmu odbicia JDK.

więc Lista? Ciąg? i List? Item ?, typ jest taki sam List.class, więc wiosna mylić, jak wstrzyknąć.

i tak jak powyżej osoby reagują, powinieneś być punktem @Qualifier, aby powiedzieć sprężynie, którą fasolę należy wstrzyknąć.

Lubię plik konfiguracyjny sprężyny, aby zdefiniować fasolę, a nie Adnotacje.

<bean>
 <property name="stringItem">
        <list>
              <....>
        </list>
 </property>

zg_spring
źródło
0

Wiosna 4.0 jest odpowiedzią dzięki zastosowaniu adnotacji @Qualifier. Mam nadzieję że to pomoże

shikjohari
źródło
0

Uważam, że nie ma to nic wspólnego z lekami generycznymi ... Jeśli wstrzykujesz dwie różne ziarna tego samego typu, musisz podać kwalifikator, który pomoże Springowi je zidentyfikować;

... gdzie indziej

@Configuration
@Bean
public Item stringItem() {
    return new StringItem();
}

@Bean
public Item integerItem() {
    return new IntegerItem();
}

Jeśli masz deklaracje nieogólne, takie jak te, musisz dodać kwalifikator, aby pomóc Springowi je zidentyfikować ...

@Autowired
**@Qualifier("stringItem")**
private Item item1;

@Autowired
**@Qualifier("integerItem")**
private Item item2;

Oczywiście w wersjach 4 i nowszych wiosna rozważa Typy Generyczne poprzez resolwery, co jest bardzo fajne ...

Ramkumar S
źródło