Проводка весной: @Autowired, @Resource и @Inject

Проводка весной: @Autowired, @Resource и @Inject

1. обзор

В этой статье Spring Framework будет продемонстрировано использование аннотаций, связанных с внедрением зависимостей, а именно аннотаций@Resource,@Inject и@Autowired. Эти аннотации предоставляют классам декларативный способ разрешения зависимостей. Например:

@Autowired
ArbitraryClass arbObject;

в отличие от их непосредственного создания (императивный путь), например:

ArbitraryClass arbObject = new ArbitraryClass();

Две из трех аннотаций принадлежат пакету расширения Java:javax.annotation.Resource иjavax.inject.Inject. Аннотация@Autowired принадлежит пакетуorg.springframework.beans.factory.annotation.

Каждая из этих аннотаций может разрешать зависимости либо путем внедрения поля, либо путем установки сеттера. Упрощенный, но практический пример будет использован для демонстрации различия между тремя аннотациями, основанными на путях выполнения, взятых каждой аннотацией.

Примеры будут сосредоточены на том, как использовать три аннотации внедрения во время интеграционного тестирования. Зависимость, требуемая тестом, может быть произвольным файлом или произвольным классом.

2. Аннотация@Resource

Аннотация@Resource является частью коллекции аннотацийJSR-250 и поставляется вместе с Java EE. Эта аннотация имеет следующие пути выполнения, перечисленные по приоритету:

  1. Матч по имени

  2. Совпадение по типу

  3. Матч по классификатору

Эти пути выполнения применимы как к установщику, так и к внедрению поля.

2.1. Полевая инъекция

Разрешение зависимостей путем внедрения поля достигается путем аннотирования переменной экземпляра аннотацией@Resource.

2.1.1. Матч по имени

Интеграционный тест, используемый для демонстрации внедрения поля совпадения по имени, приведен ниже:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceNameType.class)
public class FieldResourceInjectionIntegrationTest {

    @Resource(name="namedFile")
    private File defaultFile;

    @Test
    public void givenResourceAnnotation_WhenOnField_ThenDependencyValid(){
        assertNotNull(defaultFile);
        assertEquals("namedFile.txt", defaultFile.getName());
    }
}

Давайте пройдемся по коду. В интеграционном тестеFieldResourceInjectionTest в строке 7 разрешение зависимости по имени достигается путем передачи имени компонента в качестве значения атрибута аннотации@Resource:

@Resource(name="namedFile")
private File defaultFile;

Эта конфигурация будет разрешать зависимости, используя путь выполнения соответствия по имени. КомпонентnamedFile должен быть определен в контексте приложенияApplicationContextTestResourceNameType.

Обратите внимание, что идентификатор компонента и соответствующее значение ссылочного атрибута должны совпадать:

@Configuration
public class ApplicationContextTestResourceNameType {

    @Bean(name="namedFile")
    public File namedFile() {
        File namedFile = new File("namedFile.txt");
        return namedFile;
    }
}

Неспособность определить компонент в контексте приложения приведет к выдачеorg.springframework.beans.factory.NoSuchBeanDefinitionException. Это можно продемонстрировать, изменив значение атрибута, переданное в аннотацию@Bean, в контексте приложенияApplicationContextTestResourceNameType; или изменение значения атрибута, переданного в аннотацию@Resource, в интеграционном тестеFieldResourceInjectionTest.

2.1.2. Совпадение по типу

Чтобы продемонстрировать путь выполнения соответствия по типу, просто удалите значение атрибута в строке 7 интеграционного тестаFieldResourceInjectionTest, чтобы оно выглядело следующим образом:

@Resource
private File defaultFile;

и снова запустите тест.

Тест все равно пройдет, потому что, если аннотация@Resource не получит имя bean-компонента в качестве значения атрибута, Spring Framework перейдет к следующему уровню приоритета, сопоставлению по типу, чтобы попытаться разрешить зависимость .

2.1.3. Матч по классификатору

Чтобы продемонстрировать путь выполнения соответствия по квалификатору, сценарий интеграционного тестирования будет изменен таким образом, чтобы в контексте приложенияApplicationContextTestResourceQualifier были определены два bean-компонента:

