Як вести Autowire Bean загального типу <T> навесні?


83

У мене є квасоля, Item<T>яку потрібно автоматично підключати в @Configurationкласі.

@Configuration
public class AppConfig {

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

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

}

Але коли я намагаюся @Autowire Item<String>, я отримую наступний виняток.

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

Як я повинен вести загальний тип Autowire Item<T>навесні?

Відповіді:


145

Найпростішим рішенням є оновлення до Spring 4.0, оскільки воно автоматично розглядатиме дженерики як одну із форм @Qualifier, як показано нижче:

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

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

Фактично, ви навіть можете автоматично підключати вкладені дженерики при введенні до списку, як показано нижче:

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

Як це працює?

Новий ResolvableTypeклас забезпечує логіку фактичної роботи із загальними типами. Ви можете використовувати його самостійно, щоб легко переходити та визначати інформацію про тип. Більшість методів ResolvableTypeсамі повертають a ResolvableType, наприклад:

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

Перегляньте приклади та підручники за посиланнями нижче.

Сподіваюся, це вам допоможе.


11
Це дивно. Я думав, що Type Erasure зробив подібні речі неможливими.
John Strickler

6
Як я можу отримати bean за розв'язним типом з beanfactory?
ceram1

@JohnStrickler Я також думав, що цього не можна зробити безпечно. Я ще не перевіряв реалізацію ResolvableType, але чи означає це, що зараз ми можемо безпечно отримати загальний тип?
xi.lin

2
Насправді У деяких випадках ви можете отримати загальний тип під час виконання в Java. Одна умова - вам потрібно мати підклас загального класу. Приклад: public class Parent<T> {}Підклас батьківського класу public class Child extends Parent<Integer> { }і зараз: Child c = new Child(); System.out.println(((ParameterizedType)c.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);друкуєтьсяclass java.lang.Integer
Міхал Пшибилак

1
Чи буде це працювати з автоматичною конфігурацією компонента сканування пакетів? Я не думаю.
Преслав Рачев,

12

Якщо ви не хочете переходити на Spring 4, вам доведеться автоматично підключити до імені, як показано нижче:

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

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

Ви маєте на увазі працювати безпосередньо з бетоном StringItemта IntegerItemкласами?
user3374518

2

Нижче наведено рішення для відповіді на це запитання:


        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 навесні 5+
howard791006

1

Весняна стратегія автоматичного підключення визначена у вашому файлі конфіграції (application.xml).

якщо ви не визначили, за замовчуванням використовується тип, пружинне впорскування використовує механізм відображення JDK.

так Список? Рядок? та List? Item?, тип такий же List.class, тому весна заплутала, як вводити.

і, як відповідь вищевказаних людей, вам слід вказати @Qualifier, щоб повідомити пружині, який боб слід вводити.

мені подобається файл весняної конфіграції для визначення компонента, а не анотації.

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


0

Spring 4.0 - це відповідь із використанням анотації @Qualifier. Сподіваюся, це допомагає


0

Я вважаю, що це не має ніякого відношення до дженериків ... Якщо ви вводите дві різні квасолі одного типу, то вам потрібно надати кваліфікатор, який допоможе Spring визначити їх;

... В іншому місці

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

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

Якщо у вас є не загальні декларації, подібні до цих, вам потрібно додати кваліфікатор, який допоможе Spring визначити їх ...

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

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

Звичайно, у версіях 4 і вище spring розглядає загальні типи через роздільники, що дуже круто ...

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.