How do I use dependency injection in Spring?

In Spring, Dependency Injection (DI) means you let the Spring container create and provide the objects your class depends on, instead of manually creating them with new.

The recommended approach in modern Spring is constructor injection.

1. Define a dependency as a Spring bean

import org.springframework.stereotype.Component;

@Component
public class EmailSender {

    public void send(String message) {
        System.out.println("Sending email: " + message);
    }
}

@Component tells Spring: “Create and manage an instance of this class.”

2. Inject it into another bean

import org.springframework.stereotype.Service;

@Service
public class NotificationService {

    private final EmailSender emailSender;

    public NotificationService(EmailSender emailSender) {
        this.emailSender = emailSender;
    }

    public void notifyUser(String message) {
        emailSender.send(message);
    }
}

Because NotificationService is also a Spring bean, Spring sees its constructor and automatically provides an EmailSender.

In modern Spring, if there is only one constructor, you usually do not need @Autowired on the constructor.

3. Use the service from a controller or another bean

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class NotificationController {

    private final NotificationService notificationService;

    public NotificationController(NotificationService notificationService) {
        this.notificationService = notificationService;
    }

    @GetMapping("/notify")
    public String notifyUser() {
        notificationService.notifyUser("Hello from Spring!");
        return "Notification sent";
    }
}

Common DI styles in Spring

Constructor injection — recommended

@Service
public class MyService {

    private final MyDependency myDependency;

    public MyService(MyDependency myDependency) {
        this.myDependency = myDependency;
    }
}

Use this most of the time because it:

  • makes dependencies explicit
  • supports immutability with final
  • is easier to test
  • avoids partially initialized objects

Setter injection

@Service
public class MyService {

    private MyDependency myDependency;

    @Autowired
    public void setMyDependency(MyDependency myDependency) {
        this.myDependency = myDependency;
    }
}

Use setter injection mainly for optional dependencies or dependencies that may change after construction.

Field injection — usually avoid

@Service
public class MyService {

    @Autowired
    private MyDependency myDependency;
}

This works, but it is generally discouraged because it makes testing harder and hides the class’s required dependencies.

Injecting interfaces

A common pattern is to inject an interface rather than a concrete class:

public interface MessageSender {
    void send(String message);
}
import org.springframework.stereotype.Component;

@Component
public class EmailSender implements MessageSender {

    @Override
    public void send(String message) {
        System.out.println("Email: " + message);
    }
}
import org.springframework.stereotype.Service;

@Service
public class NotificationService {

    private final MessageSender messageSender;

    public NotificationService(MessageSender messageSender) {
        this.messageSender = messageSender;
    }

    public void notifyUser(String message) {
        messageSender.send(message);
    }
}

If there is only one implementation of MessageSender, Spring injects it automatically.

When there are multiple implementations

If multiple beans match the same type, use @Qualifier:

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class NotificationService {

    private final MessageSender messageSender;

    public NotificationService(@Qualifier("emailSender") MessageSender messageSender) {
        this.messageSender = messageSender;
    }
}

The qualifier value usually matches the bean name, which by default is the class name with a lowercase-first letter.

Creating beans with @Bean

You can also define beans in a configuration class:

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

@Configuration
public class AppConfig {

    @Bean
    public EmailSender emailSender() {
        return new EmailSender();
    }
}

This is useful when:

  • the class comes from a third-party library
  • construction requires custom setup
  • you do not want to annotate the class with @Component

Summary

Use this pattern most of the time:

@Service
public class MyService {

    private final MyDependency myDependency;

    public MyService(MyDependency myDependency) {
        this.myDependency = myDependency;
    }

    public void doWork() {
        myDependency.doSomething();
    }
}

And make sure the dependency is a Spring bean:

@Component
public class MyDependency {

    public void doSomething() {
        System.out.println("Dependency logic executed.");
    }
}

In short: annotate your classes with Spring stereotypes like @Component, @Service, or @Repository, then inject dependencies through constructors.