@Configuration
public class ApplicationContextTestResourceQualifier {

    @Bean(name="defaultFile")
    public File defaultFile() {
        File defaultFile = new File("defaultFile.txt");
        return defaultFile;
    }

    @Bean(name="namedFile")
    public File namedFile() {
        File namedFile = new File("namedFile.txt");
        return namedFile;
    }
}

Интеграционный тестQualifierResourceInjectionTest будет использоваться для демонстрации разрешения зависимостей соответствия по квалификатору. В этом сценарии конкретная зависимость bean-компонента должна быть введена в каждую ссылочную переменную:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceQualifier.class)
public class QualifierResourceInjectionIntegrationTest {

    @Resource
    private File dependency1;

    @Resource
    private File dependency2;

    @Test
    public void givenResourceAnnotation_WhenField_ThenDependency1Valid(){
        assertNotNull(dependency1);
        assertEquals("defaultFile.txt", dependency1.getName());
    }

    @Test
    public void givenResourceQualifier_WhenField_ThenDependency2Valid(){
        assertNotNull(dependency2);
        assertEquals("namedFile.txt", dependency2.getName());
    }
}

Запустите интеграционный тест, и выдаетсяorg.springframework.beans.factory.NoUniqueBeanDefinitionException. Это исключение возникает, потому что контекст приложения обнаружил два определения bean-компонента типаFile, и он не знает, какой bean-компонент должен разрешить зависимость.

Чтобы решить эту проблему, обратитесь к строкам 7–10 интеграционного тестаQualifierResourceInjectionTest:

@Resource
private File dependency1;

@Resource
private File dependency2;

и добавьте следующие строки кода:

@Qualifier("defaultFile")

@Qualifier("namedFile")

так что блок кода выглядит следующим образом:

@Resource
@Qualifier("defaultFile")
private File dependency1;

@Resource
@Qualifier("namedFile")
private File dependency2;

Запустите интеграционный тест еще раз, на этот раз он должен пройти. Целью этого теста было продемонстрировать, что даже если в контексте приложения определено несколько bean-компонентов, аннотация@Qualifier устраняет любую путаницу, позволяя внедрять определенные зависимости в класс.

2.2. Сеттер Инъекция

Пути выполнения, используемые при внедрении зависимостей в поле, применимы к внедрению на основе установки.

2.2.1. Матч по имени

Единственная разница в том, что интеграционный тестMethodResourceInjectionTest имеет метод установки:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceNameType.class)
public class MethodResourceInjectionIntegrationTest {

    private File defaultFile;

    @Resource(name="namedFile")
    protected void setDefaultFile(File defaultFile) {
        this.defaultFile = defaultFile;
    }

    @Test
    public void givenResourceAnnotation_WhenSetter_ThenDependencyValid(){
        assertNotNull(defaultFile);
        assertEquals("namedFile.txt", defaultFile.getName());
    }
}

Разрешение зависимостей с помощью внедрения установщика выполняется путем аннотирования соответствующего метода установщика ссылочной переменной. Передайте имя зависимости bean-компонента в качестве значения атрибута аннотации@Resource:

private File defaultFile;

@Resource(name="namedFile")
protected void setDefaultFile(File defaultFile) {
    this.defaultFile = defaultFile;
}

В этом примере будет повторно использоваться зависимость bean-компонентаnamedFile. Имя бина и соответствующее значение атрибута должны совпадать.

Запустите интеграционный тест как есть, и он пройдет.

Чтобы убедиться, что зависимость действительно была разрешена путем выполнения сопоставления по имени, измените значение атрибута, переданное в аннотацию@Resource, на значение по вашему выбору и снова запустите тест. На этот раз тест завершится неудачно сNoSuchBeanDefinitionException.

2.2.2. Совпадение по типу

Чтобы продемонстрировать выполнение на основе установщика совпадений по типам, мы будем использовать интеграционный тестMethodByTypeResourceTest:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceNameType.class)
public class MethodByTypeResourceIntegrationTest {

