Окабеляване през пролетта: @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 А nnotation

В @Resource анотацията е част от колекцията анотация на JSR-250 и е опакован с Джакарта ЕЕ. Тази анотация има следните пътища за изпълнение, изброени по приоритет:

  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 не получи име на боб като стойност на атрибут, Spring Framework ще продължи със следващото ниво на приоритет, съвпадение по тип, за да опита да разреши зависимостта.

2.1.3. Съвпадение по квалификация

За да демонстрира пътя на изпълнение на съвпадение по квалификатор, сценарият за тестване на интеграция ще бъде модифициран, така че в контекста на приложението ApplicationContextTestResourceQualifier да са дефинирани два компонента :

@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 ще се използва, за да се демонстрира разделителна способност на зависимост по класификация. В този сценарий трябва да се инжектира специфична зависимост от боб във всяка референтна променлива:

@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 . Това изключение се изхвърля, защото контекстът на приложението е намерил две дефиниции на боб от тип Файл и е объркано кой боб трябва да разреши зависимостта.

За да разрешите този проблем, вижте ред 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;

Пуснете теста за интеграция отново, този път той трябва да премине. Целта на този тест беше да покаже, че дори ако има множество зърна, дефинирани в контекста на приложението, анотацията @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()); } }

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

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

The namedFile bean dependency will be reused in this example. The bean name and the corresponding attribute value must match.

Run the integration test as-is and it will pass.

To see that the dependency was indeed resolved by the match-by-name execution path, change the attribute value passed to the @Resource annotation to a value of your choice and run the test again. This time, the test will fail with a NoSuchBeanDefinitionException.

2.2.2. Match by Type

To demonstrate setter-based, match-by-type execution, we will use the MethodByTypeResourceTest integration 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()); } }

Run this test as-is, and it will pass.

In order to verify that the File dependency was indeed resolved by the match-by-type execution path, change the class type of the defaultFile variable to another class type like String. Execute the MethodByTypeResourceTest integration test again and this time a NoSuchBeanDefinitionException will be thrown.

The exception verifies that match-by-type was indeed used to resolve the File dependency. The NoSuchBeanDefinitionException confirms that the reference variable name does not need to match the bean name. Instead, dependency resolution depends on the bean's class type matching the reference variable's class type.

2.2.3. Match by Qualifier

The MethodByQualifierResourceTest integration test will be used to demonstrate the match-by-qualifier execution path:

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

The objective of this test is to demonstrate that even if multiple bean implementations of a particular type are defined in an application context, a @Qualifier annotation can be used together with the @Resource annotation to resolve a dependency.

Similar to field-based dependency injection, if there are multiple beans defined in an application context, a NoUniqueBeanDefinitionException is thrown if no @Qualifier annotation is used to specify which bean should be used to resolve dependencies.

3. The @Inject Annotation

The @Inject annotation belongs to the JSR-330 annotations collection. This annotation has the following execution paths, listed by precedence:

  1. Match by Type
  2. Match by Qualifier
  3. Match by Name

These execution paths are applicable to both setter and field injection. In order to access the @Inject annotation, the javax.inject library has to be declared as a Gradle or Maven dependency.

For Gradle:

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

For Maven:

 javax.inject javax.inject 1 

3.1. Field Injection

3.1.1. Match by Type

The integration test example will be modified to use another type of dependency, namely the ArbitraryDependency class. The ArbitraryDependency class dependency merely serves as a simple dependency and holds no further significance. It is listed as follows:

@Component public class ArbitraryDependency { private final String label = "Arbitrary Dependency"; public String toString() { return label; } }

The FieldInjectTest integration test in question is listed as follows:

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

Unlike the @Resource annotation, which resolves dependencies by name first; the default behavior of the @Inject annotation resolves dependencies by type.

This means that even if a class reference variable name differs from the bean name, the dependency will still be resolved, provided that the bean is defined in the application context. Note how the reference variable name in the following test:

@Inject private ArbitraryDependency fieldInjectDependency;

differs from the bean name configured in the application context:

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

and when the test is executed, it is able to resolve the dependency.

3.1.2. Match by Qualifier

But what if there are multiple implementations of a particular class type, and a certain class requires a specific bean? Let us modify the integration testing example so that another dependency is required.

In this example, we subclass the ArbitraryDependency class, used in the match-by-type example, to create the AnotherArbitraryDependency class:

public class AnotherArbitraryDependency extends ArbitraryDependency { private final String label = "Another Arbitrary Dependency"; public String toString() { return label; } }

