Рубрики
Без рубрики

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

В этой статье мы сравним и сравним использование аннотаций, связанных с внедрением зависимостей, а именно аннотаций @Resource, @Inject и @Autowired.

Автор оригинала: baeldung.

1. Обзор

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

@Autowired 
ArbitraryClass arbObject;

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

ArbitraryClass arbObject = new ArbitraryClass();

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

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

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

Дальнейшее чтение:

Инъекция зависимостей конструктора весной

Введение в инверсию управления и инъекции зависимостей с пружиной

Использование @Autowired в абстрактных классах

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

Аннотация @Resource является частью коллекции аннотаций JSR-250 и упакована с Jakarta 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());
    }
}

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

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

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

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

@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 application; или изменив значение атрибута, переданное в аннотацию @Resource , в интеграционном тесте FieldResourceInjectionTest|/.

2.1.2. Соответствие по типу

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

@Resource
private File defaultFile;

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

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

2.1.3. Матч по квалификации

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

@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;
    }
}

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

@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 . Это исключение возникает из-за того , что контекст приложения нашел два определения компонента типа File , и он запутался в том, какой компонент должен разрешить зависимость.

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

@Resource
private File dependency1;

@Resource
private File dependency2;

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

@Qualifier("defaultFile")

@Qualifier("namedFile")

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

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

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

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

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

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

2.2.1. Совпадение по имени

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

@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());
    }
}

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

private File defaultFile;

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

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

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

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

2.2.2. Соответствие по типу

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

@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 . Выполните метод По типу Resource Test integration test еще раз, и на этот раз будет выдано исключение NoSuchBeanDefinitionException .

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

2.2.3. Матч по квалификации

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

@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;
    }
}

Цель этого теста состоит в том, чтобы продемонстрировать, что даже если в контексте приложения определено несколько реализаций компонента определенного типа, для разрешения зависимости можно использовать @Квалификатор аннотацию вместе с @Ресурсом аннотацией.

Аналогично введению зависимостей на основе полей, если в контексте приложения определено несколько компонентов, то NoUniqueBeanDefinitionException генерируется исключение , если для указания, какой компонент следует использовать для разрешения зависимостей, не используется @-квалификатор//аннотация.

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. Соответствие по типу

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

@Component
public class ArbitraryDependency {

    private final String label = "Arbitrary Dependency";

    public String toString() {
        return label;
    }
}

Тест Field Inject Test integration test, о котором идет речь, указан следующим образом:

@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. Матч по квалификации

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

В этом примере мы подклассируем класс Произвольной зависимости , используемый в примере соответствия по типу, для создания класса 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;

Тест Field Qualifier Inject Test integration test, используемый для демонстрации соответствия по квалификатору, указан следующим образом:

@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 указать, что существует несколько реализаций определенного класса, и он не знает, какую из них использовать. Чтобы прояснить путаницу, перейдите к строкам 7 и 10 квалификатора поля Inject Test integration test:

@Inject
private ArbitraryDependency defaultDependency;

@Inject
private ArbitraryDependency namedDependency;

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

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

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

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

3.1.3. Совпадение по имени

Тест FieldByName Inject Test integration test, используемый для демонстрации соответствия по имени, аналогичен пути выполнения соответствия по типу. Единственное отличие заключается в том, что теперь требуется конкретный компонент, а не конкретный тип. В этом примере мы снова подклассируем класс Произвольной зависимости , чтобы создать Еще один класс произвольной зависимости :

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 setter. Вместо аннотирования ссылочной переменной аннотируется соответствующий метод сеттера. Пути выполнения, за которыми следует внедрение зависимостей на основе полей, также применяются к внедрению на основе установщика.

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

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

  1. Совпадение по типу
  2. Матч по квалификации
  3. Совпадение по имени

Эти пути выполнения применимы как к сеттеру, так и к инжекции поля.

4.1. Закачка в Поле

4.1.1. Соответствие по типу

Пример интеграционного тестирования, используемый для демонстрации пути выполнения @Autowired match-by-type, будет аналогичен тесту, используемому для демонстрации пути выполнения @Inject match-by-type. Тест Field Autowired интеграционный тест, используемый для демонстрации соответствия по типу с использованием аннотации @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 теста Field Autowired integration test, как имя ссылочной переменной:

@Autowired
private ArbitraryDependency fieldDependency;

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

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

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

Чтобы убедиться, что зависимость действительно была устранена с помощью пути выполнения соответствия по типу, измените тип переменной fieldDependency reference и снова запустите интеграционный тест. На этот раз тест Field Autowired/| integration test должен завершиться неудачно, при этом будет выдано исключение NoSuchBeanDefinitionException|/. Это проверяет, что для устранения зависимости было использовано сопоставление по типу.

4.1.2. Матч по квалификации

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

@Configuration
public class ApplicationContextTestAutowiredQualifier {

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

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

Если выполняется тест Field Qualifier Autowired integration test, указанный ниже:

@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 .

Исключение связано с неоднозначностью, вызванной двумя компонентами, определенными в applicationcontext. Фреймворк Spring не знает, какая бобовая зависимость должна быть автоматически подключена к какой ссылочной переменной. Устраните эту проблему, добавив аннотацию @Qualifier в строки 7 и 10 автоматического теста Field Qualifier integration test:

@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.baeldung.dependency"})
    public class ApplicationContextTestAutowiredName {
}

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

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

    private final String label = "Arbitrary Dependency";

    public String toString() {
        return label;
    }
}

Значение атрибута autowired Field Dependency , переданное в аннотацию @Component , сообщает Spring Framework, что класс ArbitraryDependency является компонентом с именем autowiredFieldDependency . Для того чтобы аннотация @Autowired разрешала зависимости по имени, имя компонента должно соответствовать имени поля, определенному в тесте интеграции Fieldautowired ; пожалуйста, обратитесь к строке 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());
	}
}

Когда тест поля Autowired Name Test integration test выполняется как есть, он пройдет.

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

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

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

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

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

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

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

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

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

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

5.2. Мелкозернистая Конфигурация Поведения Приложения С Помощью Полиморфизма

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

5.3. Внедрение Зависимостей Должно Осуществляться Исключительно Платформой Jakarta EE

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

5.4. Внедрение Зависимостей Должно Осуществляться Исключительно с Помощью Фреймворка Spring

Если мандат предназначен для обработки всех зависимостей фреймворком Spring, единственным выбором является аннотация @Autowired .

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

В таблице ниже кратко излагаются результаты обсуждения.

Широкое применение синглетов через полиморфизм
Тонкозернистая конфигурация поведения приложения с помощью полиморфизма
Внедрение зависимостей должно осуществляться исключительно платформой Jakarta EE
Внедрение зависимостей должно осуществляться исключительно с помощью фреймворка Spring

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

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

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