프로그래밍 공부/Spring

[Spring] 의존 자동 주입, @AutoWired

@Autowired 어노테이션을 이용한 의존 자동 주입

대체로 스프링에서 자동주입이라고 말하면 @Autowired 어노테이션이 붙어있는 경우를 말한다.

 

@Autowired 를 해당 필드나 메서드에 붙이게 되면 스프링은 타입이 일치하는 빈 객체를 찾아서 주입하게 된다.

만약 생성자 주입을 사용한다면, 생성자가 하나일 경우 @AutoWired 를 생략해도 자동 주입된다.

 

public class MemberInfoPrinter {

	private MemberDao memDao;
	private MemberPrinter printer;

	public void printMemberInfo(String email) {
		Member member = memDao.selectByEmail(email);
		if (member == null) {
			System.out.println("데이터 없음\\n");
			return;
		}

		printer.print(member);
		System.out.println();
	}

	@Autowired // 의존 자동 주입
	public void setMemberDao(MemberDao memberDao) {
		this.memDao = memberDao;
	}

	@Autowired // 의존 자동 주입
	public void setPrinter(MemberPrinter printer) {
		this.printer = printer;
}

 

 

일치하는 빈이 없는 경우

그렇다면, @Autowired 애노테이션을 적용한 대상에 일치하는 빈이 없으면 어떻게 될까?

→ 어찌보면 당연하게도 에러가 발생한다.

 

반대로 @Autowired 애노테이션을 적용한 대상에 일치하는 빈이 두개 이상이면?

→ 두개의 빈을 발견했다는 에러가 발생한다.

 

해결책

  • @Autowired 필드 명 매칭
    • 타입 매칭 시도 → 그 결과에 여러 빈이 있을 때 파라미터 명으로 빈 매칭
  • @Qualifier → @Qualifier끼리 매칭 → 빈 이름 매칭
  • @Primary 사용 → 우선권 제공
@Autowired
private DiscountPolicy discountPolicy -> rateDiscountPolicy // 필드명 변경

@Bean
@Qualifier("summaryPrinter") // 추가 구분자 생성. 이름을 변경하는 것은 아니다!
public MemberSummaryPrinter memberPrinter2() {
	return new MemberSummaryPrinter();
}

@Component
@Primary // 해당 빈이 가장 우선권을 가진다.
public class RateDiscountPolicy implements DiscountPolicy {
}

위의 내용들끼리 겹칠 경우, 스프링의 특징을 생각해보아야 한다.

 

스프링은 자동보다는 수동이, 넒은 범위의 선택권 보다는 좁은 범위의 선택권이 우선 순위가 높다.

따라서 여기서도 @Qualifier 가 우선권이 높다.

 

 

@Autowired 어노테이션의 필수 여부

  • 자동 주입할 대상이 필수가 아닌 경우 = required 속성을 false로 지정
  • 스프링 5 버전부터는 Optional을 사용하는 경우가 더 많다.
  • required 속성을 지정하는 경우에는 자동 주입할 빈이 없을때 아예 호출하지 않지만, @Nullable 의 경우에는 자동 중비할 빈이 존재하지 않아도 메서드로 호출된다.
// 매칭되는 빈이 없는 경우에는?

//호출 안됨
@Autowired(required = false)
public void setNoBean1(Member member) {
    System.out.println("setNoBean1 = " + member);
}

//null 호출
@Autowired
public void setNoBean2(@Nullable Member member) {
    System.out.println("setNoBean2 = " + member);
}

//Optional.empty 호출
@Autowired(required = false)
public void setNoBean3(Optional<Member> member) {
    System.out.println("setNoBean3 = " + member);
}

 세 메서드의 결과

  • setNoBean1() 은 @Autowired(required=false) 이므로 호출 자체가 안된다.
  • setNoBean2 = null → 주입할 빈이 없어도 메서드는 호출된다.
  • setNoBean3 = Optional.empty

 

자동주입과 수동주입

스프링은 @Component 뿐만 아니라 @Controller , @Service , @Repository 처럼 계층에 맞추어 일반적인 애플리케이션 로직을 자동으로 스캔할 수 있도록 지원한다. 거기에 더해서 최근 스프링 부트는 컴포넌트 스캔을 기본으로 사용하고, 스프링 부트의 다양한 스프링 빈들도 조건이 맞으면 자동으로 등록하도록 설계했다.

 

또한, 수동으로 빈을 등록하고 관리하는 과정은 상당히 번거로우며, 관리할 빈이 많아지면 부담이 될 수 있다. 그리고 결정적으로 자동 빈 등록을 사용해도 OCP, DIP를 지킬 수 있다.

 

따라서, 데이터베이스 연결이나, 공통 로그 처리 처럼 업무 로직을 지원하기 위한 하부 기술이나 공통 기술과 같이 애플리케이션에 광범위하게 영향을 미치는 기술 지원 객체 정도만 수동 빈으로 등록해서 설정 정보에 바로 나타나게 하는 것이 유지보수 하기 좋다.

 

정리

  • 편리한 자동 기능을 기본으로 사용하자
  • 직접 등록하는 기술 지원 객체는 수동 등록
  • 다형성을 적극 활용하는 비즈니스 로직은 수동 등록을 고민해보자

 

참고자료

https://velog.io/@lychee/스프링-핵심-원리-07.-의존관계-자동-주입

스프링5 프로그래밍 입문 챕터 4