Làm thế nào để Autowire Bean của loại chung <T> trong Spring?


81

Tôi có một bean Item<T>được yêu cầu phải được tự động tải trong một @Configurationlớp.

@Configuration
public class AppConfig {

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

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

}

Nhưng khi tôi cố gắng @Autowire Item<String>, tôi nhận được một ngoại lệ sau đây.

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

Làm cách nào để tôi tự động gõ chung chung Item<T>trong Spring?

Câu trả lời:


144

Giải pháp đơn giản là nâng cấp lên Spring 4.0 vì nó sẽ tự động coi generic là một dạng @Qualifier, như sau:

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

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

Trong thực tế, bạn thậm chí có thể tự động truyền tải các generic lồng nhau khi đưa vào danh sách, như sau:

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

Làm thế nào điều này hoạt động?

Lớp mới ResolvableTypecung cấp logic của việc thực sự làm việc với các kiểu chung. Bạn có thể tự mình sử dụng nó để dễ dàng điều hướng và giải quyết thông tin loại. Hầu hết các phương thức trên ResolvableTypesẽ tự trả về một ResolvableType, ví dụ:

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

Xem các Ví dụ & Hướng dẫn tại các liên kết bên dưới.

Hy vọng điều này sẽ giúp bạn.


10
Thật đáng kinh ngạc. Tôi nghĩ rằng Type Erasure đã làm cho điều này không thể thực hiện được.
John Strickler

5
Làm cách nào để lấy bean bằng kiểu phân giải từ beanfactory?
ceram1

@JohnStrickler Tôi cũng nghĩ rằng điều này không thể được thực hiện một cách an toàn. Tôi vẫn chưa kiểm tra việc triển khai ResolvableType, nhưng điều đó có nghĩa là chúng ta có thể nhận được loại chung một cách an toàn ngay bây giờ không?
xi.lin

2
Trên thực tế Trong một số trường hợp, bạn có thể nhận được kiểu chung trong thời gian chạy trong Java. Một điều kiện, bạn cần có lớp con của lớp chung chung. Ví dụ: Lớp public class Parent<T> {}con của lớp cha public class Child extends Parent<Integer> { }và bây giờ: Child c = new Child(); System.out.println(((ParameterizedType)c.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);sẽ inclass java.lang.Integer
Michał Przybylak

1
Điều này có hoạt động với tự động cấu hình bean quét gói không? Tôi không nghĩ vậy.
Preslav Rachev

12

Nếu bạn không muốn nâng cấp lên Spring 4, bạn phải autowire theo tên như sau:

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

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

Bạn muốn làm việc với bê tông StringItem& IntegerItemlớp học trực tiếp?
user3374518 Ngày

2

Dưới đây là giải pháp tôi đã thực hiện để trả lời câu hỏi này:


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

BTW in Spring 5+
howard791006

1

Chiến lược tự động mong muốn của mùa xuân được xác định trong tệp cấu hình của bạn (application.xml).

nếu bạn không xác định, mặc định là theo Loại, tiêm lò xo sử dụng cơ chế phản ánh JDK.

vậy Danh sách? Chuỗi? và List? item ?, loại là giống List.class, vì vậy mùa xuân nhầm lẫn làm thế nào để tiêm.

và như phản hồi của những người ở trên, bạn nên chỉ @Qualifier để cho mùa xuân biết loại đậu nào nên được tiêm.

tôi thích tệp cấu hình mùa xuân để xác định bean thay vì sau đó là Chú thích.

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


0

Spring 4.0 là câu trả lời với việc sử dụng chú thích @Qualifier. Hi vọng điêu nay co ich


0

Tôi tin rằng nó không liên quan gì đến thuốc generic ... Nếu bạn đang tiêm hai loại đậu khác nhau cùng loại thì bạn cần cung cấp một định tính để giúp Spring xác định chúng;

... Ở những nơi khác

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

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

Nếu bạn có các khai báo không chung chung như thế này thì bạn cần thêm bộ định lượng để giúp Spring xác định chúng ...

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

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

Tất nhiên, trong các phiên bản mùa xuân 4 trở lên, việc xem xét Các loại Chung thông qua các trình phân giải, điều này rất thú vị ...

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.