0

[Java Backend Zero to Hello] BÀI 5.3: BEAN & DEPENDENCY INJECTION

Java Backend Zero to Hello

📚 Bài viết thuộc series Java Backend Zero to Hello 📌 Phần: Phase 5: Spring Framework & Spring Boot | Bài 49/86


BÀI 5.3: BEAN & DEPENDENCY INJECTION

Mục tiêu

  • Hiểu sâu về Bean lifecycle
  • Các cách inject dependency
  • Sử dụng @Qualifier, @Primary
  • Lazy initialization

1. BEAN LIFECYCLE

Khởi tạo Bean
    ↓
Populate Properties (DI)
    ↓
BeanNameAware.setBeanName()
    ↓
BeanFactoryAware.setBeanFactory()
    ↓
ApplicationContextAware.setApplicationContext()
    ↓
BeanPostProcessor.postProcessBeforeInitialization()
    ↓
@PostConstruct
    ↓
InitializingBean.afterPropertiesSet()
    ↓
Custom init-method
    ↓
BeanPostProcessor.postProcessAfterInitialization()
    ↓
Bean sẵn sàng sử dụng
    ↓
@PreDestroy
    ↓
DisposableBean.destroy()
    ↓
Custom destroy-method

2. CÁC CÁCH KHAI BÁO BEAN

2.1 @Component

@Component
public class MyService { }

2.2 @Service, @Repository, @Controller

Đều là @Component với ý nghĩa cụ thể:

  • @Service - Business logic
  • @Repository - Data access (có thêm exception translation)
  • @Controller - Web controller

2.3 @Configuration + @Bean

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        return mapper;
    }
}

2.4 @Import

@SpringBootApplication
@Import({DatabaseConfig.class, SecurityConfig.class})
public class Application { }

3. DEPENDENCY INJECTION CHI TIẾT

3.1 Constructor Injection (Khuyến nghị)

@Service
public class OrderService {
    private final UserRepository userRepository;
    private final PaymentService paymentService;

    // @Autowired không bắt buộc nếu chỉ có 1 constructor (Spring 4.3+)
    public OrderService(UserRepository userRepository, PaymentService paymentService) {
        this.userRepository = userRepository;
        this.paymentService = paymentService;
    }
}

Ưu điểm:

  • Dependency bắt buộc
  • Field final (immutable)
  • Dễ test
  • Phát hiện lỗi sớm

3.2 Setter Injection

@Service
public class OrderService {
    private UserRepository userRepository;

    @Autowired(required = false)  // Optional
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}

Khi nào dùng: Dependency optional, cần thay đổi sau khi tạo.

3.3 Field Injection (Không khuyến nghị)

@Service
public class OrderService {
    @Autowired
    private UserRepository userRepository;
}

Nhược điểm:

  • Khó test
  • Ẩn dependency
  • Không thể dùng với final

4. @QUALIFIER VÀ @PRIMARY

4.1 Vấn đề

public interface MessageService {
    void send(String message);
}

@Service
public class EmailService implements MessageService { }

@Service
public class SmsService implements MessageService { }

@Service
public class NotificationService {
    @Autowired
    private MessageService messageService;  // ❌ Không biết chọn cái nào
}

4.2 Giải pháp 1: @Qualifier

@Service
public class NotificationService {
    private final MessageService messageService;

    public NotificationService(@Qualifier("emailService") MessageService messageService) {
        this.messageService = messageService;
    }
}

4.3 Giải pháp 2: @Primary

@Service
@Primary
public class EmailService implements MessageService { }

@Service
public class SmsService implements MessageService { }

4.4 Giải pháp 3: Tên bean

@Service("emailMessageService")
public class EmailService implements MessageService { }

// Inject theo tên
@Resource(name = "emailMessageService")
private MessageService messageService;

5. LAZY INITIALIZATION

Bean chỉ được tạo khi cần dùng.

5.1 Global

spring:
  main:
    lazy-initialization: true

5.2 Per Bean

@Component
@Lazy
public class HeavyService { }

5.3 Inject Lazy

@Service
public class MyService {
    private final HeavyService heavyService;

    public MyService(@Lazy HeavyService heavyService) {
        this.heavyService = heavyService;
    }
}

6. OPTIONAL DEPENDENCY

@Service
public class MyService {

    @Autowired(required = false)
    private OptionalService optionalService;

    // Hoặc
    public MyService(Optional<OptionalService> optionalService) {
        this.optionalService = optionalService.orElse(null);
    }
}

7. COLLECTION INJECTION

public interface Plugin {
    String getName();
}

@Component
public class PluginA implements Plugin { }

@Component
public class PluginB implements Plugin { }

@Service
public class PluginManager {
    private final List<Plugin> plugins;

    public PluginManager(List<Plugin> plugins) {
        this.plugins = plugins;  // Tự động inject tất cả Plugin
    }
}

8. @ORDER VÀ @PRIORITY

@Component
@Order(1)
public class FirstPlugin implements Plugin { }

@Component
@Order(2)
public class SecondPlugin implements Plugin { }

9. @LOOKUP (Prototype trong Singleton)

Khi bean singleton cần bean prototype:

@Component
public abstract class CommandManager {

    public void process() {
        Command command = createCommand();  // Mỗi lần tạo mới
        command.execute();
    }

    @Lookup
    protected abstract Command createCommand();
}

10. @PROFILE

@Repository
@Profile("dev")
public class DevUserRepository implements UserRepository { }

@Repository
@Profile("prod")
public class ProdUserRepository implements UserRepository { }

11. @CONDITIONAL

@Configuration
public class AppConfig {

    @Bean
    @ConditionalOnProperty(name = "cache.enabled", havingValue = "true")
    public CacheService cacheService() {
        return new CacheService();
    }

    @Bean
    @ConditionalOnClass(name = "com.example.SomeClass")
    public SomeService someService() {
        return new SomeService();
    }
}

Các annotation tương tự:

  • @ConditionalOnBean
  • @ConditionalOnMissingBean
  • @ConditionalOnClass
  • @ConditionalOnProperty

12. BÀI TẬP THỰC HÀNH

Bài 1: Multi-implementation

Tạo interface PaymentService với 2 implementation: CreditCardPayment, MomoPayment. Sử dụng @Primary và @Qualifier.

Bài 2: Plugin System

Tạo hệ thống plugin với collection injection.

Bài 3: Profile

Tạo bean khác nhau cho dev và prod.


13. TÓM TẮT

Khái niệm Mô tả
@Component Generic bean
@Service, @Repository Stereotype
@Configuration + @Bean Khai báo bean thủ công
Constructor Injection Khuyến nghị
@Qualifier Chọn bean cụ thể
@Primary Bean ưu tiên
@Lazy Khởi tạo trễ
@Profile Theo môi trường
@Conditional Theo điều kiện

Bài tiếp theo: 5.4 Spring Data JPA


🧭 Điều Hướng Series

⬅️ Bài trước: BÀI 5.2: SPRING BOOT CƠ BẢN

📋 Lộ trình tổng quan: Xem Toàn Bộ Series

➡️ Bài tiếp theo: BÀI 5.4: SPRING DATA JPA


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí