Spring Autowiring: @Autowired, @Qualifier and @Primary

Spring autowiring finds a matching bean by type and injects it. Learn how Spring picks between several beans with @Qualifier, @Primary and @Fallback, how to declare optional dependencies, and what breaks with circular dependencies.

Spring bean autowiring modes

Spring autowiring is the feature where the Spring container finds a matching bean for a dependency and injects it, so we never call new for our collaborators or wire them by hand. In annotation-based code, we mark the injection point with @Autowired, or we write a single constructor, and Spring matches the dependency by its type.

We use autowiring in every Spring and Spring Boot application, for example to give a service class its repository or, as in our example, a notifier that sends messages.

The following example declares a ReminderService that needs a Notifier. Spring finds the only Notifier bean in the container, EmailNotifier, and passes it to the constructor.

@Component
public class ReminderService {

  private final Notifier notifier;

  public ReminderService(Notifier notifier) {   // single constructor, no @Autowired needed
    this.notifier = notifier;
  }

  public String remind(String to) {
    return notifier.send(to, "pay the bill");
  }
}

ReminderService service = context.getBean(ReminderService.class);
String result = service.remind("Lokesh");       // "Email to Lokesh: pay the bill"

Notice that ReminderService never mentions EmailNotifier. It asks for the Notifier interface, and the container decides which bean fits.

Next, we look at how Spring picks a bean when there is no candidate or more than one, and where to put @Autowired. After that, we cover @Qualifier, @Primary and @Fallback, optional dependencies, collections, the XML autowiring modes, and circular dependencies.

1. How Spring Autowiring Picks a Bean

In plain Java, ReminderService would create its own notifier with new EmailNotifier(). In Spring, we don’t do that. Spring creates the beans and injects them into the classes that need them, which is called dependency injection. Autowiring is the part of dependency injection where Spring chooses the bean for each dependency without an explicit reference in our configuration.

Spring always starts by type. It collects every bean that can be assigned to the declared type, here every bean that implements Notifier, and narrows the list down from there. When one candidate is left, Spring injects it, and when several are left or a required dependency has none, the application context fails to start.

Candidates after the type matchWhat decidesResult
1Nothing more to decideThat bean is injected
0required = false, Optional, @Nullable or ObjectProvider at the injection pointNothing is injected, or NoSuchBeanDefinitionException
2 or more@Qualifier at the injection pointThe bean with that qualifier or name
2 or more@Primary on one beanThe primary bean
2 or more@Fallback on all beans except one (Spring 6.2+)The only regular bean
2 or moreField or parameter name equals a bean nameThe bean with that name
2 or moreNo rule decidesNoUniqueBeanDefinitionException

Spring checks the rules for several candidates in the order of the table. A @Qualifier wins over @Primary, and the name match is the last attempt before the exception.

Decision flow of Spring autowiring. Spring finds all beans of the required type. With zero candidates, an optional injection point gets nothing and a required one fails with NoSuchBeanDefinitionException. With one candidate, Spring injects it. With two or more candidates, Spring checks @Qualifier, then @Primary, then @Fallback, then whether the field or parameter name equals a bean name, and fails with NoUniqueBeanDefinitionException when none of them selects a single bean.
Spring matches by type first, and the extra rules apply only when several beans of that type exist.

The example uses Spring Framework 7.0.9 with AnnotationConfigApplicationContext on Java 25, so it needs only the spring-context dependency. Spring Boot applications behave the same, because Spring Boot uses the same container. The complete code is in the spring-core repository on GitHub.

public interface Notifier {
  String send(String to, String text);
}

public class EmailNotifier implements Notifier {
  public String send(String to, String text) {
    return "Email to " + to + ": " + text;
  }
}

public class SmsNotifier implements Notifier {
  public String send(String to, String text) {
    return "SMS to " + to + ": " + text;
  }
}

2. Where to Put @Autowired

The @Autowired annotation marks an injection point. Spring resolves every injection point the same way, by type, but the styles behave differently in tests. We can put @Autowired on these members.

  • A constructor, which is the recommended place.
  • A setter, or any other method with parameters.
  • A field.