    private File defaultFile;

    @Resource
    protected void setDefaultFile(File defaultFile) {
        this.defaultFile = defaultFile;
    }

    @Test
    public void givenResourceAnnotation_WhenSetter_ThenValidDependency(){
        assertNotNull(defaultFile);
        assertEquals("namedFile.txt", defaultFile.getName());
    }
}

Запустите этот тест как есть, и он пройдет.

Чтобы убедиться, что зависимостьFile действительно была разрешена путем сопоставления по типу пути выполнения, измените тип класса переменнойdefaultFile на другой тип класса, напримерString. Снова выполните интеграционный тестMethodByTypeResourceTest, и на этот раз будет выданNoSuchBeanDefinitionException.

Исключение подтверждает, что соответствие по типу действительно использовалось для разрешения зависимостиFile. NoSuchBeanDefinitionException подтверждает, что имя ссылочной переменной не обязательно должно совпадать с именем bean-компонента. Вместо этого разрешение зависимости зависит от типа класса bean-компонента, соответствующего типу класса ссылочной переменной.

2.2.3. Матч по классификатору

Интеграционный тестMethodByQualifierResourceTest будет использоваться для демонстрации пути выполнения соответствия по квалификатору:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestResourceQualifier.class)
public class MethodByQualifierResourceIntegrationTest {

    private File arbDependency;
    private File anotherArbDependency;

    @Test
    public void givenResourceQualifier_WhenSetter_ThenValidDependencies(){
      assertNotNull(arbDependency);
        assertEquals("namedFile.txt", arbDependency.getName());
        assertNotNull(anotherArbDependency);
        assertEquals("defaultFile.txt", anotherArbDependency.getName());
    }

    @Resource
    @Qualifier("namedFile")
    public void setArbDependency(File arbDependency) {
        this.arbDependency = arbDependency;
    }

    @Resource
    @Qualifier("defaultFile")
    public void setAnotherArbDependency(File anotherArbDependency) {
        this.anotherArbDependency = anotherArbDependency;
    }
}

Цель этого теста - продемонстрировать, что даже если в контексте приложения определены несколько реализаций bean-компонентов определенного типа, аннотация@Qualifier может использоваться вместе с аннотацией@Resource для разрешения зависимости.

Подобно внедрению зависимостей на основе полей, если в контексте приложения определено несколько bean-компонентов, выдаетсяNoUniqueBeanDefinitionException, если не используется аннотация@Qualifier, чтобы указать, какой bean-компонент должен использоваться для разрешения зависимостей.

3. Аннотация@Inject

Аннотация@Inject принадлежит к коллекции аннотацийJSR-330. Эта аннотация имеет следующие пути выполнения, перечисленные по приоритету:

  1. Совпадение по типу

  2. Матч по классификатору

  3. Матч по имени

Эти пути выполнения применимы как к установщику, так и к внедрению поля. Чтобы получить доступ к аннотации@Inject, библиотекаjavax.inject должна быть объявлена ​​как зависимость Gradle или Maven.

Для Gradle:

testCompile group: 'javax.inject', name: 'javax.inject', version: '1'

Для Maven:


    javax.inject
    javax.inject
    1

3.1. Полевая инъекция

3.1.1. Совпадение по типу

Пример интеграционного теста будет изменен для использования другого типа зависимости, а именно классаArbitraryDependency. Зависимость классаArbitraryDependency просто служит простой зависимостью и больше не имеет значения. Это перечислено следующим образом:

@Component
public class ArbitraryDependency {

    private final String label = "Arbitrary Dependency";

    public String toString() {
        return label;
    }
}

Рассматриваемый интеграционный тестFieldInjectTest представлен следующим образом:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestInjectType.class)
public class FieldInjectIntegrationTest {

    @Inject
    private ArbitraryDependency fieldInjectDependency;

    @Test
    public void givenInjectAnnotation_WhenOnField_ThenValidDependency(){
        assertNotNull(fieldInjectDependency);
        assertEquals("Arbitrary Dependency",
          fieldInjectDependency.toString());
    }
}