The objective of each test case is to ensure that each dependency is injected correctly into each reference variable:

@Inject private ArbitraryDependency defaultDependency; @Inject private ArbitraryDependency namedDependency;

The FieldQualifierInjectTest integration test used to demonstrate match by qualifier is listed as follows:

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

If there are multiple implementations of a particular class in an application context and the FieldQualifierInjectTest integration test attempts to inject the dependencies in the manner listed below:

@Inject private ArbitraryDependency defaultDependency; @Inject private ArbitraryDependency namedDependency;

a NoUniqueBeanDefinitionException will be thrown.

Throwing this exception is the Spring Framework's way of pointing out that there are multiple implementations of a certain class and it is confused about which one to use. In order to elucidate the confusion, go to line 7 and 10 of the FieldQualifierInjectTest integration test:

@Inject private ArbitraryDependency defaultDependency; @Inject private ArbitraryDependency namedDependency;

pass the required bean name to the @Qualifier annotation, which is used together with the @Inject annotation. The code block will now look as follows:

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

The @Qualifier annotation expects a strict match when receiving a bean name. Ensure that the bean name is passed to the Qualifier correctly, otherwise, a NoUniqueBeanDefinitionException will be thrown. Run the test again, and this time it should pass.

3.1.3. Match by Name

The FieldByNameInjectTest integration test used to demonstrate match by name is similar to the match by type execution path. The only difference is now a specific bean is required, as opposed to a specific type. In this example, we subclass the ArbitraryDependency class again to produce the YetAnotherArbitraryDependency class:

public class YetAnotherArbitraryDependency extends ArbitraryDependency { private final String label = "Yet Another Arbitrary Dependency"; public String toString() { return label; } }

In order to demonstrate the match-by-name execution path, we will use the following integration test:

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

The application context is listed as follows:

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

Run the integration test as-is, and it will pass.

In order to verify that the dependency was indeed injected by the match-by-name execution path, change the value, yetAnotherFieldInjectDependency, that was passed in to the @Named annotation to another name of your choice. Run the test again – this time, a NoSuchBeanDefinitionException is thrown.

3.2. Setter Injection

Setter-based injection for the @Inject annotation is similar to the approach used for @Resource setter-based injection. Instead of annotating the reference variable, the corresponding setter method is annotated. The execution paths followed by field-based dependency injection also apply to setter based injection.

4. The @Autowired Annotation

The behaviour of @Autowired annotation is similar to the @Inject annotation. The only difference is that the @Autowired annotation is part of the Spring framework. This annotation has the same execution paths as the @Inject annotation, listed in order of precedence:

  1. Match by Type
  2. Match by Qualifier
  3. Match by Name

These execution paths are applicable to both setter and field injection.

4.1. Field Injection

4.1.1. Match by Type

The integration testing example used to demonstrate the @Autowired match-by-type execution path will be similar to the test used to demonstrate the @Inject match-by-type execution path. The FieldAutowiredTest integration test used to demonstrate match-by-type using the @Autowired annotation is listed as follows:

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

The application context for this integration test is listed as follows:

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

The objective of the integration test is to demonstrate that match-by-type takes first precedence over the other execution paths. Notice on line 8 of the FieldAutowiredTest integration test how the reference variable name:

@Autowired private ArbitraryDependency fieldDependency;

is different to the bean name in the application context:

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

When the test is run, it will pass.

In order to confirm that the dependency was indeed resolved using the match-by-type execution path, change the type of the fieldDependency reference variable and run the integration test again. This time round the FieldAutowiredTest integration test must fail, with a NoSuchBeanDefinitionException being thrown. This verifies that match-by-type was used to resolve the dependency.

4.1.2. Match by Qualifier

What if faced with a situation where multiple bean implementations have been defined in the application context, like the one listed below:

@Configuration public class ApplicationContextTestAutowiredQualifier { @Bean public ArbitraryDependency autowiredFieldDependency() { ArbitraryDependency autowiredFieldDependency = new ArbitraryDependency(); return autowiredFieldDependency; } @Bean public ArbitraryDependency anotherAutowiredFieldDependency() { ArbitraryDependency anotherAutowiredFieldDependency = new AnotherArbitraryDependency(); return anotherAutowiredFieldDependency; } }

If the FieldQualifierAutowiredTest integration test, listed below, is executed:

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

a NoUniqueBeanDefinitionException will be thrown.