With constructor injection, the dependency is a constructor parameter. Since Spring 4.3, a class with only one constructor needs no @Autowired, because Spring uses that constructor anyway. When a class has several constructors, we put @Autowired on the one Spring should call.

@Component
public class ReminderService {

  private final Notifier notifier;

  public ReminderService(Notifier notifier) {
    this.notifier = notifier;
  }
}

The Spring team recommends constructor injection for required dependencies, because the field can be final and the object is never in a half-initialized state. We can see that constructor injection pays off in unit tests, too. A test creates the class with new ReminderService(new SmsNotifier()) and needs no Spring context.

2.2. Setter and Field Injection

With setter injection, Spring calls an annotated method after it creates the object. With field injection, Spring writes the value into the field with reflection.

@Component
public class SetterReminderService {

  private Notifier notifier;

  @Autowired
  public void setNotifier(Notifier notifier) {
    this.notifier = notifier;
  }
}

@Component
public class FieldReminderService {

  @Autowired
  private Notifier notifier;
}

Both styles produce the same result in a running application, namely “Email to Lokesh: pay the bill”. The difference shows up outside the container. A test that calls new FieldReminderService() gets an object with a null field and no way to set it except reflection.

StyleField can be finalTestable without SpringTypical use
Constructoryesyes, pass the mock to the constructorrequired dependencies (the default choice)
Setternoyes, call the setteroptional dependencies with a default value
Fieldnono, needs reflection or a Spring testtest classes and quick prototypes

3. Choosing Between Several Beans of the Same Type

A real application often has several implementations of one interface. For example, a reminder app sends emails by default, but sends an SMS for urgent reminders. Both EmailNotifier and SmsNotifier are beans of type Notifier.

@Configuration
public class NotifierConfig {

  @Bean
  Notifier emailNotifier() {
    return new EmailNotifier();
  }

  @Bean
  Notifier smsNotifier() {
    return new SmsNotifier();
  }
}

The bean names come from the method names, emailNotifier and smsNotifier. With both beans present, ReminderService from the intro can no longer start, because its constructor parameter notifier matches two beans and no rule picks one.

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'reminderService':
Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type
'com.howtodoinjava.core.autowire.concepts.Notifier' available: expected single matching bean but found 2: emailNotifier,smsNotifier

The message lists the names of all candidates, which are the values we can use in @Qualifier.

3.1. @Qualifier Selects a Bean at the Injection Point

The @Qualifier annotation names the bean we want at one injection point. Without a custom qualifier, the value is the bean name.

public QualifiedReminderService(@Qualifier("smsNotifier") Notifier notifier) {
  this.notifier = notifier;
}

String result = qualifiedService.remind("Lokesh");   // "SMS to Lokesh: pay the bill"

We use @Qualifier when different classes need different implementations, e.g. the urgent-reminder service needs SMS and everything else needs email.

3.2. @Primary Sets a Default Bean

The @Primary annotation goes on the bean definition, not on the injection point. It makes one bean the default for every injection point that has no qualifier.

@Bean
@Primary
Notifier emailNotifier() {
  return new EmailNotifier();
}

@Bean
Notifier smsNotifier() {
  return new SmsNotifier();
}

String primary = reminderService.remind("Lokesh");             // "Email to Lokesh: pay the bill"
String qualified = qualifiedReminderService.remind("Lokesh");  // "SMS to Lokesh: pay the bill"

The second line shows that a @Qualifier still wins over @Primary. So we mark the common choice with @Primary and use @Qualifier only for the exceptions.

3.3. @Fallback Marks the Backup Bean (Spring 6.2+)

The @Fallback annotation works the other way round. It marks a bean that Spring injects only when no regular bean of the type exists. With two beans, marking smsNotifier as fallback has the same effect as marking emailNotifier as primary.

@Bean
Notifier emailNotifier() {
  return new EmailNotifier();
}

@Bean
@Fallback
Notifier smsNotifier() {
  return new SmsNotifier();
}

