Springでジェネリック型<T>のBeanを自動配線する方法は?


83

クラスItem<T>で自動配線する必要があるBeanがあり@Configurationます。

@Configuration
public class AppConfig {

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

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

}

しかし、しようとすると@Autowire Item<String>、次の例外が発生します。

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

Item<T>Springでジェネリック型をAutowireするにはどうすればよいですか?

回答:


145

簡単な解決策は、Spring 4.0にアップグレードすることです。これは@Qualifier、以下のように、ジェネリックをの形式として自動的に考慮するためです。

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

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

実際、以下のように、リストに挿入するときにネストされたジェネリックを自動配線することもできます。

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

これはどのように機能しますか?

新しいResolvableTypeクラスは、ジェネリック型を実際に操作するロジックを提供します。自分で使用して、タイプ情報を簡単にナビゲートして解決できます。上のほとんどのメソッドResolvableTypeは、それ自体がを返しますResolvableType。次に例を示します。

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

以下のリンクで例とチュートリアルを確認してください。

これがお役に立てば幸いです。


11
これは素晴らしいです。型消去はこのようなことを不可能にしたと思いました。
John Strickler 2014

6
BeanfactoryからresolvabletypeでBeanを取得するにはどうすればよいですか?
ceram1 2014年

@JohnStrickler私もこれは安全に行うことができないと思いました。ResolvableTypeの実装をまだ確認していませんが、ジェネリック型を安全に取得できるということですか?
xi.lin 2015年

2
実際、Javaの実行時にジェネリック型を取得できる場合があります。1つの条件として、ジェネリッククラスのサブクラスが必要です。例:親クラスのpublic class Parent<T> {}サブクラスpublic class Child extends Parent<Integer> { }となりました:Child c = new Child(); System.out.println(((ParameterizedType)c.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);印刷しますclass java.lang.Integer
のMichałPrzybylak

1
これはパッケージスキャンBeanの自動構成で機能しますか?私はそうは思いません。
Preslav Rachev 2018年

12

Spring 4にアップグレードしたくない場合は、以下のように名前で自動配線する必要があります。

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

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

具象StringItemIntegerItemクラスを直接扱うという意味ですか?
user3374518 2014年

2

以下は、この質問に答えるために私が作成した解決策です。


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

ところで、Spring 5+
howard7910 0619年

1

Spring自動配線戦略は、構成ファイル(application.xml)で定義されています。

定義していない場合、デフォルトはタイプによるもので、SpringInjectはJDKリフレクトメカニズムを使用します。

だからList?String?とList?Item?、タイプは同じList.classであるため、春は注入方法を混乱させました。

上記の人の応答のように、どのBeanを注入するかをスプリングに指示するには、@ Qualifierをポイントする必要があります。

私は、アノテーションではなくBeanを定義するためのSpring構成ファイルが好きです。

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



0

ジェネリックスとは何の関係もないと思います...同じタイプの2つの異なるBeanを注入する場合は、Springがそれらを識別するのに役立つ修飾子を提供する必要があります。

...他の場所

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

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

これらのような非ジェネリック宣言がある場合は、Springがそれらを識別できるように修飾子を追加する必要があります...

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

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

もちろん、バージョン4以降では、春はリゾルバーを介して汎用タイプを考慮します。これは非常にクールです...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.