반응형
Dependency Injection
설명
- A가 B에 의존할 경우 B가 변경이 발생하면 A에 영향을 미침
- 직접 의존 관계를 맺는 것보다 인터페이스를 통해 의존 관계를 맺어 결합도를 낮추는 것이 중요함
- 컨테이너는 의존 관계를 동적으로 설정해주는 역할을 수행
- 의존성 주입 방법에는 생성자 주입, 필드 주입, setter 주입이 있음
Constructor-Based Dependency Injection
설명
- 생성자를 통해 의존성을 주입받는 방식 (권장)
- 장점
- 필수적으로 사용해야하는 의존 객체가 없이는 인스턴스를 만들지 못하도록 강제할 수 있음
- 순환 참조 의존성을 빠르게 확인할 수 있음
- 의존 객체 필드를 final로 지정하여 불변함을 강제화할 수 있음
- 테스트 코드 작성시 생성자를 통해 의존성 주입이 쉬움
- 의존 관계가 복잡해지는 것을 쉽게 알아차려 리팩토링 시점(Single Response Principle이 깨지는 시점)을 확인하기 쉬움
- 단점
- 어쩔 수 없이 순환 참조가 필요한 경우 사용 불가
기본 예시
@Service
public class StudentService {
private final UserService userService;
public StudentService(UserService userService) {
this.userService = userService;
}
}
순환 참조 예시
@Service
public class StudentService {
private final UserService userService;
public StudentService(UserService userService) {
this.userService = userService;
}
}
@Service
public class UserService {
private final StudentService studentService;
public UserService(StudentService studentService) {
this.studentService = studentService;
}
}
- 서버 실행시 아래 로그 발생 후 종료
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| studentService defined in file [D:\workspace\spring-example\target\classes\com\example\springexample\StudentService.class]
↑ ↓
| userService defined in file [D:\workspace\spring-example\target\classes\com\example\springexample\UserService.class]
└─────┘
Field-Based Dependency Injection
설명
- 필드에 @Autowired 어노테이션을 달아 주입 받는 방식 (비권장)
- 장점
- 가장 간단하게 사용 가능
- 단점
- 의존 관계 설정을 쉽게 할 수 있기 때문에 의존 관계가 복잡해질 수 있음
- DI Container와 강한 결합을 갖는 방법이기 때문에 컨테이너 외의 다른곳에서 사용시 주입하기 어려움
- 단위 테스트시 의존성 주입이 어려움
- 의존성 주입 대상 필드를 final로 지정할 수 없어 의존 객체가 변할 수 있는 위험이 있음
기본 예시
@Service
public class StudentService {
@Autowired
private UserService userService;
}
순환 참조 예시
@Service
public class StudentService {
@Autowired
private UserService userService;
public void test() {
userService.test();
}
}
@Service
public class UserService {
@Autowired
private StudentService studentService;
public void test() {
studentService.test();
}
}
@Component
public class TestComponent {
@Autowired
private StudentService studentService;
@PostConstruct
public void init () {
studentService.test();
}
}
- 서버 실행은 문제 없이 진행되고, 순환 호출 관계에 있는 메소드 실행시 아래 오류 발생
2020-12-11 20:38:16.772 ERROR 16936 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testComponent': Invocation of init method failed; nested exception is java.lang.StackOverflowError
at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:160) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:429) ~[spring-beans-5.3.1.jar:5.3.1]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1780) ~[spring-beans-5.3.1.jar:5.3.1]
Setter-Based Dependency Injection
설명
- setter 메서드에 @Autowired 어노테이션을 달아 주입 받는 방식
- 장단점 모두 필드 주입 방식과 동일함
- 다만 setter 주입 방식은 일반 자바 문법을 사용하는 방식이기 때문에 DI Container 종류 상관 없이 사용 가능하다는 장점이 추가됨
기본 예시
@Service
public class StudentService {
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
}
DI Annotations
설명
- 주입 받을 대상에 어노테이션을 달아 설정하는 방식을 사용함
- 종류로는 @Autowired, @Resource, @Inject가 있음
차이점
- 지원
- @Resource : java
- @Autowired : spring
- @Inject : javax
- 사용 가능 위치
- @Resource : 필드, setter
- @Autowired : 생성자, 필드, setter
- @Inject : 생성자, 필드, setter
- Bean 강제 지정 방식
- @Resource : @Resource(name = "id")
- @Autowired : @Autowired @Qualifier("id")
- @Inject : @Inject @Named("id")
- Bean 검색 순위
- @Resource : 이름 → 타입
- @Autowired : 타입 → 이름
- @Inject : 타입 → 이름
테스트 공통코드
public interface Animal {
}
public class Dog implements Animal {
}
public class Cat implements Animal {
}
@Resource 테스트
- 이름 → 타입순으로 빈을 탐색하기 때문에 아래같은 경우 찾은 빈이 다른 타입을 가지므로 오류 발생
@Configuration
public class AnimalConfig {
@Bean
public Cat cat() {
return new Cat();
}
@Bean
public Dog dog() {
return new Dog();
}
}
@Service
public class AnimalService {
@Resource
private Dog cat; // error
}
- cat이라는 이름의 빈이 없으므로 타입 탐색에 의해 주입하기 때문에 오류 발생 없이 Dog 타입 객체가 주입된다.
@Configuration
public class AnimalConfig {
@Bean
public Dog dog() {
return new Dog();
}
}
@Service
public class AnimalService {
@Resource
private Dog cat; // Dog 타입 객체
}
@Autowired 테스트
- 타입 → 이름순으로 매칭된 객체를 주입하기 때문에 아래 케이스는 Dog 객체가 주입됨
@Configuration
public class AnimalConfig {
@Bean
public Cat cat() {
return new Cat();
}
@Bean
public Dog dog() {
return new Dog();
}
}
@Service
public class AnimalService {
@Autowired
private Dog cat; // Dog 객체
}
참고
반응형
'Development > Spring' 카테고리의 다른 글
[Spring] ORM (with JPA, Hibernate) (0) | 2020.12.27 |
---|---|
[Spring] WebSocket (0) | 2020.12.27 |
[Spring] Distributed Lock (with MySQL, Redis) (4) | 2020.12.27 |
[Spring] Transactional (0) | 2020.12.27 |
[Spring] AOP (0) | 2020.12.27 |