π μμ‘΄μ± μ£Όμ λ°©λ²
- μμ±μ μ£Όμ (Constructor Injection)
- νλ μ£Όμ (Field Injection)
- μμ μ μ£Όμ (Setter Injection)
1οΈβ£ μμ±μ μ£Όμ (Constructor Injection)
μμ±μ μ£Όμ λ°©λ²μ μμ±μλ₯Ό ν΅ν΄ μμ‘΄ κ΄κ³λ₯Ό μ£Όμ νλ€.
μμ±μ μ£Όμ μ μμ±μμ νΈμΆ μμ μ 1ν νΈμΆ λλ κ²μ΄ 보μ₯λκΈ° λλ¬Έμ μ£Όμ λ°μ κ°μ²΄κ° λ³νμ§ μκ±°λ, λ°λμ κ°μ²΄μ μ£Όμ μ΄ νμν κ²½μ°μ κ°μ νκΈ° μν΄ μ¬μ©ν μ μλ€.
@Service
public class ProductService {
// finalμ λΆμΌ μ μμ
private final ProductRepository productRepository;
@Autowired // μλ΅ κ°λ₯
public ProductRepository(ProductRepository productRepository) {
this.productRepository = productRepository;
}
}
Spring νλ μμν¬μμλ μμ±μ μ£Όμ μ μ κ·Ή μ§μν΄ μμ±μκ° 1κ°λ§ μμ κ²½μ°μ @Autowiredλ₯Ό μλ΅ν΄λ μ£Όμ μ΄ κ°λ₯νλλ‘ νΈμμ±μ μ 곡νκ³ μλ€.
보λμ€ μ΄λμ final ν€μλλ₯Ό μ¬μ©ν΄ ν λ² μ΄κΈ°νλ κ°μ΄ μ΄ν λ³κ²½λ μ μλλ‘ λ³΄μ₯ν μ μλ€λ κ²μ΄λ€.
μμ±μ μ£Όμ μ ν΄λμ€(κ°μ²΄)κ° μμ±λ λ, μμ‘΄μ±μ΄ ν¨κ» μ£Όμ λλ€. μ΄ λ, μμ±μλ₯Ό ν΅ν΄ νλμ κ°μ ν λΉν΄ ν λ² κ°μ΄ μ€μ λ νμλ λ³κ²½λμ§ μκ²λ finalμ μ¬μ©ν μ μλ€.
νμ§λ§ μμ μ μ£Όμ μ΄λ νλ μ£Όμ μμλ ν΄λμ€ μΈμ€ν΄μ€κ° λ¨Όμ μμ±λ νμ, λμ€μ μ£Όμ μ΄ μ΄λ£¨μ΄μ§λ€. final νλλ μ μΈκ³Ό λμμ μ΄κΈ°νλκ±°λ, μμ±μμμ μ΄κΈ°νλμ΄μΌ νλ€. νμ§λ§ νλ μ£Όμ μ΄λ μμ μ μ£Όμ λ°©μμ μμ λ§ν κ² μ²λΌ ν΄λμ€κ° μ΄λ―Έ μμ±λ μ΄νμ μμ‘΄μ±μ΄ μ£Όμ λκΈ° λλ¬Έμ, μ΄ μμ μ finalμ μ¬μ©ν μ μλ€.
2οΈβ£ μμ μ μ£Όμ (Setter Injection)
μμ μ μ£Όμ λ°©λ²μ νλ κ°μ λ³κ²½νλ Setter λ©μλλ₯Ό μ¬μ©νμ¬ μμ‘΄μ±μ μ£Όμ νλ€.
Setter μ£Όμ μ λ§λ€μ΄μ§ κ°μ²΄λ₯Ό ν΅ν΄ μ¬λ¬λ² νΈμΆμ΄ κ°λ₯νλ―λ‘ μμ±μ μ£Όμ κ³Ό λ€λ₯΄κ² 1ν νΈμΆμ 보μ₯νμ§ λͺ»νλ€.
μ£Όμ λ°λ κ°μ²΄κ° λ³κ²½λ κ°λ₯μ±μ΄ μλ κ²½μ°μ μ¬μ©νλ€. (μ€μ λ‘ λ³κ²½μ΄ νμν κ²½μ°λ κ·Ήν λλ¬Όλ€.)
@Service
public class ProductService {
private ProductRepository productRepository;
@Autowired
public setProductRepository(ProductRepository productRepository) {
this.productRepository = productRepository;
}
}
μμ μ μ£Όμ λ°©μμ @Autowiredλ‘ μ£Όμ ν λμμ΄ μλ κ²½μ° μ€λ₯κ° λ°μνλ€.
μ£Όμ ν λμμ΄ μμ΄λ λμνλλ‘ νλ €λ©΄ @Autowired(required = false)λ₯Ό ν΅ν΄ μ€μ ν μ μλ€.
3οΈβ£ νλ μ£Όμ (Field Injection)
νλ μ£Όμ λ°©λ²μ νλμ μ§μ μμ‘΄μ±μ λΆμ¬νλ λ°©λ²μ΄λ€.
νλ μ£Όμ λ°©λ²μ μ¬μ©νλ©΄ μ½λκ° κ°κ²°ν΄μ§μ§λ§ μΈλΆμμ μ κ·Όμ΄ λΆκ°λ₯νλ€λ λ¨μ μ΄ μλ€.
μ΄λ ν μ€νΈ μ½λμ μ€μμ±μ΄ λΆκ°λ¨μ λ°λΌ νλμ κ°μ²΄λ₯Ό μμ ν μ μλ νλ μ£Όμ μ κ±°μ μ¬μ©λμ§ μκ² λμλ€.
(νλ μ£Όμ μ μμ‘΄μ±μ μ§μ μ μΌλ‘ μ£Όμ λ°κΈ° λλ¬Έμ, ν μ€νΈ μ λͺ¨μ(mock) κ°μ²΄λ₯Ό μ½κ² μ£Όμ ν μ μμ΄ ν μ€νΈ μμ±μ΄ μ΄λ €μμ§λ€.)
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
}
IntelliJμμ νλ μ£Όμ μ μ¬μ©νλ©΄ Field injection is not recommended μ΄λΌλ κ²½κ³ λ¬Έκ΅¬κ° λ°μνλ€.
Spring Team recommends: “Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies”.
ν΅μ¬μ μμ±μ μ£Όμ λ°©μμ μ΄μ©νλΌλ κ²μΈλ°, Spring Teamμμ μμ±μ μ£Όμ λ°©μμ κΆμ₯νλ μ΄μ λ 무μμΌκΉ?
π μ μμ±μ μ£Όμ λ°©μμ΄ κΆμ₯λλλ°?
- μν μ°Έμ‘° λ°©μ§
- μ ν리μΌμ΄μ ꡬλ μμ μ μν μ°Έμ‘° μλ¬ μλ°© κ°λ₯
- ν
μ€νΈ μ©μ΄
- λ¨μ POJOλ₯Ό μ΄μ©ν ν μ€νΈ μ½λ μμ± κ°λ₯
- μ€νλ§μ λ 립μ μΈ μ½λ μμ±
- κ°μ²΄ λΆλ³μ±(Immutability) ν보
- μ€ν μ€μ κ°μ²΄κ° λ³νλ κ² λ°©μ§
→ μ€λ₯ μ¬μ λ°©μ§
- μ€ν μ€μ κ°μ²΄κ° λ³νλ κ² λ°©μ§
- final ν€μλ μμ± λ° Lombokκ³Όμ κ²°ν©
π± μν μ°Έμ‘° λ°©μ§
μμ±μ μ£Όμ μ¬μ© μ μ ν리μΌμ΄μ ꡬλ μμ (κ°μ²΄μ μμ± μ§μ )μ μν μ°Έμ‘° μλ¬λ₯Ό μλ°©ν μ μλ€.
μλ₯Όλ€μ΄ λ€μκ³Ό κ°μ΄ νλ μ£Όμ μ μ¬μ©ν΄ μλ‘ νΈμΆνλ μ½λκ° μλ€.
@Service
public class ProductService {
@Autowired
private ReviewService reviewService;
@Override
public List<Review> getReview(Long productId) {
return reviewService.getReview(productId);
}
}
ProductServiceκ° μ΄λ―Έ ReviewServiceμ μμ‘΄νκ³ μλλ°, ReviewServiceλ ProductServiceμ μμ‘΄νκ³ μλ€.
@Service
public class ReviewService {
@Autowired
private ProductService productService;
@Override
public List<Review> getProductReview(Long productId) {
return productService.getReview(productId);
}
}
μμ λ λ©μλλ μλ‘λ₯Ό κ³μ νΈμΆν κ²μ΄κ³ , λ©λͺ¨λ¦¬μ ν¨μμ CallStackμ΄ κ³μ μμ¬ StackOverflow μλ¬κ° λ°μνκ² λλ€.
Caused by: java.lang.StackOverflowError: null
at com.example.ProductService.getReview(ProductService.java:20) ~[main/:na]
at com.example.ReviewService.getProductReview(ReviewService.java:14) ~[main/:na]
at com.example.ProductService.getReview(ProductService.java:20) ~[main/:na]
at com.example.ReviewService.getProductReview(ReviewService.java:14) ~[main/:na]
λ¬Έμ λ μ ν리μΌμ΄μ μ΄ μ무 μ€λ₯κ° κ²½κ³ μμ΄ κ΅¬λλΌ μ€μ μ½λκ° νΈμΆλκΈ° μ κΉμ§ λ¬Έμ λ₯Ό λ°κ²¬ν μ μλ€λ κ²μ΄λ€.
κ·Έλ λ€λ©΄ μμ±μ μ£Όμ μ μ¬μ©ν κ²½μ°μλ μ΄λ»κ² λ κΉ?
@Service
public class ProductService {
private final ReviewService reviewService;
public ReviewService(ReviewService reviewService) {
this.reviewService = reviewService;
}
}
@Service
public class ReviewService {
private final ProductService productService;
public ProductService(ProductService productService) {
this.productService = productService;
}
}
μ ν리μΌμ΄μ μ€ν μ μ ν리μΌμ΄μ ꡬλ μμ (κ°μ²΄μ μμ± μ§μ )μ BeanCurrentlyInCreationExceptionμ΄ λ°μνμ¬ μ€λ₯λ₯Ό μ¬μ μ μ μ μλ€.
Description:
The dependencies of some of the beans in the application context form a cycle:
βββββββ
| reviewService defined in file [~~~\ReviewService.class]
↑ ↓
| productService defined in file [~~~\ProductService.class]
βββββββ
π€ μ μ΄λ° μ°¨μ΄κ° λ κΉ?
μ΄λ¬ν μ€ν κ²°κ³Όμ μ°¨μ΄λ μμ±μ μ£Όμ μ Beanμ μ£Όμ νλ μμκ° νλ μ£Όμ κ³Ό μμ μ μ£Όμ κ³Όλ λ€λ₯΄κΈ° λλ¬Έμ΄λ€.
@Autowiredλ₯Ό μ΄μ©ν νλ μ£Όμ μ νμ λ μ ν리μΌμ΄μ ꡬλ μμ μ μλ¬κ° λ°μνμ§ μλ μ΄μ λ λΉμ μμ±κ³Ό 쑰립(@Autowired) μμ μ΄ λΆλ¦¬λμ΄ μκΈ° λλ¬Έμ΄λ€. μμ±μ μ£Όμ μ κ°μ²΄μ μμ±κ³Ό 쑰립(μμ‘΄κ΄κ³ μ£Όμ )μ΄ λμμ μ€νλΌ μμ κ°μ μλ¬λ₯Ό μ¬μ μ μ‘μ μ μλ€. νμ§λ§ @Autowiredλ λͺ¨λ Bean μμ±μ΄ μλ£λ νμ 쑰립(μμ‘΄κ΄κ³ μ£Όμ )μ΄ μ²λ¦¬λΌ νΈμΆμ΄ λκ³ λμμΌ μν μ΄μλ₯Ό νμΈν μ μλ κ²μ΄λ€.
μ°Έκ³ λ‘ Spring Boot 2.6 λΆν°λ κΈ°λ³Έμ μΌλ‘ Bean κ°μ μν μ°Έμ‘°κ° κΈμ§λλλ‘ λ³κ²½λμλ€.
π± ν μ€νΈμ μ©μ΄
μμ±μ μ£Όμ μ¬μ© μ ν μ€νΈ μ½λλ₯Ό λ νΈλ¦¬νκ² μμ±ν μ μλ€.
ν μ€νΈκ° νΉμ νλ μμν¬μ μμ‘΄νλ κ²μ ν μ€νΈ κ²©λ¦¬κ° μ΄λ ΅κ³ μ μ°μ±μ΄ κ°μν΄ μ’μ§ μλ€. λ°λΌμ λ 립μ μΌλ‘ μΈμ€ν΄μ€νκ° κ°λ₯ν POJO(Plain Old Java Object)λ‘ μμ±νλ κ²μ΄ μ’μλ°, μμ±μ μ£Όμ μ΄ μλ λ€λ₯Έ μ£Όμ μΌλ‘ μμ±λ μ½λλ μμ μλ° μ½λλ‘ λ¨μ ν μ€νΈλ₯Ό μμ±νλ κ²μ΄ μ΄λ ΅λ€.
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
public void createProduct(String name) {
productRepository.create(name);
}
}
μλ₯Όλ€μ΄ μμ κ°μ νλ μ£Όμ λ°©μ μ½λμ λν΄ μμ μλ° ν μ€νΈ μ½λλ₯Ό μμ±νλ©΄ λ€μκ³Ό κ°μ΄ μμ±ν μ μλ€.
public class ProductServiceTest {
@Test
public void createProductTest() {
ProductService productService = new ProductService();
productService.createProduct("μν1");
}
}
μμ ν μ€νΈ μ½λλ Spring μμμ λμνμ§ μμΌλ―λ‘ μμ‘΄κ΄κ³ μ£Όμ μ΄ λμ§ μλλ€. λ°λΌμ postRepositoryκ° nullμ΄ λμ΄ findById νΈμΆ μ NPEκ° λ°μν κ²μ΄λ€. μ΄λ₯Ό ν΄κ²°νκΈ° μν΄ Setter μ¬μ© μ λ³κ²½ κ°λ₯μ±μ μ΄μ΄λκ² λλ λ¨μ μ κ°κ² λλ€.
λ°λλ‘ ν μ€νΈ μ½λμμ @Autowiredλ₯Ό μ¬μ©νκΈ° μν΄ μ€νλ§μ μ¬μ©νλ©΄ λ¨μ ν μ€νΈκ° μλ λΏ μλλΌ, μ»΄ν¬λνΈλ€μ λ±λ‘νκ³ μ΄κΈ°ννλ μκ° λλ¬Έμ ν μ€νΈ λΉμ©μ΄ μ¦κ°νκ² λλ€.
λ°λ©΄μ μμ±μ μ£Όμ μ¬μ© μ μ»΄νμΌ μμ μ κ°μ²΄λ₯Ό μ£Όμ λ°μ ν μ€νΈ μ½λλ₯Ό μμ±ν μ μμΌλ©°, μ£Όμ νλ κ°μ²΄κ° λλ½λ κ²½μ° μ»΄νμΌ μμ μ μ€λ₯λ₯Ό λ°κ²¬ν μ μλ€.
π± μ€νλ§μ λ 립μ μΈ μ½λ μμ±
νλ μ£Όμ μ¬μ© μ @Autowiredλ₯Ό μ΄μ©ν΄μΌ νλλ°, μ΄κ²μ μ€νλ§μ΄ μ 곡νλ μ΄λ Έν μ΄μ μ΄λ€.
κ·Έλ¬λ―λ‘ @Autowiredλ₯Ό μ¬μ©νλ©΄ ProductServiceμ μ€νλ§ μμ‘΄μ±μ΄ μκΈ΄λ€.
import org.springframework.beans.factory.annotation.Autowired;
// μ€νλ§ μμ‘΄μ±μ΄ ProductServiceμ importλμ΄ μ½λλ‘ λ°ν
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
}
μ¬μ©νλ νλ μμν¬λ μΈμ λ°λμ§ λͺ¨λ₯΄κ³ , νλ μμν¬λ λΉμ¦λμ€ λ‘μ§μ μμ±νλ μλΉμ€ κ³μΈ΅μμ μμμΌ ν λμμ΄ μλλ€.
μ΄λ νμν μλ° νμΌμ importν΄μΌνλ μ μ μΈμ΄μΈ μλ°μ νκ³μ΄κΈ°λ νλ, κ°λ₯ν μ€νλ§ μμ΄ μ½λκ° μμ±λλ©΄ λμ± μ μ°ν μ½λ νλ³΄κ° κ°λ₯νλ€.
π± κ°μ²΄ λΆλ³μ±(Immutability) ν보
μ€μ κ°λ°μ νλ€λ³΄λ©΄ μμ‘΄ κ΄κ³μ λ³κ²½μ΄ νμν μν©μ κ±°μμλ€. νμ§λ§ μμ μ μ£Όμ μ΄λ μΌλ° λ©μλ μ£Όμ μ μ΄μ©νλ©΄ λΆνμνκ² μμ μ κ°λ₯μ±μ μ΄μ΄λμ΄ μ μ§λ³΄μμ±μ λ¨μ΄λ¨λ¦°λ€. κ·Έλ¬λ―λ‘ μμ±μ μ£Όμ μ ν΅ν΄ λ³κ²½μ΄ κ°λ₯μ±μ λ°°μ νκ³ λΆλ³μ±μ 보μ₯νλκ²μ΄ μ’λ€.
μ΄λ μ€λ₯λ₯Ό μ¬μ μ λ°©μ§ν μ μλ μ₯μ μ κ°μ§κΈ°λ νλ€.
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
public void someMethod() {
productRepository = null;
productRepository.call();
}
}
νλ μ£Όμ μ μ¬μ©ν μ½λλ final ν€μλλ₯Ό μ¬μ©ν μ μλ€. λ°λΌμ λ°νμ μμ μ λ³κ²½λ μ μλ€.
μ μ½λμμλ nullμ μ°Έμ‘°νλλ‘ λ³κ²½νκΈ° λλ¬Έμ NullPointerExceptionμ΄ λ°μν κ²μ΄λ€.
νμ§λ§ μμ±μ μ£Όμ μ μ¬μ©νλ€λ©΄ μ΄μ κ°μ μν©μ μ»΄νμΌ μμ μ λ°©μ§ν μ μλ€.
@Service
public class ProductService {
private final ProductRepository productRepository;
public ProductRepository(ProductRepository productRepository) {
this.productRepository = productRepository;
}
public void someMethod() {
// final λ³μμ κ°μ μ¬ν λΉ ν μ μμ
productRepository = null;
}
}
π± final ν€μλ μμ± λ° Lombokκ³Όμ κ²°ν©
μμ±μ μ£Όμ μ μ¬μ©νλ©΄ νλ κ°μ²΄μ final ν€μλλ₯Ό μ¬μ©ν μ μμΌλ©° μ»΄νμΌ μμ μ λλ½λ μμ‘΄μ±μ νμΈν μ μλ€.
λ°λ©΄μ λ€λ₯Έ μ£Όμ λ°©λ²λ€μ κ°μ²΄μ μμ±(μμ±μ νΈμΆ) μ΄νμ νΈμΆλλ―λ‘ final ν€μλλ₯Ό μ¬μ©ν μ μλ€.
final ν€μλλ‘ νλλ₯Ό λͺ μμ μΌλ‘ μ μΈνλ©΄ μ½λμ κ°λ μ±κ³Ό μμ μ±μ λμΌ μ μμΌλ©°, λ€λ₯Έ κ°λ°μλ€μ΄ ν΄λΉ νλκ° λΆλ³μ΄λΌλ κ²μ μ μ μμ΄ μ½λμ μ΄ν΄λ₯Ό λμΈ μ μλ μ₯μ μ΄ μλ€.
λν final ν€μλλ₯Ό λΆμ΄λ©΄ Lombokκ³Ό κ²°ν©λμ΄ μ½λλ₯Ό κ°κ²°νκ² μμ±ν μ μλ€. Lombokμλ final λ³μλ₯Ό μν μμ±μλ₯Ό λμ μμ±ν΄μ£Όλ @RequiredArgsConstructorκ° μλλ° μμ±μ μ£Όμ
μ½λλ₯Ό Lombokκ³Ό κ²°ν©μν€λ©΄ λ€μκ³Ό κ°μ΄ κ°νΈνκ² μμ±ν μ μλ€.
@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductRepository productRepository;
private final UserRepository userRepository;
public void createProduct(String name) {
productRepository.create(name);
}
}
'Back-end' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
[JPA] EntityManager ν΅μ¬ κΈ°λ₯ (0) | 2024.04.19 |
---|---|
[Spring] Spring REST Docs κΆν μλ¬ (2) | 2024.02.29 |
[JPA] μμμ± μ»¨ν μ€νΈμ λμμ리μ μ΄μ (0) | 2024.02.14 |
[JPA] OSIV(Open Session In View)λ? (0) | 2023.09.21 |
[JPA] Hibernate Cacheλ? (0) | 2023.09.03 |