Autowiring dua kacang menerapkan antarmuka yang sama - bagaimana mengatur kacang default ke autowire?

140

Latar Belakang:

Saya memiliki aplikasi Spring 2.5 / Java / Tomcat. Ada kacang berikut, yang digunakan di seluruh aplikasi di banyak tempat

public class HibernateDeviceDao implements DeviceDao

dan kacang berikut yang baru:

public class JdbcDeviceDao implements DeviceDao

Kacang pertama dikonfigurasi jadi (semua kacang dalam paket disertakan)

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

Kacang kedua (baru) dikonfigurasi secara terpisah

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

Ini menghasilkan (tentu saja) dalam pengecualian saat memulai server:

pengecualian bersarang adalah org.springframework.beans.factory.NoSuchBeanDefinitionException: Tidak ada jenis kacang unik [com.sevenp.mobile.samplemgmt.service.dao.DeviceDao] yang ditentukan: diharapkan kacang pencocokan tunggal tetapi ditemukan 2: [deviceDao, jdbcDeviceDao]

dari kelas yang mencoba melakukan autowire kacang seperti ini

@Autowired
private DeviceDao hibernateDevicDao;

karena ada dua kacang yang menerapkan antarmuka yang sama.

Pertanyaan:

Apakah mungkin untuk mengkonfigurasi kacang sehingga

1. Saya tidak perlu mengubah kelas yang ada, yang sudah memiliki HibernateDeviceDaoautowired

2. masih bisa menggunakan kacang kedua (baru) seperti ini:

@Autowired
@Qualifier("jdbcDeviceDao")

Yaitu saya akan memerlukan cara untuk mengkonfigurasi HibernateDeviceDaokacang sebagai kacang default untuk menjadi autowired, secara bersamaan memungkinkan penggunaan a JdbcDeviceDaoketika secara eksplisit menentukannya dengan @Qualifierpenjelasan.

Apa yang sudah saya coba:

Saya mencoba mengatur properti

autowire-candidate="false"

dalam konfigurasi bean untuk JdbcDeviceDao:

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

karena dokumentasi Spring mengatakan itu

Menunjukkan apakah kacang ini harus dipertimbangkan saat mencari kandidat yang cocok untuk memenuhi persyaratan autowiring kacang lain. Perhatikan bahwa ini tidak mempengaruhi referensi eksplisit berdasarkan nama, yang akan diselesaikan bahkan jika kacang yang ditentukan tidak ditandai sebagai kandidat autowire. *

yang saya tafsirkan bahwa saya masih bisa melakukan autowire JdbcDeviceDaomenggunakan @Qualifieranotasi dan memiliki HibernateDeviceDaobean sebagai default. Rupanya interpretasi saya tidak benar, karena ini menghasilkan pesan kesalahan berikut saat memulai server:

Ketergantungan jenis [class com.sevenp.mobile.samplemgmt.service.dao.jdbc.JdbcDeviceDao]: diharapkan setidaknya 1 kacang yang cocok

berasal dari kelas tempat saya mencoba melakukan autowiring kacang dengan kualifikasi:

@Autowired
@Qualifier("jdbcDeviceDao")

Larutan:

saran skaffman untuk mencoba anotasi @Resource berhasil. Jadi konfigurasinya memiliki autowire-kandidat disetel ke false untuk jdbcDeviceDao dan saat menggunakan jdbcDeviceDao saya merujuknya menggunakan anotasi @Resource (bukan @Qualifier):

@Resource(name = "jdbcDeviceDao")
private JdbcDeviceListItemDao jdbcDeviceDao;
simon
sumber
Jika saya menggunakan antarmuka ini di 100 tempat dalam kode, dan saya ingin beralih semua ke implementasi lain, saya tidak ingin mengubah kualifikasi atau anotasi sumber daya di semua tempat. Dan saya juga tidak ingin mengubah kode implementasi mana pun. Mengapa tidak ada kemungkinan pengikatan eksplisit seperti di Guice?
Daniel Hári

Jawaban:

134

