In Spring Framework, you can basically use any of the three annotations for Dependency Injection, namely @Autowired, @Resource and @Inject. The @Autowired annotation belongs to the core-spring, however, the other two belongs to the Java extension package @javax.annotation.Resource
and @javax.inject.Inject
.
We will look into the use of each of these annotations with a practical use case, to help you choose the one that best suits you.
If you are using JDK 9 or higher, please make sure you add the javax.annotation-api
dependency for JSR-250 to work.
<!-- Use the recent version shown below -->
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
For later version, 1.3.3 onwards use jakarta.annotation-api
instead of javax.annotation-api
.
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>1.3.5</version>
</dependency>
Common example
I will use the same set of classes to understand the Injections using @Resource
, @Inject
and @Autowired
. As you can see the abstract class FileReader
is extended by 2 classes, PdfFileReader
and WordFileReader
. The below example is used to explore the wiring (Dependency Injection) in all the 3 cases.
package basic.ioc.wiring;
public abstract class FileReader {
public void print() {
System.out.println("Inside FileReader");
}
}
package basic.ioc.wiring;
import org.springframework.stereotype.Component;
@Component
public class PdfFileReader extends FileReader {
@Override
public void print() {
System.out.println("Inside PdfFileReader");
}
}
package basic.ioc.wiring;
import org.springframework.stereotype.Component;
@Component
public class WordFileReader extends FileReader {
@Override
public void print() {
System.out.println("Inside WordFileReader");
}
}
package basic.ioc.wiring;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan("basic.ioc.wiring")
@Configuration
public class ConfigWiring {
//extra configs goes here
}
The @Configuration
annotation in ConfigWiring
indicates that this is used as a source of bean definitions. As you rightfully observed, the FileReader
is not annotated with @Component
as it is an abstract class. There are two concrete classes that extend FileReader, PdfFileReader
and WordFileReader
. Spring provides stereotype @Component
annotation which registers the class as spring managed component.
1. @Resource – Dependency Injection using JSR-250
The @Resource annotation is from JSR-250 specification, which means it is from javax.annotation.Resource
. If you are using JDK8+, make sure to add the JSR-250 dependency jar in your maven/Gradle file. For later version, 1.3.3 onwards use jakarta.annotation-api
instead of javax.annotation-api
.
<!-- JSR 250 -->
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<version>1.3.5</version>
</dependency>
The Resource annotation class definition looks as below. As you can see, it accepts two important parameters that are type
and name
. The @Target({TYPE, FIELD, METHOD})
indicates where the @Resource
annotation can be used.
package javax.annotation;
@Target({TYPE, FIELD, METHOD})
@Retention(RUNTIME)
@Repeatable(Resources.class)
public @interface Resource {
String name() default "";
Class<?> type() default java.lang.Object.class;
boolean shareable() default true;
String mappedName() default "";
String description() default "";
//... Other methods skipped
}
- You can only use
@Resource
annotation on fields or bean property setters. - In other words, @Resource annotation can never be used to achieve constructor injections.
- The precedence that @Resource annotation takes to do dependency injection is:
- Match by Name
- Match by Type
- Match by
@Qualifier
- The @Resource annotation takes 2 important optional parameter
name
andtype
. If noname
is explicitly specified, the default name is derived from the field name or setter method. - Similarly, if no
type
is specified explicitly, it will do a type match and try to resolve it.
What is Dependency Ambiguity
All of the below codes uses common examples from above, where FileReader
is an abstract class and there are 2 classes PdfFileReader
and WordFileReader
that extends FileReader.
When we want to inject a dependency, we can just annotate the property or its setter method with the @Resource
annotation. The problem occurs when we have ambiguity. The code below throws NoUniqueBeanDefinitionException
as we have 2 classes extending the FileReader
.
@SpringJUnitConfig(ConfigWiring.class)
public class ResourceTest {
//Show the exception that gets reproduced
@Resource
private FileReader fReader;
}
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'basic.ioc.wiring.ResourceTest': Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'basic.ioc.wiring.FileReader' available: expected single matching bean but found 2: pdfFileReader,wordFileReader
The above code is the reason why we need to write code in such a way that there is no dependency ambiguity.
1.1. Resolve dependency by Name
For simplicity of understanding, I am using the @Resource annotation on the property, you should use them on setter for performance reasons. In the first example below, the field name pdfFileReader
is used as the bean name to resolve the dependency.
@SpringJUnitConfig(ConfigWiring.class)
public class ResourceTest {
//Resolve by property name
@Resource
private FileReader pdfFileReader;
@Test
public void resolveByPropertyName() {
Assert.assertNotNull(pdfFileReader);
Assert.assertEquals(pdfFileReader.getClass(), PdfFileReader.class);
}
}
You can also specify the bean name
explicitly as shown below.
@SpringJUnitConfig(ConfigWiring.class)
public class ResourceTest {
//Resolve by explicit name
@Resource(name = "wordFileReader")
private FileReader reader;
@Test
public void resolveByExplicitName() {
Assert.assertNotNull(reader);
Assert.assertEquals(reader.getClass(), WordFileReader.class);
}
}
1.2. Resolve dependency by Type
Spring will try to resolve the dependency by name
. However, it will not find any bean with this name so next, it tries to resolve by type. As we have WordFileReader
bean already registered, spring will autodetect this type and resolve this field.
@SpringJUnitConfig(ConfigWiring.class)
public class ResourceTest {
//Resolve by type auto detection
@Resource
private WordFileReader fileReader;
@Test
public void resolveByAutoType(){
Assert.assertNotNull(fileReader);
Assert.assertEquals(fileReader.getClass(), WordFileReader.class);
}
}
In the below code, Spring can neither detect the dependency by name nor resolve by type. Because there is ambiguity, type detection will fail with Exception BeanCreationException
. So we will specify the type explicitly, type = PdfFileReader.class
to inject PdfFileReader.
@SpringJUnitConfig(ConfigWiring.class)
public class ResourceTest {
//Resolve by explicit Type
@Resource(type = PdfFileReader.class)
private FileReader fileReader2;
@Test
public void resolveByExplicitType() {
Assert.assertNotNull(fileReader2);
Assert.assertEquals(fileReader2.getClass(), PdfFileReader.class);
}
}
1.3. Resolve dependency by @Qualifier
Spring has this special @Qualifier
annotation in which we can pass the name of the bean to be resolved.
@SpringJUnitConfig(ConfigWiring.class)
public class ResourceTest {
//Resolve by Qualifier
@Qualifier("pdfFileReader")
@Resource
private FileReader myFileReader;
@Test
public void resolveByQualifier(){
Assert.assertNotNull(myFileReader);
Assert.assertEquals(myFileReader.getClass(), PdfFileReader.class);
}
}
2. @Inject – Dependency Injection using JSR-330
The @Inject annotation is from jakarta.inject.Inject
. If you are using JDK8+, make sure to add the JSR-330 dependency jar in your pom/Gradle file.
<!-- https://mvnrepository.com/artifact/jakarta.inject/jakarta.inject-api -->
<dependency>
<groupId>jakarta.inject</groupId>
<artifactId>jakarta.inject-api</artifactId>
<version>2.0.1</version>
</dependency>
The Inject annotation looks as follow, which means it can be used on the field, setter or constructor based annotations.
package javax.inject;
@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {}
@Inject
annotation:
- The @Inject can be used on Setter, Field, or Constructor to do the respective type injections.
- The precedence that this annotation takes is:
- Resolve by Type
- Resolve by Qualifier –
@Qualifier
annotation - Resolve by Name –
@Named
annotation
- The Inject annotation does not take any parameter.
1. Resolve dependency by Type
In the below examples, I have used the annotation on the property fields. But in a real project, you should use it on setters or constructors due to performance reasons.
The example below fileReader
is of the type PdfFileReader
, so the dependency injection is straight forward.
@SpringJUnitConfig(ConfigWiring.class)
public class InjectTest {
//Inject by Type
@Inject
private PdfFileReader fileReader;
@Test
public void injectByType() {
Assert.assertNotNull(fileReader);
Assert.assertEquals(fileReader.getClass(), PdfFileReader.class);
}
}
2. Resolve by @Qualifier annotation
There are 2 implementations of the base class FileReader
and they are as PdfFileReader
and WordFileReader
. To get rid of this ambiguity, you need to specify the bean name in @Qualifier
annotation. The code @Qualifier("wordFileReader")
below represents the same.
@SpringJUnitConfig(ConfigWiring.class)
public class InjectTest {
//Inject by @Qualifier
@Inject
@Qualifier("wordFileReader")
private FileReader fileReader3;
@Test
public void injectByQualifier() {
Assert.assertNotNull(fileReader3);
Assert.assertEquals(fileReader3.getClass(), WordFileReader.class);
}
}
If you don’t specify the @Qualifier
, you will end up reproducing the exception as below.
SEVERE: Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@783a467b] to prepare test instance [basic.ioc.wiring.InjectTest@5b202a3a] org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'basic.ioc.wiring.InjectTest': Unsatisfied dependency expressed through field 'fileReader3'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'basic.ioc.wiring.FileReader' available: expected single matching bean but found 2: pdfFileReader,wordFileReader
3. Resolve dependency by Name
The @Inject
annotation detects the bean to be injected in 2 ways. First, if @Named("beanName")
annotation is specified. Otherwise, the field name is used as the bean name. The @Named annotation is from JSR-330 javax.inject.*
package and it is used to specify the bean name whose instance will be injected.
The line-6 represents the bean name detection using @Named
annotation. On the other hand, line-17 represents the auto-detection by field name.
@SpringJUnitConfig(ConfigWiring.class)
public class InjectTest {
//Inject by name with @Named annotation
@Inject
@Named("pdfFileReader")
private FileReader fileReader2;
@Test
public void injectByName() {
Assert.assertNotNull(fileReader2);
Assert.assertEquals(fileReader2.getClass(), PdfFileReader.class);
}
//Inject by field Name
@Inject
private FileReader wordFileReader;
@Test
public void injectByName2() {
Assert.assertNotNull(wordFileReader);
Assert.assertEquals(wordFileReader.getClass(), WordFileReader.class);
}
}
3. @Autowired – Dependency Injection in Spring style
The @Autowired
annotation is similar to @Inject
annotation. The only difference is @Inject is from JSR-330 specification, and the @Autowired is purely from the Spring framework.
The @Autowired annotation looks as below. As you can observe, @Autowired
can be used on the constructor, setter, field and in the parameters as well.
package org.springframework.beans.factory.annotation;
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD,
ElementType.PARAMETER, ElementType.FIELD,
ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
The examples discussed below uses field-based dependency injection, but you should always use the setter based or constructor based injections in your application due to performance reasons.
Remember the following when working with@Autowired
annotation:
- The @Autowired can be used on Setter, Field, Constructor or on parameters to do the respective type injections.
- The precedence that this annotation takes is:
- Resolve by Type
- Resolve by Qualifier – using the
@Qualifier
- Resolve by Name
- The
@Autowired(required=true)
annotation takesrequired
as an optional parameter which is alwaystrue
by default.
1. Resolve dependency by Type
Spring first tries to detect the bean to be injected by its type and do the DI. In the code below, spring simply injects PdfFileReader
instance.
@SpringJUnitConfig(ConfigWiring.class)
public class AutowiredTest {
//Inject by Type
@Autowired
private PdfFileReader pdfFileReader;
@Test
public void injectByType() {
Assert.assertNotNull(pdfFileReader);
Assert.assertEquals(pdfFileReader.getClass(), PdfFileReader.class);
}
}
2. Resolve dependency by @Qualifier
When you have ambiguity, use the @Qualifier
annotation to specify the bean name to be injected.
@SpringJUnitConfig(ConfigWiring.class)
public class AutowiredTest {
//Inject by @Qualifier
@Autowired
@Qualifier("wordFileReader")
private FileReader fileReader;
@Test
public void injectByQualifier() {
Assert.assertNotNull(fileReader);
Assert.assertEquals(fileReader.getClass(), WordFileReader.class);
}
}
3. Resolve dependency by Name
Spring @Autowired annotation can resolve the dependency by field name as discussed before. In this example, spring inject the bean with name wordFileReader
.
@SpringJUnitConfig(ConfigWiring.class)
public class AutowiredTest {
//Inject by Field name
@Autowired
private FileReader wordFileReader;
@Test
public void injectByFieldName() {
Assert.assertNotNull(wordFileReader);
Assert.assertEquals(wordFileReader.getClass(), WordFileReader.class);
}
}
Conclusion:
This article only focuses on the auto wiring part. I prefer using @Autowired or @Inject. If you want to understand the dependency injection and scopes etc in Spring, check out the respective articles.
The complete code example is available on GitHub, download the code and try it out. The test cases are located here and the common classes are here.