The exception is due to the ambiguity caused by the two beans defined in the application context. The Spring Framework does not know which bean dependency should be autowired to which reference variable. Resolve this issue by adding the @Qualifier annotation to lines 7 and 10 of the FieldQualifierAutowiredTest integration test:

@Autowired private FieldDependency fieldDependency1; @Autowired private FieldDependency fieldDependency2;

so that the code block looks as follows:

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

Run the test again, and this time it will pass.

4.1.3. Match by Name

The same integration test scenario will be used to demonstrate the match-by-name execution path when using the @Autowired annotation to inject a field dependency. When autowiring dependencies by name, the @ComponentScan annotation must be used with the application context, ApplicationContextTestAutowiredName:

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

The @ComponentScan annotation will search packages for Java classes that have been annotated with the @Component annotation. For example, in the application context, the com.baeldung.dependency package will be scanned for classes that have been annotated with the @Component annotation. In this scenario, the Spring Framework must detect the ArbitraryDependency class, which has the @Component annotation:

@Component(value="autowiredFieldDependency") public class ArbitraryDependency { private final String label = "Arbitrary Dependency"; public String toString() { return label; } }

The attribute value, autowiredFieldDependency, passed into the @Component annotation, tells the Spring Framework that the ArbitraryDependency class is a component named autowiredFieldDependency. In order for the @Autowired annotation to resolve dependencies by name, the component name must correspond with the field name defined in the FieldAutowiredNameTest integration test; please refer to line 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()); } }

When the FieldAutowiredNameTest integration test is run as-is, it will pass.

But how do we know that the @Autowired annotation really did invoke the match-by-name execution path? Change the name of the reference variable autowiredFieldDependency to another name of your choice, then run the test again.

This time, the test will fail and a NoUniqueBeanDefinitionException is thrown. A similar check would be to change the @Component attribute value, autowiredFieldDependency, to another value of your choice and run the test again. A NoUniqueBeanDefinitionException will also be thrown.

This exception is proof that if an incorrect bean name is used, no valid bean will be found. Therefore, the match-by-name execution path was invoked.

4.2. Setter Injection

Setter-based injection for the @Autowired annotation is similar the approach demonstrated for @Resource setter-based injection. Instead of annotating the reference variable with the @Inject annotation, the corresponding setter is annotated. The execution paths followed by field-based dependency injection also apply to setter-based injection.

5. Applying These Annotations

This raises the question, which annotation should be used and under what circumstances? The answer to these questions depends on the design scenario faced by the application in question, and how the developer wishes to leverage polymorphism based on the default execution paths of each annotation.

5.1. Application-Wide Use of Singletons Through Polymorphism

If the design is such that application behaviors are based on implementations of an interface or an abstract class, and these behaviors are used throughout the application, then use either the @Inject or @Autowired annotation.

The benefit of this approach is that when the application is upgraded, or a patch needs to be applied in order to fix a bug; then classes can be swapped out with minimal negative impact to the overall application behavior. In this scenario, the primary default execution path is match-by-type.

5.2. Fine-Grained Application Behavior Configuration Through Polymorphism

If the design is such that the application has complex behavior, each behavior is based on different interfaces/abstract classes, and usage of each of these implementations varies across the application, then use the @Resource annotation. In this scenario, the primary default execution path is match-by-name.

5.3. Dependency Injection Should Be Handled Solely by the Jakarta EE Platform

If there is a design mandate for all dependencies to be injected by the Jakarta EE Platform and not Spring, then the choice is between the @Resource annotation and the @Inject annotation. You should narrow down the final decision between the two annotations, based on which default execution path is required.

5.4. Dependency Injection Should Be Handled Solely by the Spring Framework

Ако мандатът е за всички зависимости да се обработват от Spring Framework, единственият избор е анотацията @Autowired .

5.5. Резюме на дискусията

Таблицата по-долу обобщава дискусията.

Сценарий @ Ресурс @ Инжектиране @Autowired
Широко приложение на сингълтони чрез полиморфизъм
Фино конфигурирана конфигурация на поведение на приложението чрез полиморфизъм
Инжектирането на зависимост трябва да се извършва единствено от платформата на Джакарта EE
Инжектирането на зависимост трябва да се извършва единствено от Spring Framework

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

Статията има за цел да даде по-задълбочена представа за поведението на всяка анотация. Разбирането как се държи всяка анотация ще допринесе за по-добър цялостен дизайн и поддръжка на приложението.

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