同じインターフェースを実装する2つのBeanの自動配線-デフォルトのBeanを自動配線に設定する方法


138

バックグラウンド:

Spring 2.5 / Java / Tomcatアプリケーションがあります。アプリケーション全体で多くの場所で使用されている次のBeanがあります

public class HibernateDeviceDao implements DeviceDao

そして次の新しいBean:

public class JdbcDeviceDao implements DeviceDao

最初のBeanはそのように構成されています(パッケージ内のすべてのBeanが含まれています)

<context:component-scan base-package="com.initech.service.dao.hibernate" />

2番目の(新しい)Beanは個別に構成されます

<bean id="jdbcDeviceDao" class="com.initech.service.dao.jdbc.JdbcDeviceDao">
    <property name="dataSource" ref="jdbcDataSource">
</bean>

これにより、(もちろん)サーバーの起動時に例外が発生します。

ネストされた例外はorg.springframework.beans.factory.NoSuchBeanDefinitionException:タイプ[com.sevenp.mobile.samplemgmt.service.dao.DeviceDao]の一意のBeanが定義されていません:一致する単一のBeanが予期されていますが、2:[deviceDao、jdbcDeviceDao]が見つかりました

このように豆を自動配線しようとするクラスから

@Autowired
private DeviceDao hibernateDevicDao;

同じインターフェースを実装する2つのBeanがあるためです。

質問:

ように豆を構成することは可能ですか

1.既にHibernateDeviceDao自動配線されている既存のクラスを変更する必要はありません

2.次のように、2番目の(新しい)Beanを引き続き使用できます。

@Autowired
@Qualifier("jdbcDeviceDao")

HibernateDeviceDaoつまり、自動ワイヤリングされるデフォルトのBeanとしてBean を構成する方法が必要です。同時にJdbcDeviceDao@Qualifierアノテーションで明示的に指定するときに、の使用を許可します。

私がすでに試したこと:

プロパティを設定してみた

autowire-candidate="false"

JdbcDeviceDaoのBean構成:

<bean id="jdbcDeviceDao" class="com.initech.service.dao.jdbc.JdbcDeviceDao" autowire-candidate="false">
    <property name="dataSource" ref="jdbcDataSource"/>
</bean>

Springドキュメントはそれを言うので

別のBeanの自動配線要件を満たすために一致する候補を探すときに、このBeanを考慮する必要があるかどうかを示します。これは名前による明示的な参照には影響を与えないことに注意してください。指定されたBeanがautowire候補としてマークされていなくても解決されます。*

これはJdbcDeviceDao@Qualifierアノテーションを使用して自動ワイヤリングし、HibernateDeviceDaoデフォルトのBeanとして使用できることを意味すると解釈しました。しかし、サーバーの起動時に次のエラーメッセージが表示されるため、私の解釈は正しくなかったようです。

タイプ[クラスcom.sevenp.mobile.samplemgmt.service.dao.jdbc.JdbcDeviceDao]の不満足な依存関係:少なくとも1つの一致するBeanが期待されます

私が修飾子を使ってBeanを自動配線しようとしたクラスから来ています:

@Autowired
@Qualifier("jdbcDeviceDao")

解決:

@Resourceアノテーションを試すというskaffmanの提案は機能しました。したがって、設定ではjdbcDeviceDaoのautowire-candidateがfalseに設定されています。jdbcDeviceDaoを使用するときは、@ Resourceアノテーション(@Qualifierではなく)を使用して参照します。

@Resource(name = "jdbcDeviceDao")
private JdbcDeviceListItemDao jdbcDeviceDao;

コードの100か所でこのインターフェイスを使用していて、すべてを別の実装に切り替えたい場合は、すべての場所で修飾子やリソースの注釈を変更したくありません。また、どちらの実装のコードも変更したくありません。Guiceのように明示的なバインディングの可能性がないのはなぜですか?
DanielHári2017年

回答:


134

Hibernate DAOクラスを@Primaryでマークすることをお勧めします。つまり、で使用@Repositoryしたと仮定しますHibernateDeviceDao

@Primary
@Repository
public class HibernateDeviceDao implements DeviceDao

これによりautowire-candidate、他のBeanを使用する必要なく、デフォルトのautowire候補として選択されます。

また、を使用するよりも@Autowired @Qualifier@Resource特定のBeanを選択するために使用する方がエレガントだと思います。

@Resource(name="jdbcDeviceDao")
DeviceDao deviceDao;

質問で、Spring 2.5を使用していることを説明するのを忘れていたので(質問を編集しました)、@ Primaryはオプションではありません。
サイモン

1
@simon:はい、それはかなり重要でした。@Resource私も提案したように、注釈を試してください。
skaffman

1
おかげで、リソースアノテーションで問題が解決しました。autowire-candidateプロパティが期待どおりに機能するようになりました。
サイモン、

ありがとう!経由でBean名を指定しての違いは何だ@Resource@Qualifier離れて、前者が後者よりも比較的新しいという事実からは、?
asgs

1
@asgsリソースアノテーションを使用すると、処理が簡単になります。autowired / qualifierのコンボを使用する代わりに、依存関係注入のマークを付けて、名前を1行で指定できます。simonのソリューションは冗長であり、自動配線されたアノテーションは削除できることに注意してください。
Gilbert Arenas Dagger

37

どう@Primaryですか?

複数の候補が単一値の依存関係を自動配線する資格がある場合、Beanが優先されることを示します。候補の中に「プライマリ」Beanが1つだけ存在する場合、それは自動配線値になります。この注釈は、Spring XMLの<bean>要素のprimary属性と意味的に同等です。

@Primary
public class HibernateDeviceDao implements DeviceDao

または、Jdbcバージョンをデフォルトで使用する場合:

<bean id="jdbcDeviceDao" primary="true" class="com.initech.service.dao.jdbc.JdbcDeviceDao">

@Primary プロダクションBeanに注釈を付けることで簡単にスタブバージョンに置き換えることができる場合は、統合テストにも最適です。


質問で、Spring 2.5を使用していることを説明するのを忘れていたので(質問を編集しました)、@ Primaryはオプションではありません。
サイモン

1
@simon:primary=""属性は以前に利用可能だったと思います。HibernateDeviceDaoXMLで宣言し、コンポーネント/注釈スキャンから除外するだけです。
Tomasz Nurkiewicz 2012年

1
ドキュメントによると、それは3.0以降で利用可能です:static.springsource.org/spring/docs/3.1.x/javadoc-api/org/…とにかく良いヒントですが、できれば次のプロジェクトのプライマリアノテーションを覚えておきますSpring 3.xを使用するには
Simon

8

Spring 2.5の場合、はありません@Primary。唯一の方法はを使用すること@Qualifierです。


2
The use of @Qualifier will solve the issue.
Explained as below example : 
public interface PersonType {} // MasterInterface

@Component(value="1.2") 
public class Person implements  PersonType { //Bean implementing the interface
@Qualifier("1.2")
    public void setPerson(PersonType person) {
        this.person = person;
    }
}

@Component(value="1.5")
public class NewPerson implements  PersonType { 
@Qualifier("1.5")
    public void setNewPerson(PersonType newPerson) {
        this.newPerson = newPerson;
    }
}

Now get the application context object in any component class :

Object obj= BeanFactoryAnnotationUtils.qualifiedBeanOfType((ctx).getAutowireCapableBeanFactory(), PersonType.class, type);//type is the qualifier id

you can the object of class of which qualifier id is passed.

0

@Resource(name = "{your child class name}")は機能するが、@ Autowiredが機能しないことがある理由は、マッチングシーケンスの違いによるものです。

@Autowire
タイプ、修飾子、名前の一致シーケンス

@Resourceの
名前、タイプ、修飾子の一致シーケンス

より詳細な説明はここにあります:
インジェクトとリソースとAutowiredアノテーション

この場合、親クラスまたはインターフェースから継承された異なる子クラスは、同じタイプからのものであるため、@ Autowireを混乱させます。@ResourceはNameを最初に一致する優先度として使用するため、機能します。

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