В отличие от аннотации@Resource, которая сначала разрешает зависимости по имени; поведение аннотации@Inject по умолчанию разрешает зависимости по типу.

Это означает, что даже если имя переменной ссылки на класс отличается от имени компонента, зависимость все равно будет разрешена при условии, что компонент определен в контексте приложения. Обратите внимание, как ссылка на имя переменной в следующем тесте:

@Inject
private ArbitraryDependency fieldInjectDependency;

отличается от имени компонента, настроенного в контексте приложения:

@Bean
public ArbitraryDependency injectDependency() {
    ArbitraryDependency injectDependency = new ArbitraryDependency();
    return injectDependency;
}

и когда тест выполняется, он может разрешить зависимость.

3.1.2. Матч по классификатору

Но что, если существует несколько реализаций определенного типа класса, и определенный класс требует определенного компонента? Давайте изменим пример интеграционного тестирования, чтобы потребовалась другая зависимость.

В этом примере мы подклассифицируем классArbitraryDependency, используемый в примере сопоставления по типу, для создания классаAnotherArbitraryDependency:

public class AnotherArbitraryDependency extends ArbitraryDependency {

    private final String label = "Another Arbitrary Dependency";

    public String toString() {
        return label;
    }
}

Цель каждого теста - убедиться, что каждая зависимость правильно введена в каждую ссылочную переменную:

@Inject
private ArbitraryDependency defaultDependency;

@Inject
private ArbitraryDependency namedDependency;

Интеграционный тестFieldQualifierInjectTest, используемый для демонстрации соответствия по квалификатору, представлен следующим образом:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestInjectQualifier.class)
public class FieldQualifierInjectIntegrationTest {

    @Inject
    private ArbitraryDependency defaultDependency;

    @Inject
    private ArbitraryDependency namedDependency;

    @Test
    public void givenInjectQualifier_WhenOnField_ThenDefaultFileValid(){
        assertNotNull(defaultDependency);
        assertEquals("Arbitrary Dependency",
          defaultDependency.toString());
    }

    @Test
    public void givenInjectQualifier_WhenOnField_ThenNamedFileValid(){
        assertNotNull(defaultDependency);
        assertEquals("Another Arbitrary Dependency",
          namedDependency.toString());
    }
}

Если существует несколько реализаций определенного класса в контексте приложения и интеграционный тестFieldQualifierInjectTest пытается внедрить зависимости указанным ниже способом:

@Inject
private ArbitraryDependency defaultDependency;

@Inject
private ArbitraryDependency namedDependency;

Будет брошенNoUniqueBeanDefinitionException.

Создание этого исключения - это способ Spring Framework указать, что существует несколько реализаций определенного класса, и он не понимает, какую из них использовать. Чтобы прояснить путаницу, перейдите к строкам 7 и 10 интеграционного тестаFieldQualifierInjectTest:

@Inject
private ArbitraryDependency defaultDependency;

@Inject
private ArbitraryDependency namedDependency;

передать требуемое имя bean-компонента в аннотацию@Qualifier, которая используется вместе с аннотацией@Inject. Блок кода теперь будет выглядеть следующим образом:

@Inject
@Qualifier("defaultFile")
private ArbitraryDependency defaultDependency;

@Inject
@Qualifier("namedFile")
private ArbitraryDependency namedDependency;

Аннотация@Qualifier ожидает строгого совпадения при получении имени компонента. Убедитесь, что имя bean-компонента передано вQualifier правильно, в противном случае будет выбраноNoUniqueBeanDefinitionException. Запустите тест еще раз, и на этот раз он должен пройти.

3.1.3. Матч по имени

Интеграционный тестFieldByNameInjectTest, используемый для демонстрации сопоставления по имени, аналогичен сопоставлению по пути выполнения по типу. Единственная разница в том, что теперь требуется конкретный компонент, а не конкретный тип. В этом примере мы снова подклассифицируем классArbitraryDependency, чтобы создать классYetAnotherArbitraryDependency:

public class YetAnotherArbitraryDependency extends ArbitraryDependency {

    private final String label = "Yet Another Arbitrary Dependency";

