먼저 의존성이란 무엇인지 알아보자.
사용하는 객체 A 와 사용되는 객체 B 가 있다고 예를 들어본다.
아래는 new 키워드를 사용하는 예시이다.
A는 B 를 사용하기 위해 new 키워드를 이용해 생성하고 B 의 메서드를 사용한다.
( DI 컨테이너에서는 new 키워드를 사용하지 않고, 스프링 프레임워크가 대신한다 )
아래와 같은 형태를 A 는 B 에 의존한다 라고 한다.
class A {
B b = new B();
b.methodX();
}
위의 상황에서 설계가 변경되어서 B 클래스에서 C 클래스로 변경하고, methodY 를 호출해야한다고 하면 아래와 같이 코드를 변경해야 한다.
class A {
C c = new C();
c.methodY();
}
이러한 방식으로 설계가 변경된다면 10군데든 , 100군데든 모두 찾아서 수정작업을 진행해야 할 것이다.
이러한 방법을 효율적으로 하기 위해 인터페이스를 이용한다.
A클래스와 B클래스 , A 클래스와 C 클래스 사이에 인터페이스를 생성한다.
B 클래스와 C 클래스는 생성한 인터페이스를 구현한다.
// I 인터페이스
interface class I {
methodX();
}
// I 인터페이스를 구현한 B 클래스
class B implements I {
medthodX() {
...
}
}
// I 인터페이스를 구현한 C 클래스
class C implements I {
medthodX() {
...
}
}
위와 같이 가정한다면 A 클래스에서는 아래와 같이 수정할 수 있다.
이는 인터페이스를 참조 받는 유형으로 사용하기 때문에 변수의 이름을 변경하지 않아도 된다.
바퀴를 갈아끼듯이 사용할 수 있다.
// B 래스의 메서드를 사용하고 싶을 때
lass A {
I i = new B();
i.methodX();
}
// C 클래스의 메서드를 사용하고 싶을 때
class A {
I i = new C();
i.methodX();
}
DI 컨테이너 인스턴스 생성을 맡기고 관리하는 것을 아래 5가지 규칙을 지키면 보장할 수 있다.
1. 인터페이스를 이용하여 의존성을 만든다.
- 의존하는 부분에 인터페이스를 사용한다.
2. 인스턴스를 명시적으로 생성하지 않는다.
- new 키워드를 사용하지 않는다
3. 어노테이션을 클래스에 부여한다.
- @Component 등을 이용해서 클래스나 메서드를 Bean으로 등록한다.
4. 스프링 프레임워크에서 인스턴스를 생성한다.
- 스프링 프레임워크는 시작할 때 대상의 프로젝트를 컴포넌트 스캔하여 Bean으로 등록한 클래스를 추출하고 인스턴스로 생성한다.
5. 인스턴스를 이용하고 싶은 곳에 어노테이션을 부여한다.
- @Autowired 등을 사용한다.
DI ( Dependency Injection ) : 의존성 주입
- 강하게 결합된 클래스들을 분리한다 -> 관심사의 분리
* 강한결합 : A 클래스 내부에서 B 라는 객체를 직접 생성하고 있을 때, B를 C로 바꾸고 싶다면, A 클래스를 수정해야한다. - 결합도를 낮추고 유연성을 확보해준다
- 테스트 작성을 용이하게 한다.
Spring 은 @AutoWired 어노테이션을 이용해 다양한 의존성주입 방법을 제공한다.
의존성 주입 방법으로는 3가지가 있다.
1. 생성자주입 ( 추천방법 )
2. 필드주입 -> Spring에서도 추천하지 않는 방법
3. Setter(수정) 주입
1. 생성자주입
public class MemberService {
private final MemberRepository memberRepository;
private final PasswordEncoder passwordEncoder;
@AutoWired // 생성자가 1개일때는 생략이 가능하다
public MemberService(MemberRepository memberRepository, PasswordEncoder passwordEncoder) {
this.memberRepository = memberRepository;
this.passwordEncoder = passwordEncoder;
}
- 생성자 주입은 생성자의 호출 시점에 1회 호출 되는 것이 보장된다.
- 의존관계가 설정되지 않으면 객체생성이 불가능하므로, 컴파일 단계에 인지가 가능하다.
- final 로 선언이 가능하다 -> 불변
- 테스트 코드에 용이하다
추천하는 이유를 상세히 알아보자
1. 순환참조를 방지
A -> B를 참조하고 B -> A 를 참조한다고 가정
계속해서 무한대로 순환하는데, 생성자를 통해주입하고 실행하면 BeanCurrentlyInCreationException 에러가 발생해
오류를 체크할 수 있다.
@Service
public class AService {
// 순환 참조
@Autowired
private BService bService;
public void HelloA() {
bService.HelloB();
}
}
@Service
public class BService {
// 순환 참조
@Autowired
private AService aService;
public void HelloB() {
aService.HelloA();
}
}
2. 불변성
final 로 선언해서 불변성을 보장할 수 있다. ( 객체가 변할 일이 없다 )
OOP 의 원칙중 OCP(Open Closed Pricipal ) 원리를 지킬 수 있다. - 개방 폐쇄의 원칙
null 의 입력을 방지해주는 역할도 한다.
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
2. 필드주입
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private MemberService memberService;
}
- Spring 에서는 이미 추천하지 공식적으로 추천하지 않는 방식이다.
- 프레임워크에 의존적이고 객체지향적으로 좋지 않다.
- 단위테스트를 진행할 경우 의존관계를 가지는 객체를 생성해서 주입할 수 없다.
- 애플리케이션 코드에서는 사용하지 않으며, 테스트 코드에서만 사용한다.
3. 수정자 주입
@Service
public class UserService {
private UserRepository userRepository;
private MemberService memberService;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Autowired
public void setMemberService(MemberService memberService) {
this.memberService = memberService;
}
}
- 주입받는 객체가 변경될 가능성이 있을 때 사용 -> 거의 없다.
- SetXXX 메서드를 사용하므로, 어디서든 변경이 가능하다
참고
https://mangkyu.tistory.com/125
https://dev-coco.tistory.com/70
https://mangkyu.tistory.com/150
'Spring > Spring-detail' 카테고리의 다른 글
PSA(Portable Service Abstraction) 이란? (0) | 2022.10.28 |
---|---|
AOP 란 무엇인가? + 예제 (0) | 2022.10.26 |
Ioc(Inversion of Control) 제어의 역전이란? (0) | 2022.10.26 |
@Bean 과 @Component 비교 (0) | 2022.10.24 |
빈을 등록하기 위한 방법 @Configuraion, @Bean, @Component (0) | 2022.10.14 |
댓글