Различия между применением аннотаций @Resource, @Autowired и @Inject для внедрения зависимостей под spring

Spring

Чтобы изучить, как '@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

  1. Matches by Type
  2. Restricts by Qualifiers
  3. Matches by Name

@Resource

  1. Matches by Name
  2. Matches by Type
  3. 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 в стиле аннотаций.