Розуміння використання режиму Spring @Autwired


309

Я читаю довідкову документацію 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?

Відповіді:


542

TL; DR

Анотація @Autowired позбавляє вас від необхідності робити електропроводку у файлі XML (чи будь-яким іншим способом) і просто знаходить для вас, що потрібно вводити куди, і робить це для вас.

Повне пояснення

@AutowiredАнотацій дозволяє пропускати конфігурації в іншому місці , що вводити і просто робить це для вас. Припустимо, що com.mycompany.moviesви маєте пакет, ви повинні помістити цей тег у свій XML (контекстний файл програми):

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

Цей тег виконає автоматичне сканування. Якщо припустити, що кожен клас, який повинен стати квасолею, позначається правильною анотацією, як-от @Component(для простого квасолі) або @Controller(для керування сервлетом) або @Repository(для DAOкласів), і ці класи десь під пакетом com.mycompany.movies, Весна знайде все це і створить квасоля для кожного. Це робиться в 2 сканування класів - перший раз він просто шукає класи, які повинні стати бобом, і картографує ін'єкції, які йому потрібно робити, а під час другого сканування вводить боби. Звичайно, ви можете визначити свої квасолі в більш традиційному XML-файлі або за допомогою класу @Configuration (або будь-якої комбінації трьох).

В @Autowiredанотації йдеться про весну, де потрібно зробити ін'єкцію. Якщо ви покладете його на метод, setMovieFinderвін зрозуміє (за префіксом set+ @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щоб сказати, яку з двох кольорових бобів ввести:

@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оскільки воно не є специфічним для весни та є частиною JSR-330стандарту .
  2. Ще однією хорошою практикою було б поставити @Inject/ @Autowiredна конструктор замість методу. Якщо ви покладете його на конструктор, ви можете перевірити, що введена квасоля не є нульовою та швидко виходить з ладу при спробі запуску програми та уникає, NullPointerExceptionколи вам потрібно фактично використовувати боб.

Оновлення : Щоб завершити малюнок, я створив нове запитання про @Configurationклас.


6
Просто для завершення вашої дивовижної відповіді: "@Ресурс" є частиною стандарту JSR-250 і має додаткову семантику над простою ін'єкцією JSR-330) :)
Ігнасіо Рубіо

Якщо MovieFinderце інтерфейс, і у нас є боб для MovieFinderImpl(bean id = movieFinder), Spring автоматично вводить його за типом або за назвою?
Jaskey

@jaskey - це залежить від того, чи використовуєте ви @Qualifier. Якщо ви - по імені, якщо ні - за типом. Типовий тип працював би лише у тому випадку, якщо MovieFinderу вашому контексті є лише один тип квасолі . Більше 1 призведе до виключення.
Аві

@Avi, Чудова відповідь. Але я не розумію, як @Autowiredанотація працює над prepareметодом у прикладі 2 . Це ініціалізація, MovieRecommenderале, технічно, це НЕ сетер.
Karan Chadha

@KaranChadha - @Autowiredтакож працює для конструкторів. Він знаходить необхідні залежності та вводить їх у конструктор.
Аві

21

Ніщо в прикладі не говорить про те, що "класи реалізують той же інтерфейс". MovieCatalogє типом і CustomerPreferenceDaoє іншим типом. Весна може легко їх розрізнити.

Навесні 2.x проводка квасолі здебільшого відбувається через ідентифікатори бобів або назви. Це все ще підтримується Spring 3.x, але часто у вас буде один екземпляр квасолі з певним типом - більшість служб є однотонними. Створювати імена для них втомливо. Тож Весна почала підтримувати "автопровід за типом".

Наведені приклади показують різні способи використання ін'єкційних бобів у поля, методи та конструктори.

XML вже містить всю інформацію, яка потрібна Spring, оскільки вам потрібно вказати повноцінне ім’я класу в кожному бобі. Потрібно бути обережним з інтерфейсами, хоча:

Ця автоматична проводка не вдасться:

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

Оскільки Java не зберігає імена параметрів у байтовому коді, Spring вже не може розрізняти два боби. Виправлення полягає у використанні @Qualifier:

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

@AaronDigulla Це було приємно. Однак я хочу знати, як ви викликаєте функцію prepare, які параметри будуть використовуватися для виклику цієї функції?
Nguyen Quang Anh

@NguyenQuangAnh Я не закликаю цей метод, Spring зробить це, коли боб буде створений. Це відбувається саме тоді, коли @Autowiredполя вводяться. Потім Spring побачить, що потрібні параметри, і він буде використовувати ті самі правила, які використовуються для введення поля, щоб знайти параметри.
Аарон Дігулла

5

Так, ви можете налаштувати файл xml контексту Spring servlet для визначення ваших бобів (тобто класів), щоб він міг зробити автоматичну ін'єкцію за вас. Однак зауважте, що вам потрібно зробити інші конфігурації, щоб мати Spring і запущений, і найкращий спосіб це зробити - це слідувати підручнику вгору.

Після того, як ви, можливо, настроїли Spring, ви можете зробити наступне у своєму файлі xml-файлу Spring servlet для прикладу 1 для роботи вище (будь ласка, замініть ім'я пакета com.movies на те, що справжнє ім'я пакета, і якщо це третя сторона class, тоді переконайтеся, що відповідний файл jar знаходиться на classpath):

<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 ' - інша квасоля, яка має посилання на очікуваний клас.


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