How Spring Uses Annotations for Dependency Injection: @Autowired, @Component, @Configuration

Illustration for How Spring Uses Annotations for Dependency Injection: @Autowired, @Component, @Configuration
By Last updated:

A common pain point for Java developers before Spring was managing dependencies manually—creating objects with new, wiring them across layers, and maintaining factory classes. This approach quickly led to rigid, hard-to-test code.

With Spring’s annotation-based dependency injection, developers no longer need to manually configure every bean in XML. Annotations such as @Autowired, @Component, and @Configuration allow the Spring container to automatically discover, instantiate, and wire beans using reflection.

A frequent misconception is that @Autowired magically injects dependencies—it doesn’t. Behind the scenes, Spring uses reflection and proxies to locate matching beans, set fields, or call constructors. If the wiring fails, it throws a NoSuchBeanDefinitionException.

Think of annotations as instructions written on the blueprint of your house, and Spring as the contractor who reads them and builds everything according to plan.


Key Annotations for Dependency Injection

1. @Component – Marking Classes as Beans

import org.springframework.stereotype.Component;

@Component
public class UserService {
    public void registerUser(String name) {
        System.out.println("User " + name + " registered.");
    }
}
  • Tells Spring to manage UserService as a bean.
  • Automatically discovered via component scanning.

2. @Autowired – Injecting Dependencies

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class UserController {
    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public void handleRequest(String name) {
        userService.registerUser(name);
    }
}
  • Injects a UserService instance into UserController.
  • Works on constructors, fields, and setter methods.
  • Uses reflection to resolve and assign the dependency.

3. @Configuration + @Bean – Java-Based Config

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
    @Bean
    public UserService userService() {
        return new UserService();
    }
}
  • Replaces XML configuration with Java-based bean definitions.
  • Reflection ensures that Spring recognizes the return type as a bean.

How Spring Uses Reflection for Dependency Injection

  1. Classpath Scanning – Finds classes annotated with @Component.
  2. Bean Definition – Creates metadata for each discovered class.
  3. Dependency Resolution – Uses reflection to:
    • Inspect constructors, fields, or methods.
    • Match required dependencies with available beans.
    • Inject them dynamically at runtime.
  4. Proxies – Wrap beans with proxies (e.g., for AOP or transactions).

Real-World Example

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.GetMapping;

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

@RestController
class HelloController {
    private final UserService userService;

    @Autowired
    HelloController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("/hello")
    public String hello() {
        userService.registerUser("Alice");
        return "Hello World";
    }
}
  • @SpringBootApplication triggers component scanning.
  • @RestController is itself a @Component.
  • @Autowired ensures UserService is injected automatically.

📌 What's New in Java Versions?

  • Java 5 – Introduced annotations, enabling Spring to move away from XML.
  • Java 8 – Lambdas simplified configuration; annotations remained central.
  • Java 9+ – JPMS requires explicit module exports for reflection to work.
  • Java 17/21 – No major changes, Spring still uses reflection + annotations.

Pitfalls and Best Practices

Pitfalls

  • Multiple beans of the same type cause ambiguity unless @Qualifier is used.
  • Circular dependencies can lead to runtime failures.
  • Overusing field injection reduces testability.

Best Practices

  • Prefer constructor injection over field injection.
  • Use @Qualifier or custom annotations for multiple beans.
  • Keep configuration classes modular with @Configuration.
  • Rely on Spring Boot’s auto-configuration whenever possible.

Summary + Key Takeaways

  • @Component, @Autowired, and @Configuration form the backbone of Spring’s annotation-based DI.
  • Spring uses reflection to scan, instantiate, and wire dependencies.
  • Constructor injection improves testability and immutability.
  • Annotations replace XML, making configuration more concise and maintainable.

FAQ

  1. Does @Autowired always create a new instance?
    No, it injects existing beans from the Spring container.

  2. What’s the difference between @Component and @Bean?
    @Component is class-level auto-detection; @Bean is explicit factory method definition.

  3. Can I autowire private fields?
    Yes, Spring uses reflection to access private fields, but constructor injection is preferred.

  4. What happens if two beans of the same type exist?
    Spring throws a NoUniqueBeanDefinitionException unless @Qualifier is used.

  5. Is reflection a performance bottleneck in Spring DI?
    No, Spring optimizes reflection and caches metadata for efficiency.

  6. Can I combine @Configuration and @Component?
    Yes, but @Configuration is specifically for bean definitions.

  7. Does Spring Boot require @Autowired explicitly?
    In many cases, constructors with one parameter are autowired automatically.

  8. What annotation should I use for interfaces?
    Use @Component on implementations and inject the interface type.

  9. Is XML configuration obsolete in Spring?
    Not obsolete, but annotation and Java config are the preferred approaches.

  10. Can I disable annotation-based DI in Spring?
    Yes, by excluding component scanning and relying on manual bean definitions.