Autowiring ถั่วสองตัวที่ใช้อินเทอร์เฟซเดียวกัน - วิธีตั้งค่า bean เริ่มต้นเป็น autowire อัตโนมัติอย่างไร


138

พื้นหลัง:

ฉันมีแอปพลิเคชัน Spring 2.5 / Java / Tomcat มีถั่วต่อไปนี้ซึ่งใช้ทั่วทั้งแอปพลิเคชั่นในหลาย ๆ ที่

public class HibernateDeviceDao implements DeviceDao

และถั่วต่อไปนี้ซึ่งเป็นของใหม่:

public class JdbcDeviceDao implements DeviceDao

bean แรกถูกกำหนดค่าให้ดังนั้น (beans ทั้งหมดในแพ็คเกจถูกรวมไว้)

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

ถั่วที่สอง (ใหม่) ถูกกำหนดค่าแยกกัน

<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] พบถั่วที่จับคู่เดี่ยว แต่พบ 2: [deviceDao, jdbcDeviceDao]

จากคลาสที่พยายาม autowire bean แบบนี้

@Autowired
private DeviceDao hibernateDevicDao;

เนื่องจากมีสองถั่วที่ใช้อินเตอร์เฟสเดียวกัน

คำถาม:

เป็นไปได้ไหมที่จะกำหนดค่า beans เพื่อให้

1.ฉันไม่ต้องทำการเปลี่ยนแปลงคลาสที่มีอยู่ซึ่งมีHibernateDeviceDaoautowired อยู่แล้ว

2.ยังคงสามารถใช้ถั่วที่สอง (ใหม่) เช่นนี้:

@Autowired
@Qualifier("jdbcDeviceDao")

นั่นคือฉันจะต้องมีวิธีการกำหนดค่าHibernateDeviceDaoถั่วเป็นถั่วเริ่มต้นให้เป็นแบบอัตโนมัติพร้อมกันทำให้การใช้งานของJdbcDeviceDaoเมื่อระบุอย่างชัดเจนด้วย@Qualifierคำอธิบายประกอบ

สิ่งที่ฉันได้ลองไปแล้ว:

ฉันลองตั้งค่าคุณสมบัติ

autowire-candidate="false"

ในการกำหนดค่า bean สำหรับ JdbcDeviceDao:

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

เพราะเอกสาร Spring บอกว่า

บ่งชี้ว่าควรพิจารณาว่า bean นี้หรือไม่เมื่อค้นหาผู้สมัครที่ตรงกันเพื่อตอบสนองความต้องการในการเข้าร่วมของถั่วอื่น โปรดทราบว่าสิ่งนี้จะไม่ส่งผลกระทบต่อการอ้างอิงที่ชัดเจนโดยใช้ชื่อซึ่งจะได้รับการแก้ไขแม้ว่าถั่วที่ระบุจะไม่ถูกทำเครื่องหมายเป็นตัวเลือกตอบรับอัตโนมัติ *

ซึ่งฉันตีความว่าหมายความว่าฉันยังสามารถ autowire JdbcDeviceDaoโดยใช้@QualifierคำอธิบายประกอบและมีHibernateDeviceDaoถั่วเป็นค่าเริ่มต้น เห็นได้ชัดว่าการตีความของฉันไม่ถูกต้อง แต่เนื่องจากผลลัพธ์ในข้อความแสดงข้อผิดพลาดต่อไปนี้เมื่อเริ่มต้นเซิร์ฟเวอร์:

ไม่พอใจการพึ่งพาประเภท [class com.sevenp.mobile.samplemgmt.service.dao.jdbc.JdbcDeviceDao]: คาดว่าจะมีถั่วที่ตรงกันอย่างน้อย 1 ตัว

มาจากชั้นเรียนที่ฉันลอง autowiring bean พร้อมกับ qualifier:

@Autowired
@Qualifier("jdbcDeviceDao")

สารละลาย:

คำแนะนำของ skaffmanเพื่อลองใช้คำอธิบายประกอบ @Resource ดังนั้นการกำหนดค่ามีการตั้งค่าผู้สมัคร autowire เป็นเท็จสำหรับ jdbcDeviceDao และเมื่อใช้ jdbcDeviceDao ฉันอ้างถึงมันโดยใช้คำอธิบายประกอบ @Resource (แทน @Qualifier):

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

ถ้าฉันใช้อินเทอร์เฟซนี้ที่ 100place ในรหัสและฉันต้องการสลับทั้งหมดเป็นการใช้งานอื่นฉันไม่ต้องการเปลี่ยนคำอธิบายประกอบแบบคัดเลือกหรือทรัพยากรในทุกสถานที่ และฉันก็ไม่ต้องการเปลี่ยนรหัสของการใช้งานอย่างใดอย่างหนึ่ง ทำไมไม่มีความเป็นไปได้ที่มีผลผูกพันอย่างชัดเจนเหมือนใน Guice?
Daniel Hári

คำตอบ:


134

ฉันขอแนะนำให้ทำเครื่องหมายชั้นเรียนไฮเบอร์เนต DAO ด้วย@Primaryเช่น (สมมติว่าคุณใช้@RepositoryบนHibernateDeviceDao):

