Чтобы изучить, как '@Resource', '@Autowired' и '@Inject' решают проблему внедрения зависимостей, я создал интерфейс «Party» и два его класса реализации «Person», «Organization». Таким образом, я могу внедрить Beans без использования конкретных типов (имеется в виду использование типов интерфейса). Это также облегчает мне изучение того, как Spring решает неопределенность внедрения зависимостей, когда интерфейс имеет несколько соответствующих ему классов реализации.
public interface Party {}
package com.sourceallies.person;
...
@Component
public class Person implements Party {}
package com.sourceallies.organization;
...
@Component
public class Organization implements Party {}
Установка пакета, в котором два класса реализации, аннотированные «@Component» в файле конфигурации Spring, требуют проверки внедрения
<context:component-scan base-package="com.sourceallies.organization"/>
<context:component-scan base-package="com.sourceallies.person"/>
Тест 1: неоднозначная инъекция bean-компонента
Этот тест проверяет, что при внедрении Party, когда она имеет несколько классов реализации
@Resource
private Party party;
@Autowired
private Party party;
@Inject
private Party party;
Приведенные выше три случая вызывают одно и то же исключение NoSuchBeanDefinitionException. Простой взгляд на имя исключения означает, что соответствующего компонента нет, но детали показывают, что найдены два компонента.
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.sourceallies.Party] is defined: expected single matching bean but found 2: [organization, person]
Тест 2: внедрение имени поля
@Resource
private Party person;
@Autowired
private Party person;
@Inject
private Party person;
Если инъекция @Resource может установить необязательное свойство name, следующий синтаксис эквивалентен приведенной выше аннотации @Resource, но @Autowired и @Inject не имеют подобных эквивалентов.
@Resource(name="person")
private Party party;
Все три вышеперечисленных метода в конечном итоге внедряются в bean-компонент «Person».
Тест 3: Внедрение типа поля
@Resource
private Person party;
@Autowired
private Person party;
@Inject
private Person party;
Во всех вышеперечисленных случаях будет внедрен bean-компонент «Person».
Тест 4: Внедрение с именем класса реализации по умолчанию
@Resource
@Qualifier("person")
private Party party;
@Autowired
@Qualifier("person")
private Party party;
@Inject
@Qualifier("person")
private Party party;
Во всех вышеперечисленных случаях будет внедрен bean-компонент «Person».
Тест 5: укажите имя класса реализующего класса
Используйте аннотацию Qualifier в классе реализации, чтобы указать имя, которое будет использоваться при внедрении.
package com.sourceallies.person;
...
@Component
@Qualifier("personBean")
public class Person implements Party {}
При внедрении используйте аннотацию Qualifier, чтобы указать класс реализации, имя которого нужно вводить.
@Resource
@Qualifier("personBean")
private Party party;
@Autowired
@Qualifier("personBean")
private Party party;
@Inject
@Qualifier("personBean")
private Party party;
Во всех вышеперечисленных случаях будет внедрен bean-компонент «Person».
Тест 6: инъекция коллекции
@Resource
private List<Party> parties;
@Autowired
private List<Party> parties
@Inject
private List<Party> parties;
В приведенных выше случаях будут введены две бобы в списке. Этот метод также может использовать «@qualifier», чтобы квалифицировать впрыск бобов, и каждую фасоль удовлетворяет указанному «квалификатору», будет вводиться в список.
Тест 7: Плохая конфигурация
Использовать несвязанное «плохое» в качестве имени совпадения, указанного «@Qualifier»
@Resource
@Qualifier("bad")
private Party person;
@Autowired
@Qualifier("bad")
private Party person;
@Inject
@Qualifier("bad")
private Party person;
Использование аннотации «@Resource» в этом случае будет игнорировать конфигурацию «@Qualifier», поэтому будет внедрен bean-компонент «Person». Последние два выдают сообщение об ошибке «NoSuchBeanDefinitionException», поскольку не удалось найти компонент, соответствующий конфигурации «@Qualifier».
org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.sourceallies.Party] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true), @org.springframework.beans.factory.annotation.Qualifier(value=bad)}
Суммировать
Сообщения об ошибках '@Autowired' и '@Inject' совершенно одинаковы. Оба они представляют собой внедрение зависимостей, реализованное через класс 'AutowiredAnnotationBeanPostProcessor', и они взаимозаменяемы.
«@Resource» реализует внедрение зависимостей через класс «CommonAnnotationBeanPostProcessor». Несмотря на это, их производительность при внедрении зависимостей очень похожа. Ниже приводится сводка их порядка выполнения при внедрении зависимостей:
@Autowired and @Inject
- Matches by Type
- Restricts by Qualifiers
- Matches by Name
@Resource
- Matches by Name
- Matches by Type
- Restricts by Qualifiers (ignored if match is found by name)
'@Resource' превосходит '@Autowired' и '@Inject' с точки зрения скорости работы при внедрении по имени, но это тривиально и недостаточно, чтобы оправдать предпочтение '@Resource'. Я склонен использовать «@Resource», потому что его проще настроить.
@Resource(name="person")
@Autowired
@Qualifier("person")
@Inject
@Qualifier("person")
Вы можете сказать использовать имя поля по умолчанию в качестве имени компонента при внедрении, и два других способа будут такими же краткими:
@Resource
private Party person;
@Autowired
private Party person;
@Inject
private Party person;
Оно делает. Но как насчет того, когда вам нужно реорганизовать код? При использовании метода '@Resource' нужно просто изменить атрибут имени, не касаясь имени внедренного компонента (имя интерфейса согласовывается при внедрении компонента). Поэтому я рекомендую следовать следующему стилю синтаксиса при внедрении с использованием аннотаций:
1. Явно определите имя компонента в своем компоненте вместо использования по умолчанию [@Component("beanName")].
2. Используйте как @Resource, так и его атрибут name [@Resource(name="beanName")].
3. Избегайте использования аннотации '@Qualifier', если только вы не хотите создать набор похожих bean-компонентов. Например, может потребоваться создать набор наборов для хранения ряда определений «правил». В настоящее время вы можете выбрать метод аннотации @Qualifier. Такой подход позволяет легко поместить в коллекцию большое количество классов, следующих одним и тем же правилам.
4. Используйте следующую конфигурацию, чтобы ограничить пакеты, требующие сканирования отдельных компонентов: [context:component-scan base-package="com.sourceallies.person"]. Это может уменьшить ситуацию, когда Spring сканирует много недопустимых пакетов. Следование приведенным выше принципам может улучшить читаемость и стабильность вашей конфигурации spring в стиле аннотаций.