Wie Autowire Bean vom generischen Typ <T> im Frühjahr?


81

Ich habe eine Bohne, Item<T>die in einer @ConfigurationKlasse automatisch verdrahtet werden muss .

@Configuration
public class AppConfig {

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

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

}

Aber wenn ich es versuche @Autowire Item<String>, bekomme ich folgende Ausnahme.

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

Wie soll ich Item<T>im Frühjahr einen generischen Autowire-Typ verwenden ?

Antworten:


144

Eine einfache Lösung besteht darin, ein Upgrade auf Spring 4.0 durchzuführen, da Generika automatisch wie folgt betrachtet @Qualifierwerden:

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

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

Tatsächlich können Sie verschachtelte Generika sogar automatisch verdrahten, wenn Sie sie in eine Liste einfügen:

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

Wie funktioniert das?

Die neue ResolvableTypeKlasse bietet die Logik, tatsächlich mit generischen Typen zu arbeiten. Sie können es selbst verwenden, um einfach zu Typinformationen zu navigieren und diese aufzulösen. Die meisten Methoden ResolvableTypegeben selbst ein zurück ResolvableType, zum Beispiel:

// 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);

Schauen Sie sich die Beispiele und Tutorials unter den folgenden Links an.

Hoffe das hilft dir.


10
Das ist großartig. Ich dachte, Type Erasure mache so etwas nicht möglich.
John Strickler

5
Wie kann ich Bean nach Resolvabletype von Beanfactory erhalten?
ceram1

@ JohnStrickler Ich dachte auch, dass dies nicht sicher gemacht werden kann. Ich habe die Implementierung von ResolvableType noch nicht überprüft, aber bedeutet dies, dass wir jetzt sicher einen generischen Typ erhalten können?
xi.lin

2
In einigen Fällen können Sie zur Laufzeit in Java einen generischen Typ erhalten. Eine Bedingung ist, dass Sie eine Unterklasse der generischen Klasse haben. Beispiel: public class Parent<T> {}Unterklasse der Elternklasse public class Child extends Parent<Integer> { }und jetzt: Child c = new Child(); System.out.println(((ParameterizedType)c.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);wird gedrucktclass java.lang.Integer
Michał Przybylak

1
Funktioniert dies mit der automatischen Konfiguration von Package-Scan-Beans? Das glaube ich nicht.
Preslav Rachev

12

Wenn Sie nicht auf Spring 4 aktualisieren möchten, müssen Sie den Namen wie folgt automatisch verdrahten:

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

@Autowired
@Qualifier("integerItem")
private Item<Integer> intItem; // Injects the integerItem bean

Wollen Sie direkt mit Beton StringItem& IntegerItemKlassen arbeiten?
user3374518

2

Unten ist eine Lösung, die ich zur Beantwortung dieser Frage gemacht habe:


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

Übrigens im Frühjahr 5+
howard791006

1

Die automatische Drahtstrategie von Spring ist in Ihrer Konfigurationsdatei (application.xml) definiert.

Wenn Sie nicht definiert haben, ist die Standardeinstellung Typ, Federeinspritzung JDK-Reflektionsmechanismus verwenden.

also Liste? String? und List? Item?, der Typ ist der gleiche List.class, also verwirrt Spring, wie man injiziert.

und wie oben Personenantwort, sollten Sie Punkt @Qualifier sein, um Frühling zu sagen, welche Bohne injiziert werden soll.

Ich mag Spring Configration File, um Bean zu definieren, anstatt Annotation.

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


0

Spring 4.0 ist die Antwort mit der Verwendung der Annotation @Qualifier. Hoffe das hilft


0

Ich glaube, es hat nichts mit Generika zu tun ... Wenn Sie zwei verschiedene Bohnen des gleichen Typs injizieren, müssen Sie ein Qualifikationsmerkmal angeben, damit Spring sie identifizieren kann.

... Anderswo

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

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

Wenn Sie nicht generische Deklarationen wie diese haben, müssen Sie ein Qualifikationsmerkmal hinzufügen, damit Spring sie identifizieren kann ...

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

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

Natürlich berücksichtigt der Frühling in Versionen 4 und höher die generischen Typen durch die Resolver, was sehr cool ist ...

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.