@Primary
@Repository
public class HibernateDeviceDao implements DeviceDao

วิธีนี้มันจะถูกเลือกเป็นค่าเริ่มต้น autowire candididate โดยไม่จำเป็นต้องautowire-candidateใส่ถั่วอื่น

นอกจากนี้แทนที่จะใช้@Autowired @Qualifierฉันพบว่ามันดีกว่าที่จะใช้@Resourceสำหรับการเลือกเมล็ดเฉพาะเช่น

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

ฉันลืมที่จะพูดถึงในคำถามที่ฉันใช้ Spring 2.5 (ตอนนี้ฉันได้แก้ไขคำถาม) ดังนั้น @Primary จึงไม่ใช่ตัวเลือก
simon

1
@simon: ใช่นั่นค่อนข้างสำคัญ ลองใช้@Resourceคำอธิบายประกอบตามที่ฉันแนะนำ
skaffman

1
ขอบคุณคำอธิบายประกอบทรัพยากรแก้ไขปัญหา - ตอนนี้คุณสมบัติผู้สมัคร autowire ทำงานตามที่ฉันคาดไว้
simon

ขอบคุณ! อะไรคือความแตกต่างระหว่างการระบุชื่อถั่วผ่าน@Resourceและ@Qualifierนอกเหนือจากข้อเท็จจริงที่ว่าชื่อก่อนหน้านี้ค่อนข้างใหม่กว่าชื่อหลัง
asgs

1
@asgs การใช้คำอธิบายประกอบทรัพยากรทำให้สิ่งต่าง ๆ ง่ายขึ้น แทนที่จะมีคำสั่งผสม autowired / qualifier คุณสามารถทำเครื่องหมายสำหรับการฉีดพึ่งพาและระบุชื่อในหนึ่งบรรทัด โปรดทราบว่าวิธีแก้ปัญหาของ simon นั้นซ้ำซ้อนคำอธิบายประกอบแบบอัตโนมัติสามารถลบออกได้
Gilbert Arenas Dagger

37

เกี่ยวกับ@Primaryอะไร

บ่งชี้ว่า bean ควรถูกกำหนดค่าตามความชอบเมื่อผู้สมัครหลายคนมีคุณสมบัติที่จะได้รับการพึ่งพาที่มีมูลค่าเดียว หากมีหนึ่งถั่วหลัก 'อยู่ในหมู่ผู้สมัครมันจะเป็นค่า autowired คำอธิบายประกอบนี้เทียบเท่ากับคุณลักษณะ<bean>ขององค์ประกอบprimaryใน Spring XML

@Primary
public class HibernateDeviceDao implements DeviceDao

หรือหากคุณต้องการให้ใช้ Jdbc เป็นค่าเริ่มต้น:

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

@Primary ยังยอดเยี่ยมสำหรับการทดสอบการรวมกันเมื่อคุณสามารถแทนที่ถั่วการผลิตด้วยเวอร์ชันที่ทำเป็น stubbed ได้อย่างง่ายดายโดยใส่คำอธิบายประกอบ


ฉันลืมที่จะพูดถึงในคำถามที่ฉันใช้ Spring 2.5 (ตอนนี้ฉันได้แก้ไขคำถาม) ดังนั้น @Primary จึงไม่ใช่ตัวเลือก
simon

1
@simon: ฉันเชื่อว่ามีprimary=""แอตทริบิวต์ก่อนหน้านี้ เพียงประกาศHibernateDeviceDaoใน XML และแยกออกจากการสแกนส่วนประกอบ / การเพิ่มความคิดเห็น
Tomasz Nurkiewicz

1
ตามเอกสารมีให้ตั้งแต่ 3.0: static.springsource.org/spring/docs/3.1.x/javadoc-api/org/…คำแนะนำดี ๆ ฉันจะจดจำคำอธิบายประกอบหลักสำหรับโครงการต่อไปเมื่อฉันสามารถ ใช้ Spring 3.x
simon

8

สำหรับฤดูใบไม้ผลิ 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 (ชื่อ = "{ชื่อคลาสลูกของคุณ}") ใช้งานได้ แต่บางครั้ง @Autowired ไม่ทำงานเป็นเพราะความแตกต่างของลำดับการจับคู่

การจับคู่ลำดับของ @Autowire
Type, Qualifier, Name

การจับคู่ลำดับของ
ชื่อ@Resource ประเภทตัวระบุ

คำอธิบายรายละเอียดเพิ่มเติมสามารถดูได้ที่นี่:
คำอธิบายประกอบคำบรรยาย Inject และ Resource และ Autowired

ในกรณีนี้คลาสลูกที่แตกต่างกันซึ่งสืบทอดมาจากคลาสพาเรนต์หรืออินเตอร์เฟสจะสร้างความสับสน @Autowire เนื่องจากเป็นประเภทเดียวกัน เนื่องจาก @Resource ใช้ชื่อเป็นลำดับความสำคัญแรกที่ตรงกัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.