0

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

📚 Series: Java Backend Zero to Hello 📂 Phân đoạn: Phase 5: Spring Framework & Spring Boot 📖 Nội dung: BÀI 5.3: BEAN & DEPENDENCY INJECTION 💡 Khóa học lập trình Backend Java & Spring Boot chuẩn doanh nghiệp từ con số 0.


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 bài học

⭐ Hãy bookmark (clip) lại series để tiện theo dõi các bài học tiếp theo nhé!


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í