    public String toString() {
        return label;
    }
}

Чтобы продемонстрировать путь выполнения соответствия по имени, мы будем использовать следующий интеграционный тест:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestInjectName.class)
public class FieldByNameInjectIntegrationTest {

    @Inject
    @Named("yetAnotherFieldInjectDependency")
    private ArbitraryDependency yetAnotherFieldInjectDependency;

    @Test
    public void givenInjectQualifier_WhenSetOnField_ThenDependencyValid(){
        assertNotNull(yetAnotherFieldInjectDependency);
        assertEquals("Yet Another Arbitrary Dependency",
          yetAnotherFieldInjectDependency.toString());
    }
}

Контекст приложения указан следующим образом:

@Configuration
public class ApplicationContextTestInjectName {

    @Bean
    public ArbitraryDependency yetAnotherFieldInjectDependency() {
        ArbitraryDependency yetAnotherFieldInjectDependency =
          new YetAnotherArbitraryDependency();
        return yetAnotherFieldInjectDependency;
    }
}

Запустите интеграционный тест как есть, и он пройдет.

Чтобы проверить, действительно ли зависимость была введена путем выполнения сопоставления по имени, измените значениеyetAnotherFieldInjectDependency, которое было передано в аннотацию@Named, на другое имя по вашему выбору. Снова запустите тест - на этот раз выдаетсяNoSuchBeanDefinitionException.

3.2. Сеттер Инъекция

Внедрение на основе сеттера для аннотации@Inject аналогично подходу, используемому для инъекции на основе сеттера@Resource. Вместо того, чтобы аннотировать ссылочную переменную, соответствующий метод установки аннотируется. Пути выполнения, сопровождаемые внедрением зависимостей на основе полей, также применяются к внедрению на основе сеттеров.

4. Аннотация@Autowired

Поведение аннотации@Autowired аналогично аннотации@Inject. Единственное отличие состоит в том, что аннотация@Autowired является частью среды Spring. Эта аннотация имеет те же пути выполнения, что и аннотация@Inject, перечисленные в порядке приоритета:

  1. Совпадение по типу

  2. Матч по классификатору

  3. Матч по имени

Эти пути выполнения применимы как к установщику, так и к внедрению поля.

4.1. Полевая инъекция

4.1.1. Совпадение по типу

Пример интеграционного тестирования, используемый для демонстрации пути выполнения соответствия@Autowired по типу, будет аналогичен тесту, используемому для демонстрации пути выполнения соответствия@Inject по типу. Интеграционный тестFieldAutowiredTest, используемый для демонстрации соответствия по типу с использованием аннотации@Autowired, представлен следующим образом:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestAutowiredType.class)
public class FieldAutowiredIntegrationTest {

    @Autowired
    private ArbitraryDependency fieldDependency;

    @Test
    public void givenAutowired_WhenSetOnField_ThenDependencyResolved() {
        assertNotNull(fieldDependency);
        assertEquals("Arbitrary Dependency", fieldDependency.toString());
    }
}

Контекст приложения для этого интеграционного теста указан следующим образом:

@Configuration
public class ApplicationContextTestAutowiredType {

    @Bean
    public ArbitraryDependency autowiredFieldDependency() {
        ArbitraryDependency autowiredFieldDependency =
          new ArbitraryDependency();
        return autowiredFieldDependency;
    }
}

Цель интеграционного теста - продемонстрировать, что сопоставление по типу имеет приоритет над другими путями выполнения. Обратите внимание на строку 8 интеграционного тестаFieldAutowiredTest, как имя ссылочной переменной:

@Autowired
private ArbitraryDependency fieldDependency;

отличается от имени компонента в контексте приложения:

@Bean
public ArbitraryDependency autowiredFieldDependency() {
    ArbitraryDependency autowiredFieldDependency =
      new ArbitraryDependency();
    return autowiredFieldDependency;
}

Когда тест будет запущен, он пройдет.

