2022. 8. 31. 14:51ㆍSpring
@Autowired
자동의존주입받을 대상(필드, 세터 메소드)에 @Autowired 애노테이션을 붙이면
스프링이 타입이 일치하는 빈 객체를 찾아 주입해준다.
package spring;
import org.springframework.beans.factory.annotation.Autowired;
public class ChangePasswordService {
@Autowired //자동 주입 기능
private MemberDao memberDao;
public void changePassword(String email, String oldPwd, String newPwd) {
Member member=memberDao.selectByEmail(email);
if(member==null) //암호 변경하고자 하는 회원의 이메일이 존재하지 않으면 예외처리
throw new MemberNotFoundException();
member.changePassword(oldPwd, newPwd);
memberDao.update(member);
}
//자동 주입이 되므로 setMemberDao 메소드는 사용X
public void setMemberDao(MemberDao memberDao) { //의존 객체 주입
this.memberDao=memberDao;
}
}
위의 코드에서 memberDao 변수는 자동주입된다.
MemberDao 타입의 빈 객체가 존재한다면 해당 빈 객체가 memberDao에 주입되므로 memberDao의 세터는 사용하지 않아도 된다.
만약 대응하는 빈 객체가 존재하지 않는다면 어떻게 될까?
해당 타입의 빈 객체가 존재하지 않는다는 내용의 에러가 발생한다.
같은 타입의 빈 객체가 2개이상이면 어떨까?
마찬가지로 어떤 빈인지 한정할 수 없다는 내용의 에러가 발생한다.
@Qualifier
자동 주입 가능한 빈이 2개 이상일 때 빈을 지정할 수 있는 방법이 있다.
@Qualifier 애노테이션을 이용해 의존 객체를 선택하는 것이다.
@Bean
@Qualifier("printer")
public MemberPrinter memberPrinter1() {
return new MemberPrinter();
}
@Bean
@Qualifier("print")
public MemberPrinter memberPrinter2() {
return new MemberPrinter();
}
위와 같이 같은 타입의 빈 객체가 2개있다.
@Qualifier 애노테이션을 붙이고 그 뒤에 각각 "printer" 와 "print" 값을 한정 값으로 지정했다.
@Autowired
@Qualifier("print")
public void setPrinter(MemberPrinter printer) {
this.printer = printer;
}
@Autowired
@Qualifier("printer")
public void setMemberPrinter(MemberPrinter printer) {
this.printer=printer;
}
실제로 자동주입할 때 주입할 대상에 @Qualifier 애노테이션과 한정 값을 붙이면 해당 빈 객체가 주입된다.
(함정은 자동주입하는 모든 대상에 한정값을 붙여야 에러가 나지 않는 것 같다.)
!!!몇 가지 실험하면서 새롭게 알게 된 사실!!!
한정 값은 String 타입의 const expression 이어야 한다.
그래서 int형 같은 다른 타입은 안되고 큰 따옴표로 표현하거나 String 타입의 변수로 지정가능한데,
만일 String 변수로 지정할 경우 final을 붙여야 한다.
왜냐하면 위에서도 언급했듯이 const expression 이어야하기 때문이다.
(근데 굳이 final String으로 할 일이 있을까? 프로젝트를 해봐야 알 듯)
빈 이름과 기본 한정자
@Qualifier 애노테이션이 없으면 빈의 이름을 한정자(한정값?)로 지정한다.
@Bean
public MemberDao memberDao() {
return new MemberDao();
}
@Bean
public MemberDao memberDao1() {
return new MemberDao();
}
같은 타입의 두 빈 객체가 있다. 이 때 빈 이름은 한정자가 된다.
@Autowired
private MemberDao memberDao;
@Autowired
private MemberDao memberDao1;
필드 이름을 한정자로 사용하면 빈 객체를 한정하여 주입할 수 있다.
상속 관계에서의 자동 주입
@Bean
@Qualifier("printer")
public MemberPrinter memberPrinter1() {
return new MemberPrinter();
}
@Bean
public MemberSummaryPrinter memberPrinter2() {
return new MemberSummaryPrinter();
}
MemberSummaryPrinter 클래스가 MemberPrinter 클래스를 상속하고 있다.
어떤 대상에게 MemberPrinter 타입의 빈 객체를 자동주입하려고 하면
한정자없이는 오류가 난다.
왜냐하면 MemberPrinter 타입은 MemberSummaryPrinter 타입의 객체를 받을 수 있기 때문이다.
MemberPrinter 타입을 주입받고 싶다면 위와 같이 지정된 한정자를 사용하고,
MemberSummaryPrinter 타입을 주입받고 싶다면 MemberSummaryPrinter 타입을 명시하여
주입할 필드나 메소드(파라미터) 등을 표현해야 한다.
@Autowired 애노테이션의 필수 여부
public class MemberPrinter {
private DateTimeFormatter dateTimeFormatter;
public void print(Member member) {
if(dateTimeFormatter==null)
System.out.printf("회원 정보: 아이디=%d, 이메일=%s, 이름=%s, 등록일=%tF\n",
member.getId(), member.getEmail(), member.getName(), member.getRegisterDateTime());
else
System.out.printf("회원 정보: 아이디=%d, 이메일=%s, 이름=%s, 등록일=%s\n",
member.getId(), member.getEmail(), member.getName(),
dateTimeFormatter.format(member.getRegisterDateTime()));
}
@Autowired
public void setDateFormatter(DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter=dateTimeFormatter;
}
}
위 코드에서 dateTitmeFormatter는 반드시 필요한 필드는 아니다.
null이어도 해당 클래스의 기능이 잘 구현되기 때문이다.
하지만 이대로 실행하면 DateTimeFormatter 빈 객체가 없을 경우 에러가 발생한다.
이런 상황을 해결하는 방법에는 3가지가 있다.
첫 번째는 required 속성을 활용하는 것이다.
@Autowired(required=false)
public void setDateFormatter(DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter=dateTimeFormatter;
}
@Autowired 애노테이션 옆에 required 속성을 false로 지정하면
주입할 타입의 빈 객체가 없을 경우 해당 메소드를 실행하지 않는다.
두 번째는 자바8의 Optional을 사용하는 것이다.(spring 5부터 사용가능)
@Autowired
//해당 타입의 빈 객체가 존재하면 해당 빈을 값으로 갖는 Optional을 인자로 전달
//존재하지 않으면 값이 없는 Optional을 인자로 전달
public void setDateFormatter2(Optional<DateTimeFormatter> formatterOpt) {
if(formatterOpt.isPresent())
this.dateTimeFormatter=formatterOpt.get();
else
this.dateTimeFormatter=null;
}
주입할 타입의 빈 객체가 존재하면 해당 빈을 값으로 갖는 Optional을 인자로 전달하고,
존재하지 않으면 값이 없는 Optional을 인자로 전달한다.
세 번째는 @Nullable 애노테이션을 사용하는 것이다.
@Autowired
public void setDateFormatter3(@Nullable DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter=dateTimeFormatter;
}
자동 주입할 빈이 존재하면 해당 빈을 인자로 전달하고, 존재하지 않으면 null을 인자로 전달한다.
여기서 required 와 @Nullable 의 차이점은 주입할 빈이 존재하지 않을 경우
required는 메소드가 호출되고, @Nullable은 메소드가 호출되지 않는다는 점이다.
그리고 다음 코드를 보자.
public class MemberPrinter {
private DateTimeFormatter dateTimeFormatter;
public MemberPrinter() {
dateTimeFormatter=DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");
}
public void print(Member member) {
if(dateTimeFormatter==null)
System.out.printf("회원 정보: 아이디=%d, 이메일=%s, 이름=%s, 등록일=%tF\n",
member.getId(), member.getEmail(), member.getName(), member.getRegisterDateTime());
else
System.out.printf("회원 정보: 아이디=%d, 이메일=%s, 이름=%s, 등록일=%s\n",
member.getId(), member.getEmail(), member.getName(),
dateTimeFormatter.format(member.getRegisterDateTime()));
}
@Autowired(required=false)
public void setDateFormatter(DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter=dateTimeFormatter;
}
}
생성자에서 dateTimeFormatter를 초기화한다.
required 로 자동주입할 경우 일치하는 빈이 존재하지 않을 때 주입 대상인 필드나 메소드에 null을 전달하지 않는다.
위 코드의 실행결과는 초기화 형식대로 출력된다.
하지만 @Nullable 애노테이션을 사용하면 null값을 전달하기 때문에 초기화하지 않은 형식으로 출력된다.
명시적 의존 주입
@Bean
public MemberInfoPrinter infoPrinter() {
MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
infoPrinter.setPrinter(memberPrinter2());
return infoPrinter;
}
위의 빈 객체는 명시적으로 memberPrinter1이 아닌 memberPrinter2를 주입하고 있다.
하지만 주입받는 대상이 @Autowired 를 통해 memberPrinter1을 자동 주입받고 있다면
결국은 memberPrinter1이 자동 주입된다.
따라서 @Autowired 애노테이션을 사용했다면 설정 클래스에서 객체를 주입하기보다는
일관되게 자동주입기능을 사용하는 것이 편리하다.
'Spring' 카테고리의 다른 글
| [Spring] AOP 프로그래밍 (0) | 2022.09.05 |
|---|---|
| [Spring] 빈 라이프사이클과 범위 (0) | 2022.09.04 |
| [Spring] 컴포넌트 스캔 (0) | 2022.09.04 |