목차
의존주입 방법
설정 클래스에서 직접 의존 주입
설정클래스에서 의존객체를 직접 주입해주는 방법이다.
@Configuration
public class AppCtx {
@Bean
public MemberDao memberDao(){
return new MemberDao();
}
@Bean
public MemberRegisterService memberRegisterService(){
return new MemberRegisterService(memberDao());
}
@Bean
public ChangePasswordService changePasswordService(){
ChangePasswordService changePasswordService = new ChangePasswordService();
changePasswordService.setMemberDao(memberDao());
return changePasswordService;
}
}
자동 의존 주입
설정클래스에서 직접 주입하지 않고, 스프링이 자동으로 의존 객체를 주입해주는 방법이다.
설정클래스에서 생성자, setter로 memberDao를 주입해주는 코드를 모두 주석처리한다.
@Configuration
public class AppCtx {
@Bean
public MemberDao memberDao(){
return new MemberDao();
}
@Bean
public MemberRegisterService memberRegisterService(){
// return new MemberRegisterService(memberDao());
return new MemberRegisterService();
}
@Bean
public ChangePasswordService changePasswordService(){
ChangePasswordService changePasswordService = new ChangePasswordService();
// changePasswordService.setMemberDao(memberDao());
return changePasswordService;
}
}
의존을 주입받는 클래스에서 필드 또는 Setter메서드에 @Autowired를 붙인다.
(여기서 생성자에 @Autowired를 붙이면 AppCtx에서 @Bean을 생성할 때 에러가 발생한다. )
public class MemberRegisterService {
@Autowired //⭐
private MemberDao memberDao;
// constructor DI
// public MemberRegisterService(MemberDao memberDao) {
// this.memberDao = memberDao;
// }
public Long regist(RegisterDTO req){
//회원가입 로직
...
}
}
public class ChangePasswordService {
@Autowired //⭐
private MemberDao memberDao;
// @Autowired //⭐ 여기에 붙여도된다.
// public void setMemberDao(MemberDao memberDao) {
// this.memberDao = memberDao;
// }
public void changePassword(String email, String oldpwd, String newpwd){
//비밀번호 변경로직
...
}
}
@Autowired 어노테이션을 필드나 setter메서드에 붙이면
스프링 컨테이너는 타입이 일치하는 Bean객체를 찾아 주입해준다.
@Autowired 의존 주입 시 타입 일치하는 Bean이 없는 경우
UnsatisfiedDependencyException 발생
@Autowired 의존 주입 시 타입 일치하는 Bean이 2개 이상인 경우 -> @Qualifier 어노테이션 사용
@Configuration
public class AppCtx {
@Bean
public MemberDao memberDao1(){ //⭐
return new MemberDao();
}
@Bean
public MemberDao memberDao2(){ //⭐
return new MemberDao();
}
@Bean
public MemberRegisterService memberRegisterService(){
return new MemberRegisterService();
}
@Bean
public ChangePasswordService changePasswordService(){
return new ChangePasswordService();
}
}
public class MemberRegisterService {
@Autowired
private MemberDao memberDao;
...
}
public class ChangePasswordService {
@Autowired
private MemberDao memberDao;
...
}
그냥 실행하면 NoUniqueBeanDefinitionException 발생
@Qualifier를 사용하여 해결할 수 있다.
@Configuration
public class AppCtx {
@Bean
@Qualifier("memberDaoForRegister") //⭐
public MemberDao memberDao1(){
return new MemberDao();
}
@Bean
public MemberDao memberDao2(){
return new MemberDao();
}
@Bean
public MemberRegisterService memberRegisterService(){
return new MemberRegisterService();
}
@Bean
public ChangePasswordService changePasswordService(){
return new ChangePasswordService();
}
}
public class MemberRegisterService {
@Autowired
@Qualifier("memberDaoForRegister") //⭐
private MemberDao memberDao;
public Long regist(RegisterDTO req){
//회원가입 로직
}
}
AppCtx의 @Bean 등록 시 @Qualifier로 이름 지정, @Autowired로 빈 주입 시 @Qualifier로 이름 지정
양쪽에 적어주어서 빈의 이름으로 구분한다.
(동일 타입 빈이 2개일때, 하나에만 Qualifier를 붙여도 두 빈을 구분할 수 있기 때문에 오류없이 실행 가능하다)
상위/하위 타입 관계와 자동주입
public class MemberParentPrinter {
}
public class MemberChildPrinter extends MemberParentPrinter{
}
상속관계에서 @Qualifier 애노테이션을 안붙인다면 이것도 NoUniqueBeanDefinitionException이 발생한다.
@Autowired 필수 여부
@Autowired는 기본적으로 타입에 해당하는 빈이 존재하지 않으면 Exception이 발생한다.
@Autowired(required = false)
public void setDateFormatter(DateTimeFormatter dateTimeFormatter){
this.dateTimeFormatter = dateTimeFormatter;
}
@Autowired
public void setDateFormatter(Optional<DateTimeFormatter> dateTimeFormatter){
this.dateTimeFormatter = dateTimeFormatter;
}
@Autowired
public void setDateFormatter(@Nullable DateTimeFormatter dateTimeFormatter){
this.dateTimeFormatter = dateTimeFormatter;
}
자동 주입할 대상이 필수가 아닌 경우에는
@Autowired 애노테이션의 required 속성을 false로 지정하거나,
Optional을 사용하거나, @Nullable을 사용하여 자동주입을 수행하지 않으면된다.
위의 방식은 필드에도 그대로 적용된다.
@Autowired(required = false)
private DateTimeFormatter dateTimeFormatter;
@Autowired
private Optional<DateTimeFormatter> dateTimeFormatter;
@Autowired
@Nullable
private DateTimeFormatter dateTimeFormatter;
생성자 초기화와 필수 여부 지정
public class MemberPrinter{
private DateTimeFormatter dateTimeFormatter;
public MemberPrinter(){
dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");
}
@Autowired(required = false)
public void setDateTimeFormatter(DateTimeFormatter dateTimeFormatter){
this.dateTimeFormatter = dateTimeFormatter;
}
}
위의 코드를 실행하면 DateTimeFormatter는 기본생성자에서 초기화 된 값("yyyy년 MM월 dd일")을 사용한다.
=> required = false는 매칭되는 빈이 없다면 아무것도 하지 않는다.
=> 기본생성자의 초기화 값을 변경하지 않는다.
public class MemberPrinter{
private DateTimeFormatter dateTimeFormatter;
public MemberPrinter(){
dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");
}
@Autowired
public void setDateTimeFormatter(@Nullable DateTimeFormatter dateTimeFormatter){
this.dateTimeFormatter = dateTimeFormatter;
}
}
위의 코드를 실행하면 DateTimeFormatter는 기본생성자가 아닌 Setter에서 설정된 값을 사용한다.
=> @Nullable은 매칭되는 빈이 없다면 값을 null로 설정한다.
=> 기본생성자의 초기화 값을 설정한 후, Setter를 실행함으로써 null이 설정된다.
Optional도 마찬가지로 Optional을 할당한다.
자동 주입과 명시적 의존 주입
@Configuration
public class AppCtx{
@Bean @Qualifier("printer")
public MemberPrinter memberPrinter(){
return new MemberPrinter();
}
@Bean @Qualifier("summaryPrinter")
public MemberSummaryPrinter memberPrinter2(){
return new MemberSummaryPrinter();
}
@Bean
public MemberInfoPrinter infoPrinter(){
MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
//MemberInfoPrinter의 setPrinter메서드에는 @Autowired가 붙어있어서
//memberPrinter(=MemberPrinter)를 설정해준다.
infoPrinter.setPrinter(memberPrinter2());
//그 다음 setter로 다시 memberPrinter2(=MemberSummaryPrinter)를 설정해준다.
}
}
public class MemberInfoPrinter{
@Autowired @Qualifier("printer")
public void setPrinter(MemberPrinter memberPrinter){
this.printer = memberPrinter;
}
}
결과는 MemberPrinter의 내용으로 실행된다.
즉, setter DI로 외부에서 값을 넣어주어도
해당 setter에 @Autowired가 붙어있으면 @Autowired가 우선권을 갖는다.
우선순위 : @Autowired > Setter DI 주입
정리
• 의존 주입 방법
- 직접의존주입 : Bean을 생성할 때 setter로 의존을 주입하는 방법
- 자동의존주입 : Bean을 생성할 때 setter를 사용하지 않고,
각 클래스들의 필드/setter에 @Autowired를 붙여서 의존을 주입하는 방법
• @Autowired는 타입이 일치하는 Bean객체를 찾아 주입해준다.
타입 일치 Bean이 없으면 -> UnsatisfiedDependencyException 발생
타입 일치 Bean이 2개 이상이면 -> NoUniqueBeanDefinitionException 발생.
@Qualifier를 사용하여 각각 빈의 이름을 지정하여 해결 가능
• @Qualifier를 사용하면 설정클래스와 주입되는 클래스에 모두 적어주어야한다.
• 상위/하위 타입 관계에서 @Qualifier는 안붙이면 -> NoUniqueBeanDefinitionException 발생. @Qualifier로 해결 가능
• 자동 주입할 대상이 필수가 아니면 3가지 방법 사용
- @Autowired(required = false) : 매칭되는 빈이 없다면 값을 할당하지 않는다
- Optional : 매칭되는 빈이 없으면 Optional 할당
- @Nullable : 매칭되는 빈이 없으면 Null을 할당한다.
• 의존 주입받는 클래스의 setter에 @Autowired가 있어서 A를 주입받을 수 있는데
설정클래스에서 setter를 호출해 B를 주입해주면?
@Autowired가 setter DI보다 우선순위가 높기때문에 A로 동작하게된다.
'Backend' 카테고리의 다른 글
모던 자바 인 액션 - 람다표현식, 함수형인터페이스 (1) | 2022.07.10 |
---|---|
모던 자바 인 액션 - 람다 탄생 과정 (0) | 2022.05.06 |
Spring DI(의존주입), Container(객체 조립) (0) | 2022.03.28 |
Spring Container(ApplicationContext)와 Bean (0) | 2022.03.28 |
자바, Spring Boot로 크롤링하기 - Selenium 이용 (동적페이지), 속도 개선 방법 (0) | 2022.03.08 |