Чтобы подтвердить, что зависимость действительно была разрешена с использованием пути выполнения сопоставления по типу, измените тип ссылочной переменнойfieldDependency и снова запустите интеграционный тест. На этот раз интеграционный тестFieldAutowiredTest должен завершиться неудачно, и будет выданNoSuchBeanDefinitionException. Это подтверждает, что сопоставление по типу было использовано для разрешения зависимости.

4.1.2. Матч по классификатору

Что если столкнуться с ситуацией, когда в контексте приложения были определены несколько реализаций bean-компонентов, как показано ниже:

@Configuration
public class ApplicationContextTestAutowiredQualifier {

    @Bean
    public ArbitraryDependency autowiredFieldDependency() {
        ArbitraryDependency autowiredFieldDependency =
          new ArbitraryDependency();
        return autowiredFieldDependency;
    }

    @Bean
    public ArbitraryDependency anotherAutowiredFieldDependency() {
        ArbitraryDependency anotherAutowiredFieldDependency =
          new AnotherArbitraryDependency();
        return anotherAutowiredFieldDependency;
    }
}

Если выполняется интеграционный тестFieldQualifierAutowiredTest, указанный ниже:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestAutowiredQualifier.class)
public class FieldQualifierAutowiredIntegrationTest {

    @Autowired
    private ArbitraryDependency fieldDependency1;

    @Autowired
    private ArbitraryDependency fieldDependency2;

    @Test
    public void givenAutowiredQualifier_WhenOnField_ThenDep1Valid(){
        assertNotNull(fieldDependency1);
        assertEquals("Arbitrary Dependency", fieldDependency1.toString());
    }

    @Test
    public void givenAutowiredQualifier_WhenOnField_ThenDep2Valid(){
        assertNotNull(fieldDependency2);
        assertEquals("Another Arbitrary Dependency",
          fieldDependency2.toString());
    }
}

Будет брошенNoUniqueBeanDefinitionException.

Исключение связано с неоднозначностью, вызванной двумя компонентами, определенными в контексте приложения. Spring Framework не знает, какая зависимость bean-компонента должна быть автоматически подключена к какой ссылочной переменной. Решите эту проблему, добавив аннотацию@Qualifier к строкам 7 и 10 интеграционного тестаFieldQualifierAutowiredTest:

@Autowired
private FieldDependency fieldDependency1;

@Autowired
private FieldDependency fieldDependency2;

так что блок кода выглядит следующим образом:

@Autowired
@Qualifier("autowiredFieldDependency")
private FieldDependency fieldDependency1;

@Autowired
@Qualifier("anotherAutowiredFieldDependency")
private FieldDependency fieldDependency2;

Запустите тест еще раз, и на этот раз он пройдет.

4.1.3. Матч по имени

Тот же сценарий интеграционного теста будет использоваться для демонстрации пути выполнения сопоставления по имени при использовании аннотации@Autowired для внедрения зависимости поля. При автоматическом подключении зависимостей по имени аннотация@ComponentScan должна использоваться с контекстом приложенияApplicationContextTestAutowiredName:

@Configuration
@ComponentScan(basePackages={"com.example.dependency"})
    public class ApplicationContextTestAutowiredName {
}

Аннотация@ComponentScan будет искать в пакетах классы Java, которые были аннотированы аннотацией@Component. Например, в контексте приложения пакетcom.example.dependency будет сканироваться на предмет классов, которые были аннотированы аннотацией@Component. В этом сценарии Spring Framework должна обнаружить классArbitraryDependency, который имеет аннотацию@Component:

@Component(value="autowiredFieldDependency")
public class ArbitraryDependency {

    private final String label = "Arbitrary Dependency";

    public String toString() {
        return label;
    }
}

Значение атрибутаautowiredFieldDependency, переданное в аннотацию@Component, сообщает Spring Framework, что классArbitraryDependency является компонентом с именемautowiredFieldDependency. Чтобы аннотация@Autowired разрешала зависимости по имени, имя компонента должно соответствовать имени поля, определенному в интеграционном тестеFieldAutowiredNameTest; пожалуйста, обратитесь к строке 8:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  loader=AnnotationConfigContextLoader.class,
  classes=ApplicationContextTestAutowiredName.class)