String result = reminderService.remind("Lokesh");   // "Email to Lokesh: pay the bill"

@Fallback helps in libraries and auto-configuration. A library declares its default bean as a fallback, so the bean that the application defines takes over without any @Primary in application code.

3.4. Matching by Field or Parameter Name

When no annotation decides, Spring compares the field name or the parameter name with the bean names and injects the bean with the same name. In the following class, the parameter is called smsNotifier, so Spring injects the smsNotifier bean without any annotation.

public NamedReminderService(Notifier smsNotifier) {
  this.notifier = smsNotifier;
}

String result = namedService.remind("Lokesh");   // "SMS to Lokesh: pay the bill"

Since Spring 6.1, name matching for constructor and method parameters works only when the code is compiled with the -parameters flag of javac. Without the flag, the class file has no parameter names, and the same class fails with NoUniqueBeanDefinitionException. The Spring Boot parent POM sets the flag, but a Maven project without it must add it to the compiler plugin.

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.16.0</version>
  <configuration>
    <release>25</release>
    <parameters>true</parameters>
  </configuration>
</plugin>

Field names are always in the class file, so the name match for fields needs no flag. Still, a rename in an IDE can change which bean gets injected, so we prefer an explicit @Qualifier.

4. Optional Dependencies

By default, an injection point is required. When no bean of the type exists, the context fails at startup.

org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
'com.howtodoinjava.core.autowire.concepts.Notifier' available: expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations: {}

Some dependencies are optional, though. For example, the SMS module is a paid add-on, and a customer without it should still get the app running with email only. Spring offers four ways to mark a dependency as optional. In OptionalReminderService, we declare all four while the container has no Notifier bean, so each comment shows the empty value.

@Autowired(required = false)
Notifier fieldNotifier;                                   // null (field is left untouched)

public OptionalReminderService(Optional<Notifier> optionalNotifier,          // Optional.empty
                               @Nullable Notifier nullableNotifier,           // null
                               ObjectProvider<Notifier> notifierProvider) {
  this.optionalNotifier = optionalNotifier;
  this.nullableNotifier = nullableNotifier;
  this.providedNotifier = notifierProvider.getIfAvailable(EmailNotifier::new);  // EmailNotifier (the default)
}

The @Nullable annotation here is org.jspecify.annotations.Nullable, which Spring Framework 7 uses for its own null-safety, but Spring accepts a @Nullable annotation from any package.

The safest choice is ObjectProvider with a default, or Optional, because the code can never forget the null check. With required = false, Spring does not set the field at all, so the field keeps its initial value, and a non-required setter method is not called. That makes required = false easy to misuse for hiding a configuration error, so we use it only for dependencies that are optional by design.

5. Injecting All Beans of a Type

When an injection point is a List, a Set, an array or a Map with String keys, Spring injects all beans of the element type instead of failing on several candidates. For example, a broadcast service sends a reminder through every notifier the app has.

public BroadcastService(List<Notifier> notifiers, Map<String, Notifier> notifiersByName) {
  this.notifiers = notifiers;
  this.notifiersByName = notifiersByName;
}

List<String> sent = broadcastService.broadcast("Lokesh");   // [SMS to Lokesh: pay the bill, Email to Lokesh: pay the bill]
Set<String> names = broadcastService.notifiersByName().keySet();   // [emailNotifier, smsNotifier]

The list follows the @Order values of the beans, so smsNotifier with @Order(1) comes before emailNotifier with @Order(2). Without @Order or the Ordered interface, the order is the registration order of the bean definitions. The map keys are the bean names, which lets the caller pick a notifier by name at runtime.

6. Autowiring Modes in XML Configuration

Before annotations, autowiring was switched on per bean in XML with the autowire attribute. These autowiring modes still exist in Spring Framework 7 for XML configuration.

ModeHow Spring finds the dependency
noNo autowiring (the default). Dependencies are set with ref elements.
byNameFor each setter, Spring looks for a bean whose name equals the property name, e.g. setNotifier() gets the bean notifier.
byTypeFor each setter, Spring injects the only bean of the property type. Several beans of the type cause an error, and no bean leaves the property unset.
constructorLike byType, but for constructor arguments. A missing bean is an error.

