Понимание Spring @Пользовательское использование

Я читаю справочную документацию весны 3.0.x, чтобы понять аннотацию Spring Autowired:

3.9.2 @Autowired и @Inject

Я не могу понять приведенные ниже примеры. Нужно ли нам что-то делать в XML, чтобы он работал?

ПРИМЕР 1

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}

ПРИМЕР 2

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(MovieCatalog movieCatalog,
                    CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

Как можно использовать два класса для реализации одного и того же интерфейса и использования одного и того же класса?

Пример:

class Red implements Color
class Blue implements Color

class myMainClass{
    @Autowired 
    private Color color;

    draw(){
        color.design(); 
    } 
}

Какой метод проектирования будет называться? Как я могу убедиться, что метод проектирования класса Red будет вызываться, а не Blue?

Ответ 1

TL; DR

Аннотации @Autowired избавляют вас от необходимости прокладывать проводку самостоятельно в файле XML (или любым другим способом) и просто находят для вас, что нужно вводить туда, где и что вы делаете для вас.

Полное объяснение

@Autowired аннотация позволяет пропустить конфигурации в другом месте, что нужно вводить, и просто делает это для вас. Предполагая, что ваш пакет com.mycompany.movies вы должны поместить этот тег в свой XML файл (контекстный файл приложения):

<context:component-scan base-package="com.mycompany.movies" />

Этот тег будет выполнять автоматическое сканирование. Предполагая, что каждый класс, который должен стать bean-компонентом, аннотируется с помощью правильной аннотации, такой как @Component (для простого компонента) или @Controller (для управления сервлетом) или @Repository (для классов DAO), и эти классы находятся где-то под пакетом com.mycompany.movies, Spring найдет все это и создаст компонент для каждого из них. Это делается при 2-х проверках классов - в первый раз, когда он просто ищет классы, которые должны стать компонентом, и отображает инъекции, которые он должен выполнять, а на втором сканировании он вводит компоненты. Конечно, вы можете определить свои компоненты в более традиционном XML файле или в классе @Configuration (или любой комбинации из трех).

@Autowired сообщает Spring, где должна произойти инъекция. Если вы положили его на метод setMovieFinder он понимает (по set префикса + @Autowired annotation), что необходимо @Autowired компонент. Во втором сканировании Spring ищет компонент типа MovieFinder, и если он находит такой компонент, он вводит его в этот метод. Если он найдет два таких компонента, вы получите Exception. Чтобы избежать Exception, вы можете использовать аннотацию @Qualifier и указать, какой из двух компонентов будет вводить следующим образом:

@Qualifier("redBean")
class Red implements Color {
   // Class code here
}

@Qualifier("blueBean")
class Blue implements Color {
   // Class code here
}

Или, если вы предпочитаете объявлять компоненты в своем XML, это выглядит примерно так:

<bean id="redBean" class="com.mycompany.movies.Red"/>

<bean id="blueBean" class="com.mycompany.movies.Blue"/>

В объявлении @Autowired вам нужно также добавить @Qualifier чтобы указать, какая из двух цветовых @Qualifier для инъекции:

@Autowired
@Qualifier("redBean")
public void setColor(Color color) {
  this.color = color;
}

Если вы не хотите использовать две аннотации (@Autowired и @Qualifier), вы можете использовать @Resource для объединения этих двух:

@Resource(name="redBean")
public void setColor(Color color) {
  this.color = color;
}

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

Я просто добавлю еще два комментария:

  1. Хорошей практикой было бы использовать @Inject вместо @Autowired потому что он не зависит от Spring и является частью стандарта JSR-330.
  2. Другой хорошей практикой было бы поставить @Inject/@Autowired на конструктор вместо метода. Если вы поместите его в конструктор, вы можете проверить, что введенные bean-компоненты не являются нулевыми и не работают быстро, когда вы пытаетесь запустить приложение и избегаете исключения NullPointerException когда вам нужно фактически использовать компонент.

Обновление. Чтобы завершить изображение, я создал новый вопрос о классе @Configuration.

Ответ 2

Ничто в этом примере не говорит о том, что "классы реализуют один и тот же интерфейс". MovieCatalog - тип, а CustomerPreferenceDao - другой тип. Spring могут легко отличить их друг от друга.

В Spring 2.x проводка beans в основном произошла с помощью идентификаторов или имен bean. Это все еще поддерживается Spring 3.x, но часто у вас будет один экземпляр bean с определенным типом - большинство служб - это одиночные. Создание имен для них утомительно. Итак, Spring начал поддерживать "autowire by type".

В примерах показаны различные способы, которые вы можете использовать для ввода beans в поля, методы и конструкторы.

XML уже содержит всю информацию, необходимую Spring, поскольку вам нужно указать полное имя класса в каждом bean. Однако вам нужно быть немного осторожным с интерфейсами:

Это автоматическое завершение не будет выполнено:

 @Autowired
 public void prepare( Interface1 bean1, Interface1 bean2 ) { ... }

Так как Java не сохраняет имена параметров в байтовом коде, Spring больше не может различать два beans. Исправление состоит в использовании @Qualifier:

 @Autowired
 public void prepare( @Qualifier("bean1") Interface1 bean1,
     @Qualifier("bean2")  Interface1 bean2 ) { ... }

Ответ 3

Да, вы можете настроить XML файл Spring сервлета контекста, чтобы определить ваш beans (т.е. классы), чтобы он мог выполнить автоматическую инъекцию для вас. Однако имейте в виду, что вам нужно делать другие конфигурации, чтобы работать с Spring и лучший способ сделать это, - это следовать за учебником.

Как только у вас будет настроен ваш Spring, вы можете сделать следующее в вашем xml файле контекста сервлета Spring для примера 1 выше, чтобы работать (пожалуйста, замените имя пакета com.movies к тому, что истинное имя пакета и если это сторонний класс, тогда убедитесь, что соответствующий файл jar находится в пути к классам):

<beans:bean id="movieFinder" class="com.movies.MovieFinder" />

или если класс MovieFinder имеет конструктор с примитивным значением, то вы могли бы что-то вроде этого,

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg value="100" />
</beans:bean>

или если класс MovieFinder имеет конструктор, ожидающий другого класса, тогда вы можете сделать что-то вроде этого,

<beans:bean id="movieFinder" class="com.movies.MovieFinder" >
    <beans:constructor-arg ref="otherBeanRef" />
</beans:bean>

... где "otherBeanRef" - ​​это еще один bean, который ссылается на ожидаемый класс.

Ответ 4

@Autowired

Пусть Spring автоматически подключит другие beans к вашим классам с помощью @Autowired аннотации.

@Service
public class CompanyServiceImpl implements CompanyService {

    @Autowired
    private CompanyDAO companyDAO;

    ...
}

Spring Конспект аннотации Spring beans может быть связан по имени или по типу. @Autowire по умолчанию является инъекцией типа. Аннотацию @Qualifier Spring можно использовать для дальнейшей тонкой настройки автоподготовки. Аннотацию @Resource (javax.annotation.Resource) можно использовать для проводки по имени. Beans, которые сами по себе определяются как тип коллекции или карты, не могут быть введены через @Autowired, поскольку сопоставление типов не относится к ним надлежащим образом. Используйте @Resource для такого beans, ссылаясь на конкретную коллекцию или карту bean по уникальному имени