Dependency Injection


Spring Boot-এর সবচেয়ে শক্তিশালী ফিচারগুলোর একটি হলো Dependency Injection (DI)এটি আমাদের কোডকে loosely coupled, maintainable, এবং testable করে তোলে।


Dependency Injection কী?

Dependency Injection হলো এমন একটি Design Pattern, যেখানে কোনো ক্লাস নিজে থেকে নির্ভরশীল অবজেক্ট তৈরি না করে, Spring Framework সেই অবজেক্ট (dependency) তৈরি করে inject করে দেয়।

সহজভাবে বললে —
তুমি “কোন dependency দরকার” শুধু ঘোষণা করবে, Spring সেটি তৈরি করে তোমার ক্লাসে বসিয়ে দেবে।


একটা সহজ উদাহরণ

❌ আগে কেমন ছিল (Without DI)

public class UserService {
    private UserRepository userRepository = new UserRepository(); // hardcoded dependency

    public void saveUser(String name) {
        userRepository.save(name);
    }
}

এখানে UserService নিজে UserRepository তৈরি করছে।
অর্থাৎ এটি tightly coupled — Unit test করা কঠিন এবং পরিবর্তন করাও ঝুঁকিপূর্ণ।


Spring Boot এ কেমন হয় (With DI)

@Service
public class UserService {

    private final UserRepository userRepository;

    // Constructor-based Dependency Injection
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void saveUser(String name) {
        userRepository.save(name);
    }
}

এখানে Spring নিজে UserRepository bean তৈরি করে এবং UserService এ inject করে দেয়।


Dependency Injection এর ধরন

Spring এ DI তিনভাবে করা যায় 👇

ধরন উদাহরণ সুপারিশ
Constructor Injection public MyService(MyRepo repo) ✅ সবচেয়ে ভালো (immutable ও test-friendly)
Setter Injection setMyRepo(MyRepo repo) ☑️ প্রয়োজনীয় হলে ব্যবহার
Field Injection @Autowired private MyRepo repo; 🚫 avoid করা ভালো (testability কমে)


কেন Constructor Injection ভালো?

Dependency final রাখা যায় (immutable)।

- NullPointerException এড়ানো যায়।

- Unit test সহজ হয়, কারণ mock dependency পাঠানো যায়।


Spring Boot কিভাবে জানে কোন dependency inject করতে হবে?

Spring Boot এর IoC Container (Inversion of Control Container) কাজটি করে।
তুমি যদি কোনো ক্লাসে annotation দাও যেমন:

@Component
@Service
@Repository
@Controller

তাহলে Spring সেই ক্লাসটিকে Bean হিসেবে তৈরি করে এবং container এ রাখে।
এরপর যখন অন্য ক্লাসে সেই bean প্রয়োজন হয়, তখন @Autowired বা constructor দিয়ে inject করে দেয়।


Example: Dependency Injection in Action

Step 1 – Repository তৈরি

@Repository
public class UserRepository {
    public void save(String name) {
        System.out.println("Saving user: " + name);
    }
}


Step 2 – Service তৈরি

@Service
public class UserService {
    private final UserRepository userRepository;

    // Constructor Injection
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void registerUser(String name) {
        userRepository.save(name);
    }
}


Step 3 – Controller তৈরি

@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    // Constructor Injection
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping
    public String createUser(@RequestParam String name) {
        userService.registerUser(name);
        return "User created successfully!";
    }
}


Output

POST /api/users?name=Maruf
→ Saving user: Maruf
→ User created successfully!


@Autowired কীভাবে কাজ করে?

@Autowired দিয়ে Spring বোঝে কোথায় dependency inject করতে হবে।
যেমন 

@Service
public class EmailService {

    @Autowired
    private MailSender mailSender;

    public void sendEmail() {
        mailSender.send("hello@example.com");
    }
}

Spring container দেখে যে MailSender নামে একটি bean আছে — তা হলে সেটি inject করে দেয়।


Multiple Bean থাকলে কী হবে?

যদি একই টাইপের একাধিক bean থাকে, তখন Spring জানে না কোনটি ব্যবহার করবে।
তখন আমরা @Qualifier ব্যবহার করি 👇

@Service
public class PaymentService {

    private final PaymentGateway gateway;

    public PaymentService(@Qualifier("paypalGateway") PaymentGateway gateway) {
        this.gateway = gateway;
    }
}


Bean Lifecycle সংক্ষেপে

Spring Boot অ্যাপ চালু হয়

- Spring container তৈরি হয়

- সব annotated bean (@Service, @Repository, ইত্যাদি) স্ক্যান হয়

- Dependency resolve হয়ে inject হয়

- অ্যাপ ready to use! 🚀


Custom Bean তৈরি

@Configuration
public class AppConfig {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

এখন Spring container PasswordEncoder কে bean হিসেবে inject করতে পারবে।


Dependency Injection এর সুবিধা

- কোড সহজে maintain করা যায়
- Unit Test করা সহজ
- Object lifecycle Spring handle করে
- Loosely coupled architecture
- Reusability বৃদ্ধি পায়


Best Practices

Constructor Injection ব্যবহার করো

- Optional dependency হলে @Autowired(required = false) ব্যবহার করো

- Interface ভিত্তিক dependency রাখো (e.g., PaymentGateway)

- Circular dependency (A → B → A) এড়াও

- Field Injection পরিহার করো