The file autowire-modes.xml wires the same classes in all three active modes. Calling remind(“Lokesh”) on each of the three beans returns “Email to Lokesh: pay the bill”.

<bean id="notifier" class="com.howtodoinjava.core.autowire.concepts.EmailNotifier"/>

<bean id="byNameService" class="com.howtodoinjava.core.autowire.concepts.SetterReminderService"
      autowire="byName"/>

<bean id="byTypeService" class="com.howtodoinjava.core.autowire.concepts.SetterReminderService"
      autowire="byType"/>

<bean id="constructorService" class="com.howtodoinjava.core.autowire.concepts.ReminderService"
      autowire="constructor"/>

The modes are an XML feature only. The autowire attribute of @Bean was deprecated in Spring 5.1 and no longer exists in Spring 6, and the old autodetect mode is not part of the current XML schema. In annotation-based code, @Autowired works like byType with the extra rules from section 1.

7. Circular Dependencies

A circular dependency exists when bean A needs bean B and bean B needs bean A. With constructor injection, Spring cannot create either bean first, because each constructor needs the other object already built.

For example, a CalendarService asks a ReminderScheduler to schedule a reminder for each new event, and the scheduler asks the calendar for event details.

@Component
public class CalendarService {

  private final ReminderScheduler scheduler;

  public CalendarService(ReminderScheduler scheduler) {
    this.scheduler = scheduler;
  }
}

@Component
public class ReminderScheduler {

  private final CalendarService calendar;

  public ReminderScheduler(CalendarService calendar) {
    this.calendar = calendar;
  }
}
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'calendarService':
Requested bean is currently in creation: Is there an unresolvable circular reference or an asynchronous initialization dependency?

The best fix is a design change, e.g. moving the shared logic into a third bean that both classes use. When that is not possible, @Lazy on one constructor parameter breaks the cycle. Spring injects a proxy (a generated subclass) instead of the real bean, and the proxy looks up the real bean on the first method call.

public LazyReminderScheduler(@Lazy LazyCalendarService calendar) {
  this.calendar = calendar;
}

String added = calendarService.addEvent("dentist");                // "Added dentist, reminder set for dentist"
String injected = scheduler.calendarClass();                       // "LazyCalendarService$$SpringCGLIB$$0" (a proxy)

With field or setter injection, Spring Core resolves the same cycle by injecting an early reference to the half-built bean. Spring Boot switches that off. The property spring.main.allow-circular-references is false by default since Spring Boot 2.6, so a Spring Boot application fails at startup for every cycle.

8. Spring Autowiring FAQs

8.1. Why Is My @Autowired Field null?

The most common cause is that Spring did not create the object. Spring injects dependencies only into beans that it creates, so an object created with new FieldReminderService() keeps a null field, and calling it throws a NullPointerException. Other causes are a class without a stereotype annotation such as @Component or @Service, a class outside the scanned packages, a static field, or @Autowired(required = false) with no matching bean.

8.2. Is @Autowired Required on a Constructor?

No, not when the class has a single constructor. Spring has used the only constructor for injection since Spring 4.3. We add @Autowired only when a class has several constructors, to mark the one Spring should call.

8.3. What Is the Difference Between @Autowired, @Inject and @Resource?

@Autowired and jakarta.inject.Inject both inject by type and follow the rules from section 1. Only @Autowired has the required attribute. The annotation jakarta.annotation.Resource selects the bean by name first, and the declared type plays no role in that match. We use @Resource when the name is the identity of the bean, such as a specific DataSource.

8.4. Can Spring Autowire a String or an int?

No. Autowiring by type does not apply to simple values such as primitives, String and Class. We inject them with @Value from a property file or set them in a @Bean method.

9. Conclusion

Spring autowiring matches dependencies by type. One candidate is injected, no candidate fails unless the injection point is optional, and several candidates need a @Qualifier, a @Primary or @Fallback bean, or a name match.