public class FieldAutowiredNameIntegrationTest {

    @Autowired
    private ArbitraryDependency autowiredFieldDependency;

    @Test
    public void givenAutowiredAnnotation_WhenOnField_ThenDepValid(){
        assertNotNull(autowiredFieldDependency);
        assertEquals("Arbitrary Dependency",
          autowiredFieldDependency.toString());
    }
}

Когда интеграционный тестFieldAutowiredNameTest запущен как есть, он будет пройден.

Но как мы узнаем, что аннотация@Autowired действительно вызывала путь выполнения сопоставления по имени? Измените имя ссылочной переменнойautowiredFieldDependency на другое имя по вашему выбору, затем снова запустите тест.

На этот раз тест завершится неудачно, и будет выданNoUniqueBeanDefinitionException. Аналогичная проверка заключалась бы в изменении значения атрибута@Component,autowiredFieldDependency, на другое значение по вашему выбору и повторном запуске теста. Также будет брошенNoUniqueBeanDefinitionException.

Это исключение является доказательством того, что если используется неправильное имя компонента, действительный компонент не будет найден. Таким образом, был вызван путь выполнения соответствия по имени.

4.2. Сеттер Инъекция

Внедрение на основе сеттера для аннотации@Autowired аналогично подходу, продемонстрированному для инъекции на основе сеттера@Resource. Вместо аннотирования ссылочной переменной аннотацией@Inject, аннотируется соответствующий метод установки. Пути выполнения, сопровождаемые внедрением зависимостей на основе полей, также применяются к внедрению на основе сеттеров.

5. Применение этих аннотаций

Возникает вопрос, какую аннотацию следует использовать и при каких обстоятельствах? Ответ на эти вопросы зависит от сценария разработки, с которым сталкивается рассматриваемое приложение, и от того, как разработчик желает использовать полиморфизм на основе путей выполнения по умолчанию каждой аннотации.

5.1. Широкое применение синглтонов посредством полиморфизма

Если дизайн таков, что поведение приложения основано на реализациях интерфейса или абстрактного класса, и эти поведения используются во всем приложении, тогда используйте аннотацию@Inject или@Autowired.

Преимущество этого подхода заключается в том, что когда приложение обновляется или требуется исправление, чтобы исправить ошибку; затем классы могут быть заменены с минимальным негативным влиянием на общее поведение приложения. В этом сценарии основной путь выполнения по умолчанию - соответствие по типу.

5.2. Детализированная конфигурация поведения приложения посредством полиморфизма

Если дизайн таков, что приложение имеет сложное поведение, каждое поведение основано на разных интерфейсах / абстрактных классах, и использование каждой из этих реализаций варьируется в приложении, тогда используйте аннотацию@Resource. В этом сценарии основной путь выполнения по умолчанию - поиск по имени.

5.3. Внедрение зависимостей должно обрабатываться исключительно платформой Java EE

Если существует мандат на проектирование для внедрения всех зависимостей платформой Java EE, а не Spring, тогда выбор будет между аннотацией@Resource и аннотацией@Inject. Вам следует сузить окончательное решение между двумя аннотациями, основываясь на том, какой путь выполнения по умолчанию требуется.

5.4. Внедрение зависимостей должно обрабатываться исключительно Spring Framework

Если мандат состоит в том, чтобы все зависимости обрабатывались Spring Framework, единственным выбором является аннотация@Autowired.

5.5. Резюме обсуждения

Таблица ниже суммирует обсуждение.

сценарий

@Ресурс

@Inject

@Autowired

Широкое применение синглтонов за счет полиморфизма

Детализированная конфигурация поведения приложения посредством полиморфизма

Внедрение зависимостей должно выполняться исключительно платформой Java EE.

Внедрение зависимостей должно обрабатываться исключительно Spring Framework

6. Заключение

Цель этой статьи - дать более глубокое понимание поведения каждой аннотации. Понимание того, как ведет себя каждая аннотация, будет способствовать улучшению общего дизайна и обслуживания приложения.

Код, использованный во время обсуждения, можно найти наGitHub.