공부한 github : https://github.com/anso33/SpringStudy_second.git
[ 섹션 7. 의존관계 자동 주입 ]
# 의존관계 주입 방법
+ 생성자 주입
: 생성자를 통해서 의존 관계를 주입 받는 방식
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
- 생성자를 1번만 호출하는 것을 보장
- 생성자가 1개만 있을 때는 @Autowired를 생략해도 자동 주입할 수 있다. (스프링 빈에서)
- 불변, 필수 의존관계에 사용
+ 수정자 주입 (setter)
: 수정자 메서드 setter()을 통해서 의존관계를 주입하는 방식
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
}
- 자바빈 프로퍼티 규약에 일치하는 방법
** 자바빈 프로퍼티 : 필드의 값을 직접 변경하지 않고 메서드 setter()를 이용하여 값을 읽거나 수정하는 규칙
+ 필드 주입
: 필드에 바로 주입하는 방식
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
- 외부에서 변경이 불가능해서 테스트하기 힘들다
- DI 프레임워크가 있어야만 한다.
- 쓰지마세요
> 스프링 설정을 목적으로 하는 @Configuration같은 곳에서만 특별한 용도로 사용한다.
+ 일반 메서드 주입
: 일반 메서드를 통해서 주입 받는 방식
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
- 한번에 여러 필드를 주입받을 수 있다.
- 일반적으로 잘 사용하지 않는다.
# 최근의 경향으로는 생성자 주입 방식을 권장
- 불변
- 의존관계의 대부분은 애플리케이션이 종료되기 전까지 불변해야한다.
- 수정자 주입을 사용하려면 setter()을 public 처리해야한다.
- 누락
- 생성자 주입 방식을 사용하면 주입할 데이터를 누락했을때 컴파일 오류(Null Point Exception)가 발생하여 어떤 값을 필수로 주입해야하는지 알 수 있다.
- final
- 생성자 주입 방식을 선택하면 필드에 final을 사용할 수 있어, 생성자에 값이 생성되지 않았을 때
컴파일 오류(variable discountPolicy might not have been initialized)가 발생한다.
# 롬복
: getter, setter, toString 등의 메서드 코드를 줄여주는 JAVA 라이브러리
>> 이 코드를
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
롬복을 사용하면 다음과 같이 줄일 수 있다.
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
}
>>> 생성자를 1개 두고 @Autowired는 생략, @RequiredArgsConstructor(롬복 라이브러리)를 사용하여 기능은 모두 유지하면서 코드는 간소화한다.
# 조회하는 빈이 2개 이상일 때 오류가 발생한다.
+ @Autowired는 빈을 Type으로 조회한다. > 타입으로 조회했을 때 선택된 빈이 2개 이상이면 오류가 발생한다.
ex) FixDiscountPolicy와 RateDiscountPolicy를 동시에 @component (스프링빈 등록)하면 오류(NoUniqueBeanDefinitionException)가 발생
NoUniqueBeanDefinitionException: No qualifying bean of type
'hello.core.discount.DiscountPolicy' available: expected single matching bean
but found 2: fixDiscountPolicy,rateDiscountPolicy
# 해결 방법
@Autowired 필드 명 매칭 ▶ @Qualifier → @Qualifier끼리 매칭 → 빈 이름 매칭 ▶ @Primary 사용
[ 섹션 8. 빈 생명주기 콜백 ]
# 빈 생명주기(Bean LifeCycle)
: 객체가 생성되는 과정부터 어떤 작업을 수행하고, 어떻게 소멸되는지의 과정.
- 스프링 컨테이너는 이런 빈(Bean) 객체의 생명주기를 컨테이너의 생명 주기 내에서 관리하고, 객체 생성이나 소멸 시 호출될 수 있는 콜백 메서드를 제공한다.
[ 참고 ]
https://haruhiism.tistory.com/186
# 빈 생명주기 콜백 시작
: 객체의 초기화와 종료 작업
- connect()를 호출해서 연결을 맺고, disConnect()를 호출해서 연결을 끊는다.
> 실행한 결과
여기서 객체를 생성하는 단계에는 url이 없어 url정보 없이 connect가 호출된다.
객체를 생성한 다음 외부에서 수정자 주입을 통해 setUrl()을 호출하면 url이 존재하게 된다.
** 스프링 빈의 이벤트 라이프사이클
: 스프링컨테이너생성 → 스프링 빈 생성 → 의존관계 주입 → 초기화 콜백 → 사용 → 소멸 전 콜백 → 스프링 종료
# 스프링이 Bean 생명주기 콜백을 지원하는 3가지 방법
- 인터페이스 InitializingBean, DisposableBean
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
- InitializingBean : afterPropertiesSet()으로 초기화를 지원
- DisposableBean : Destroy()로 소멸을 지원
스프링 컨테이너의 종료가 호출되자 소멸 메서드가 호출되었다.
** 단점
- 해당 인터페이스는 스프링 전용 인터페이스
- 초기화, 소멸 메서드의 이름을 변경할 수 없다.
- 개발자가 직접 코드를 고칠 수 없는 외부 라이브러리에는 적용할 수 없다.
- 설정 정보에 초기화 메서드, 종료 메서드 지정
: 설정 정보에 초기화, 소멸 메서드를 지정할 수 있다.
ex) @Bean(initMethod = "init", destroyMethod = "close")
>> 사용하여 실행한 결과
+ 설정 정보 사용 특징
- 메서드 이름을 자유롭게 줄 수 있다.
- 스프링 빈이 스프링 코드에 의존하지 않는다.
- 코드가 아니라 설정 정보를 사용하기 때문에 코드를 고칠 수 없는 외부 라이브러리에도 초기화, 종료 메서드를 적용할 수 있다.
+ 종료 메서드 추론
- @Bean의 destroyMethod 속성에는 아주 특별한 기능이 있다.
- 라이브러리는 대부분 close , shutdown 이라는 이름의 종료 메서드를 사용한다.
- @Bean의 destroyMethod 는 기본값이 (inferred)(추론)으로 등록되어 있다.
- 이 추론 기능은 close , shutdown 라는 이름의 메서드를 자동으로 호출해준다. 이름 그대로 종료 메서드를 추론해서 호출해준다.
- 따라서 직접 스프링 빈으로 등록하면 종료 메서드는 따로 적어주지 않아도 잘 동작한다.
- 추론 기능을 사용하기 싫으면 destroyMethod="" 처럼 빈 공백을 지정하면 된다.
- @PostConstruct, @PreDestroy 애노테이션 지원
- 최신 스프링에서 가장 권장하는 방법이다.
- 애노테이션 하나만 붙이면 모두 해결!
- 패키지가 javax.annotation.PostConstruct == 스프링에 종속적인 기술이 아니라 JSR-250 라는 자바 표준
즉, 스프링이 아닌 다른 컨테이너에서도 동작한다.
- 컴포넌트 스캔과 잘 어울린다.
**단점
: 외부 라이브러리에는 적용하지 못한다
(해결방법) : 외부 라이브러리를 초기화, 종료 해야 하면 @Bean의 기능을 사용하자.
>> @PostConstruct, @PreDestroy 애노테이션을 사용하고
코드를 고칠 수 없는 외부 라이브러리를 초기화, 종료해야할 때는 @Bean 의 initMethod , destroyMethod 를 사용하자.
'Spring | SpringBoot' 카테고리의 다른 글
[스프링 스터디] 1주차. 단위테스트, 롬복 (0) | 2022.05.07 |
---|---|
[스프링 스터디] 7주차 - 스프링 핵심 원리.기본편 (마지막) (0) | 2022.02.24 |
[스프링 스터디] 5주차 - 스프링 핵심 원리.기본편 (0) | 2022.02.10 |
[스프링 스터디] 4주차 - 스프링 핵심 원리.기본편 (0) | 2022.01.30 |
[스프링 스터디] 3주차 - 스프링 핵심 원리.기본편 (0) | 2022.01.25 |
댓글