Понимание 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
(вы можете прочитать некоторые дополнительные данные об этом в первом комментарии к этому ответу) избавляет вас от использования двух аннотаций, и вместо этого вы используете только один.
Я просто добавлю еще два комментария:
- Хорошей практикой было бы использовать
@Inject
вместо @Autowired
потому что он не зависит от Spring и является частью стандарта JSR-330
. - Другой хорошей практикой было бы поставить
@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 по уникальному имени