DI внедряет String bean как отдельный элемент List <String>вместо правильного List <String>bean - PullRequest
0 голосов
/ 29 апреля 2019

Я впервые изучаю Spring Framework и DI и пробую небольшое тестовое приложение с Spring-boot 1.2.0 (ограничение проекта из-за версии Spring Framework, требуемой для 4.1.x), я создал BookBean, который имеет два атрибута: заголовок строки и список авторов. По-видимому, DI вводит заголовок как единственный член списка, и я абсолютно не понимаю, почему.

Я добавил некоторые записи в журнал для методов, помеченных @Bean, чтобы увидеть, когда они вызываются, плюс конструктор BookBean, и я заметил, что метод String вызывается ПОСЛЕ вызова конструктора:

[2019-04-29 14:46:05.631] boot - 3888  INFO [main] --- CollectionConfig: returning title: [A sample book]
[2019-04-29 14:46:05.637] boot - 3888  INFO [main] --- BookBean: construction called
[2019-04-29 14:46:05.649] boot - 3888  INFO [main] --- CollectionConfig: returning authors: [[John, Adam, Harry]]

Это заставляет меня верить, что DI не имеет доступного bean-компонента List при попытке создать BookBean, и «делает следующее лучшее», возвращая List, введенный с единственным известным ему String-бином: title.

В свою очередь, это заставило меня поверить, что я, возможно, неправильно делаю всю автопроводку, и что я не могу выстроить автопроводку по типу / имени в соответствии с запросом. Насколько я понимаю, по умолчанию autowire - это Type, и что конструктор должен попытаться найти bean-компоненты двух разных типов: String и List, но я попытался аннотировать bean-компоненты с помощью @Bean (name = "title" / "авторы" ) безуспешно. Если я также попытаюсь аннотировать параметры конструктора с помощью @Qualifier («title» / «авторы»), я получаю следующую ошибку:

[2019-04-29 14:54:25.847] boot - 20824  INFO [main] --- CollectionConfig: returning title: [A sample book]
[2019-04-29 14:54:25.853] boot - 20824  WARN [main] --- AnnotationConfigEmbeddedWebApplicationContext: Exception encountered during context initialization - cancelling refresh attempt
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'bookBean' defined in URL [jar:file:/C:/dev/Repos/branches/branch_rest_remake/branch_2019_04_11/webapp/target/webapp-0.0.1-SNAPSHOT.jar!/com/company/spring/webapp/domain/BookBean.class]: Unsatisfied dependency expressed through constructor argument with index 1 of type [java.util.List]: : No qualifying bean of type [java.lang.String] found for dependency [collection of java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=authors)}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.String] found for dependency [collection of java.lang.String]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=authors)}

Это мои классы: (РЕДАКТИРОВАТЬ - удалены экземпляры Logger из примера, но они есть в коде)

@RestController
@RequestMapping("/")
public class DummyController {
    @Autowired
    private BookBean bookBean;

    @RequestMapping("/di")
    String dummyDependencyInjection() {
        logger.info("called /di");
        return bookBean.printAuthors();
    }
}
@Configuration
public class CollectionConfig {
    @Bean
    public String title() {
        String title = "A sample book";
        logger.info("returning title: ["+title+"]");
        return title;
    }

    @Bean
    public List<String> authors() {
        List<String> authors = Arrays.asList("John", "Adam", "Harry");
        logger.info("returning authors: ["+authors+"]");
        return authors;
    }
}
@Component
public class BookBean {
    private String title;
    private List<String> authors;

    @Autowired
    public BookBean(String title, List<String> authors) {
        logger.info("construction called");
        this.title = title;
        this.authors = authors;
    }

    public String printAuthors() {
        logger.info("printAuthors called");
        return "Authors in "+ title + "\n"+authors;
    }
}

Без аннотаций @Qualifier bean-компонент создается с заголовком: «Образец книги» и авторы: [«Образец книги»], когда фактическое значение для авторов должно быть: [«Джон», «Адам», « Гарри "]

С аннотацией @Qualifier просто не удается загрузиться.

Ответы [ 2 ]

2 голосов
/ 29 апреля 2019

По умолчанию @Autowired будет пытаться найти бины по типу. В вашем классе BookBean вы вводите String title и List<String> authors.

Что делает Spring под капотом?

Заголовок строки - он найдет a Бин типа String (если он находит более одного, ему требуется @Qualifier, чтобы определить, какой из них ввести), в вашем случае @Bean String title()

Список авторов - он попытается найти все Бобов типа String, в вашем сценарии у вас есть только один: title()

В заключение, ваш боб List<String> authors() будет недоступен, если вы не введете его @Resource(id ="authors").

Возвращаясь на несколько шагов назад, вы никогда не должны полагаться на примитивы для внедрения зависимостей, поскольку они могут ввести вас в заблуждение. Я бы предложил обернуть их в некоторые классы, a.k.a определить класс Title и класс Authors , если , вам действительно нужно использовать внедрение зависимостей.

1 голос
/ 29 апреля 2019

Когда аннотация @Bean присутствует в типизированной коллекции или массиве, компонент автоматически заполняется всеми компонентами этого типа, зарегистрированными в контексте приложения (см. справочная документация ).В вашем случае это title бывшее (A sample book), поэтому список authors содержит только эту запись.

Если вы хотите автоматически связать конкретный компонент, вы можете сослаться на имя, используя @Qualifier аннотация (см. справочная документация ).

В вашем случае конструктор можно переписать так:

@Autowired
public BookBean(String title, @Qualifier("authors") List<String> authors) {
    this.title = title;
    this.authors = authors;
}

При использовании набранногоколлекции, которые справочная документация предлагает на самом деле (прокрутите немного вниз под серой секцией).Вы должны использовать аннотацию @Resource:

private String title;

@Resource
private List<String> authors;

public BookBean(String title) {
    this.title = title;
}
...