Saya sarankan menandai kelas Hibernate DAO dengan @Primary, yaitu (dengan asumsi Anda digunakan @Repositorypada HibernateDeviceDao):

@Primary
@Repository
public class HibernateDeviceDao implements DeviceDao

Dengan cara ini akan dipilih sebagai kandidat autowire default, tanpa perlu autowire-candidate di kacang lain.

Selain itu, daripada menggunakan @Autowired @Qualifier, saya merasa lebih elegan digunakan @Resourceuntuk memetik biji tertentu, yaitu

@Resource(name="jdbcDeviceDao")
DeviceDao deviceDao;
skaffman
sumber
Saya lupa menyebutkan dalam pertanyaan bahwa saya menggunakan Spring 2.5 (saya sekarang telah mengedit pertanyaan tersebut) jadi @Primary bukanlah pilihan.
simon
1
@simon: Ya, itu agak penting. Coba @Resourceanotasi, seperti yang juga saya sarankan.
Skaffman
1
Terima kasih, anotasi sumber daya menyelesaikan masalah - sekarang properti kandidat autowire berfungsi seperti yang saya harapkan.
simon
Terima kasih! Apa perbedaan antara menentukan nama kacang melalui @Resourcedan @Qualifier, terlepas dari fakta bahwa yang pertama relatif lebih baru daripada yang terakhir?
asgs
1
@asgs Menggunakan anotasi sumber daya menyederhanakan banyak hal. Alih-alih memiliki kombinasi autowired / qualifier, Anda dapat menandainya untuk injeksi dependensi dan menentukan namanya dalam satu baris. Perhatikan bahwa solusi Simon adalah redundan, anotasi autowired dapat dihapus.
Belati Arena Gilbert
37

Tentang apa @Primary?

Menunjukkan bahwa kacang harus diberi preferensi ketika beberapa kandidat memenuhi syarat untuk mengotorisasi ketergantungan bernilai tunggal. Jika tepat satu kacang 'primer' ada di antara para kandidat, itu akan menjadi nilai autowired. Anotasi ini secara semantik setara dengan atribut <bean>elemen primarydi Spring XML.

@Primary
public class HibernateDeviceDao implements DeviceDao

Atau jika Anda ingin versi Jdbc Anda digunakan secara default:

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

@Primary juga bagus untuk pengujian integrasi ketika Anda dapat dengan mudah mengganti kacang produksi dengan versi stub dengan memberi anotasi.

Tomasz Nurkiewicz
sumber
Saya lupa menyebutkan dalam pertanyaan bahwa saya menggunakan Spring 2.5 (saya sekarang telah mengedit pertanyaan tersebut) jadi @Primary bukanlah pilihan.
simon
1
@ Simon: Saya yakin primary=""atribut tersedia sebelumnya. Cukup deklarasikan HibernateDeviceDaodalam XML dan kecualikan dari pemindaian komponen / anotasi.
Tomasz Nurkiewicz
1
Menurut dokumentasinya, ini tersedia sejak 3.0: static.springsource.org/spring/docs/3.1.x/javadoc-api/org/… Bagaimanapun juga, saya akan mengingat anotasi Primer untuk proyek berikutnya ketika saya bisa untuk menggunakan Spring 3.x
simon
9

Untuk Spring 2.5, tidak ada @Primary. Satu-satunya cara adalah menggunakan @Qualifier.

blang
sumber
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.
Sandeep Jain
sumber
0

Alasan @Resource (name = "{your child class name}") berfungsi tetapi @Autowired terkadang tidak berfungsi adalah karena perbedaan Urutan pencocokannya

Urutan pencocokan
Jenis @Autowire , Qualifier, Name

Urutan pencocokan @Resource
Name, Type, Qualifier

Penjelasan lebih detail dapat ditemukan di sini:
Inject and Resource dan Autowired annotations

Dalam kasus ini, kelas anak berbeda yang diwarisi dari kelas induk atau antarmuka membingungkan @Autowire, karena mereka berasal dari tipe yang sama; Karena @Resource menggunakan Nama sebagai prioritas pencocokan pertama, ini berfungsi.

SINAR
sumber