We write a single constructor for required dependencies, which needs no @Autowired, and use Optional or ObjectProvider for optional ones. Name matching for parameters needs the -parameters compiler flag since Spring 6.1, so explicit qualifiers are the safer choice.

The XML modes byName, byType and constructor still work in Spring Framework 7, but annotation-based code covers them. Circular constructor dependencies fail with BeanCurrentlyInCreationException, and the right fix is a design change, with @Lazy as the fallback.

10. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. @Autowired is used on properties, it is equivalent to autowiring by ‘byType‘ in configuration file.

    @Autowired is used on setters, it is also equivalent to autowiring by ‘byType‘ in configuration file.

    Are you sure about these two ?

    If yes then how you will apply auto detect strategy ?

  2. very good explanations with examples which is really a help. Thanks a lot. Could you please add some spring data access tutorial as well, it will be a great help. Thanks in advance.

  3. Injecting beans with xml file is obsolete. Please update the article by presenting Java config classes. Also some examples would be welcomed here.

  4. XML formatting is not working on the page.

    this is how it is showing.

    <bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor">
        <property name="requiredParameterValue" value="false" />
    </bean>
  5. Nice Explanation..
    But i have one doubt, if one bean is of singleton type and another is of non- singleton(say:- prototype) and we are trying to @Autowired non singleton into singleton bean, So in that case this @Autowired will work or should we pass any parameter while autowiring? because both have different type.

  6. Nice explained in the detailed and crispy manner. Mr. Gupta proud to find a person who accept blames and in response gives another place to refer.

  7. Hi Lokesh,

    I am trying to Autowire a bean with @Autowired annotation finally messed up with following errors from 2 days, could you pls help me in understanding the issue

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'homeController': 
    Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: 
    Could not autowire field: private com.emusicstore.dao.ProductDAO com.emusicstore.app.HomeController.productDAO; 
    nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'productDAOImpl':
     Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: 
    Could not autowire field: private org.hibernate.SessionFactory com.emusicstore.dao.impl.ProductDAOImpl.sessionFactory; 
    nested exception is org.springframework.beans.factory.BeanCreationException: 
    Error creating bean with name 'sessionFactory' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: 
    Cannot resolve reference to bean 'dataSource' while setting bean property 'dataSource'; nested exception is org.springframework.beans.factory.BeanCreationException: 
    Error creating bean with name 'dataSource' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: 
    Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
    PropertyAccessException 1: org.springframework.beans.MethodInvocationException: 
    Property 'driverClassName' threw exception; nested exception is java.lang.IllegalStateException: 
    Could not load JDBC driver class [com.mysql.jdbc.Drive]
  8. Nice explanation. Just few questions :
    1. what if in case of When @Autowired is used on properties ::: property type is of interface type. i.e. ref variable is of interface but we need to inject child type of that interface.

  9. “if no dependency is found, application should not throw any exception and autowiring should simpy be ignored.” Strongly disagree with this.
    You intended to autowire a bean so not finding it means your config is wrong. And you need to find this out the earliest point you can: context start up. Better not start a missconfigured, not working application. I say always use @Required annotation or required=true property in @Autowired.

  10. Thanks Lokesh Gupta for sharing such valuable tutorials.It is very nicely explained tutorial.

    I get how to do autowiring but i did’t get why we need autowiring and what is autowiring .If you can exaplain it i wold be greatefull of you.

    • If you want to inject spring’s beans dependencies from one bean to another, then you should configure them in someplace so that the spring container can read the suitable dependency for you and inject it in runtime. This was done by using applicationContext.xml file in previous spring versions. Now you can define these dependencies using annotations (@Autowired). Spring container read that annotation and search the matching dependency in all available container-managed beans and inject the appropriate bean if found one.

  11. Rk, I am happy that you cared and shared your thoughts. What docs says, I agree with it. I am also saying “If there are no matching beans,
    nothing happens; the property is not set.”
    For reference regarding multiple, please read the table content in section “Table 3.2. Autowiring modes” in these spring 3 docs.

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.