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 পরিহার করো