Tự động cài đặt hai bean thực hiện cùng một giao diện - làm thế nào để đặt bean mặc định thành autowire?


138

Lý lịch:

Tôi có một ứng dụng Spring 2.5 / Java / Tomcat. Có loại đậu sau, được sử dụng trong suốt ứng dụng ở nhiều nơi

public class HibernateDeviceDao implements DeviceDao

và đậu sau đây là mới:

public class JdbcDeviceDao implements DeviceDao

Bean đầu tiên được cấu hình như vậy (bao gồm tất cả các bean trong gói)

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

Bean thứ hai (mới) được cấu hình riêng

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

Kết quả này (tất nhiên) trong một ngoại lệ khi khởi động máy chủ:

ngoại lệ lồng nhau là org.springframework.beans.factory.NoSuchBeanDefTHERException: Không có bean duy nhất nào thuộc loại [com.sevenp.mobile.samplemgmt.service.dao.DeviceDao] được định nghĩa: bean phù hợp dự kiến ​​nhưng được tìm thấy 2: [deviceDao, jdbc

từ một lớp học cố gắng tự động đậu như thế này

@Autowired
private DeviceDao hibernateDevicDao;

bởi vì có hai hạt thực hiện cùng một giao diện.

Câu hỏi:

Có thể cấu hình đậu sao cho

1. Tôi không cần phải thực hiện thay đổi đến các lớp học hiện có, mà đã có HibernateDeviceDaoautowired

2. vẫn có thể sử dụng bean thứ hai (mới) như thế này:

@Autowired
@Qualifier("jdbcDeviceDao")

Tức là tôi sẽ cần một cách để cấu hình HibernateDeviceDaobean làm bean mặc định được tự động, đồng thời cho phép sử dụng một JdbcDeviceDaokhi rõ ràng chỉ định như vậy với @Qualifierchú thích.

Những gì tôi đã thử:

Tôi đã thử thiết lập tài sản

autowire-candidate="false"

trong cấu hình bean cho JdbcDeviceDao:

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

bởi vì tài liệu về mùa xuân nói rằng

Cho biết liệu có nên xem xét loại đậu này hay không khi tìm kiếm các ứng cử viên phù hợp để đáp ứng các yêu cầu tự động của một loại đậu khác. Lưu ý rằng điều này không ảnh hưởng đến các tham chiếu rõ ràng theo tên, điều này sẽ được giải quyết ngay cả khi bean được chỉ định không được đánh dấu là một ứng cử viên tự động. *

mà tôi đã giải thích có nghĩa là tôi vẫn có thể tự động JdbcDeviceDaosử dụng @Qualifierchú thích và có HibernateDeviceDaobean mặc định. Tuy nhiên, rõ ràng cách giải thích của tôi không đúng, vì điều này dẫn đến thông báo lỗi sau khi khởi động máy chủ:

Sự phụ thuộc không thỏa mãn của loại [class com.sevenp.mobile.samplemgmt.service.dao.jdbc.JdbcDeviceDao]: dự kiến ​​ít nhất 1 bean phù hợp

đến từ lớp học mà tôi đã thử tự động đậu với vòng loại:

@Autowired
@Qualifier("jdbcDeviceDao")

Giải pháp:

đề nghị của skaffman để thử chú thích @Resource hoạt động. Vì vậy, cấu hình có ứng cử viên tự động được đặt thành false cho jdbcDeviceDao và khi sử dụng jdbcDeviceDao, tôi đề cập đến nó bằng cách sử dụng chú thích @Resource (thay vì @Qualifier):

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

Nếu tôi sử dụng giao diện này tại 100 không gian trong mã và tôi muốn chuyển tất cả sang triển khai khác, tôi không muốn thay đổi vòng loại hoặc chú thích tài nguyên ở mọi nơi. Và tôi cũng không muốn thay đổi mã của một trong hai triển khai. Tại sao không có khả năng ràng buộc rõ ràng như trong Guice?
Daniel Hári

Câu trả lời:


134

Tôi khuyên bạn nên đánh dấu lớp DAO Hibernate bằng @Primary, tức là (giả sử bạn đã sử dụng @Repositorytrên HibernateDeviceDao):

@Primary
@Repository
public class HibernateDeviceDao implements DeviceDao

Bằng cách này, nó sẽ được chọn làm ứng cử viên tự động mặc định, không cần autowire-candidatetrên các bean khác.

Ngoài ra, thay vì sử dụng @Autowired @Qualifier, tôi thấy nó thanh lịch hơn khi sử dụng @Resourceđể chọn các loại đậu cụ thể, tức là

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

Tôi đã quên đề cập đến trong câu hỏi rằng tôi đang sử dụng Spring 2.5 (hiện tôi đã chỉnh sửa câu hỏi) vì vậy @Primary không phải là một lựa chọn.
simon

1
@simon: Vâng, điều đó khá quan trọng. Hãy thử @Resourcechú thích, như tôi cũng đề nghị.
skaffman

1
Cảm ơn, chú thích tài nguyên đã giải quyết vấn đề - bây giờ thuộc tính ứng viên tự động hoạt động như tôi mong đợi.
simon

Cảm ơn! Có gì khác biệt giữa việc chỉ định tên bean thông qua @Resource@Qualifier, ngoài thực tế là cái trước tương đối mới hơn cái sau?
vào

1
@asgie Sử dụng chú thích tài nguyên đơn giản hóa mọi thứ. Thay vì có kết hợp tự động / vòng loại, bạn có thể đánh dấu nó để tiêm phụ thuộc và chỉ định tên trong một dòng. Lưu ý rằng giải pháp của simon là dư thừa, có thể xóa chú thích tự động.
Dao găm Gilbert Arenas

37

Thế còn @Primary ?

Chỉ ra rằng một bean nên được ưu tiên khi nhiều ứng cử viên đủ điều kiện để tự động phụ thuộc một giá trị duy nhất. Nếu chính xác một hạt 'chính' tồn tại trong số các ứng cử viên, đó sẽ là giá trị tự động. Chú thích này tương đương về mặt ngữ nghĩa với thuộc tính <bean>của phần tử primarytrong Spring XML.

@Primary
public class HibernateDeviceDao implements DeviceDao

Hoặc nếu bạn muốn phiên bản Jdbc của bạn được sử dụng theo mặc định:

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

@Primary cũng rất tốt cho thử nghiệm tích hợp khi bạn có thể dễ dàng thay thế bean sản xuất bằng phiên bản gốc bằng cách chú thích nó.


Tôi đã quên đề cập đến trong câu hỏi rằng tôi đang sử dụng Spring 2.5 (hiện tôi đã chỉnh sửa câu hỏi) vì vậy @Primary không phải là một lựa chọn.
simon

1
@simon: Tôi tin rằng primary=""thuộc tính đã có sẵn trước đó. Chỉ cần khai báo HibernateDeviceDaobằng XML và loại trừ nó khỏi quá trình quét thành phần / chú thích.
Tomasz Nurkiewicz

1
Theo tài liệu, nó có sẵn từ 3.0: static.springsource.org/spring/docs/3.1.x/javadoc-api/org/ mẹo Dù sao đi nữa, tôi sẽ nhớ chú thích chính cho dự án tiếp theo khi tôi có thể để sử dụng Spring 3.x
simon

8

Đối với mùa xuân 2.5, không có @Primary. Cách duy nhất là sử dụng @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

Lý do tại sao @Resource (name = "{tên lớp con của bạn}") hoạt động nhưng đôi khi @Autowired không hoạt động là do sự khác biệt của chuỗi Kết hợp của chúng

Trình tự khớp của @Autowire
Loại , Vòng loại, Tên

Trình tự khớp của @Resource
tên Tên, Loại, Vòng loại

Giải thích chi tiết hơn có thể được tìm thấy ở đây:
Tiêm và tài nguyên và chú thích tự động

Trong trường hợp này, lớp con khác nhau được kế thừa từ lớp cha hoặc giao diện gây nhầm lẫn @Autowire, vì chúng thuộc cùng loại; Vì @Resource sử dụng Tên làm ưu tiên phù hợp đầu tiên, nó hoạt động.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.