티스토리 뷰

맴버 필드에 @Autowired

public class MemberRegisterService
{
    @Autowired
    private MemberDao memberDao;

 

이렇게 클래스의 맴버 필드에 @Autowired 애노테이션을 붙여주면 해당 객체가 스프링에 의해 자동 주입된다. 

스프링 컨테이너의 설정 클래스가 아래와 같다고 해보자

@Configuration
public class AppCtx
{
    @Bean
    public MemberDao memberDao()
    {
        return new MemberDao();
    }

    @Bean
    public MemberRegisterService memberRegSvc()
    {
        return new MemberRegisterService();
    }

 

스프링은 설정 파일을 읽다가 MemberRegisterService 객체를 등록해야 한다는 것을 알게되고  MemberRegisterService 클래스를 찾아갔더니 MemberDao 객체가 @Autowired 표시되어 있으므로 등록된 MemberDao 빈 객체를 찾아 주입한다.

 

 

메소드에 @Autowired

public class MemberInfoPrinter
{

    private MemberDao memberDao;
    private MemberPrinter printer;    

    @Autowired
    public void setMemberDao(MemberDao memberDao)
    {
        this.memberDao = memberDao;
    }
    @Autowired
    public void setPrinter(MemberPrinter printer)
    {
        this.printer = printer;
    }
}

메소드에 @Autowired를 붙였을때도 마찬가지로, 스프링 설정 파일을 읽다가 MemberInfoPrinter 객체를 등록해야 한다는 것을 알게되고, 해당 클래스로 가보니 두개의 세터 메소드가 @Autowired 되어 있으므로 호출해서 해당 객체를 주입한다.

 

 

자동 주입 실패

@Autowired 애노테이션을 적용한 대상에 일치하는 빈 객체가 없으면 익셉션이 발생한다.

 

또한 @Autowired 애노테이션을 적용한 대상에 일치하는 빈 객체가 2개 이상이어도 자동 주입하려는 빈이 어떤 빈인지 정확히 알 수 없어 익셉션이 발생한다. 

 

 

@Qualifer 애노테이션을 이용한 의존 객체 선택 

@Autowired 애노테이션을 적용한 대상에 일치하는 빈 객체가 2개 이상이라면, 자동 주입하려는 빈이 어떤 빈인지 정확히 알 수 없어 익셉션이 발생한다고 하였다.

 

따라서 자동 주입할 빈을 정확히 정해줄수 있는 방법이 필요한데, @Qualifer 애노테이션을 이용한다. 

 

 

우선 @Qualifer 애노테이션을 빈 설정 클래스의 @Bean 애노테이션에 사용한다.

@Bean
@Qualifier("printer")
public MemberPrinter memberPrinter1()
{
    return new MemberPrinter();
}

@Bean
public MemberPrinter memberPrinter2()
{
    return new MemberPrinter();
}

 

그리고 클래스에서 @Autowired 애노테이션을 붙여 자동주입 하도록 한 메소드에 붙인다.

@Autowired
@Qualifier("printer")
public void setMemberPrinter(MemberPrinter printer)
{
    this.printer = printer;
}

 

이렇게 하면 @Autowired 에 의해 setMemberPrinter 메소드가 실행되면 같은 한정자 "printer"를 갖는 빈 객체인 memberPrinter1 의 빈 객체가 자동 주입 대상으로 사용된다. 

 

 

 

빈의 한정자

기본적으로 스프링은 빈의 이름을 한정자로 지정한다.

@Bean
public MemberPrinter memberPrinter2()
{
    return new MemberPrinter();
}

위의 memberPrinter2 빈의 한정자는 memberPrinter2 이다. 

 

하지만 @Qualifer 애노테이션이 있다면 @Qualifer로 지정한 이름이 한정자가 된다.

@Bean
@Qualifier("printer")
public MemberPrinter memberPrinter1()
{
    return new MemberPrinter();
}

위의 빈의 한정자는 printer 이다. 

 

 

상속관계와 자동 주입

스프링 컨테이너가 특정 타입 빈을 자동주입해야 하는 @Autowired 애노테이션을 만났을때, 자동 주입해야 하는 타입의 빈이 하나 있고, 해당 타입을 상속받는 또 다른 타입이 있다면 스프링은 어느 빈을 주입해야 할지 알 수 없다. 

 

@Bean
public MemberPrinter memberPrinter1()
{
    return new MemberPrinter();
}

@Bean
public MemberSummaryPrinter memberPrinter2()
{
    return new MemberSummaryPrinter();
}

예를들어 위와 같이 두 개의 빈이 있고, MemberSummaryPrinter가 MemberPrinter를 상속받는 관계라고 해보자.

스프링 컨테이너가 MemberPrinter를 자동 주입해야 하는 상황이 왔을때 두 가지 타입을 보게 된다.

 

1. MemberPrinter 타입의 빈인 memberPrinter1

2. MemberPrinter 를 상속 받는 MemberSummaryPrinter 타입의 memberPrinter2 

 

따라서 스프링은 둘 중 어떤 빈을 주입해야할지 알 수 없게 된다.

 

 

해결 방법은 두가지가 있다.

 

1. @Qualifer 애노테이션으로 어떤 빈을 주입할지 정해준다.

@Autowired
@Qualifier("printer")
public void setPrinter(MemberPrinter printer)
{
    this.printer = printer;
}

이렇게 해주면 스프링이 자동주입할때 printer 한정자를 갖는 빈을 주입할것이다.

 

2. @Autowired 애노테이션이 있는 세터 메소드가 자식 메소드를 사용해야 한다면 정확히 자식 메소드를 인자로 받도록 해준다.

@Autowired
public void setMemberPrinter(MemberSummaryPrinter printer)
{
    this.printer = printer;
}

여기서는 인자의 타입이 MemberSummaryPrinter이므로 해당 타입의 인자를 받을 것이다. 

 

 

자동주입이 필수가 아닐 경우 (@Autowired(required=false), @Nullable)

public class MemberPrinter
{
    private DateTimeFormatter dateTimeFormatter;

    public void print(Member member)
    {
        // DateTimeFormatter 객체가 없어도 printer 메소드는 정상작동한다
        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 는 자동주입에 필요한 빈 객체가 없으면 익셉션을 발생시킴 
    @Autowired
    public void setDateTimeFormatter(DateTimeFormatter dateTimeFormatter)
    {
        this.dateTimeFormatter = dateTimeFormatter;
    }
}

위 클래스의 print() 함수는 DateTimeFormatter 객체가 없어도 if else 문에 의해 정상적으로 작동된다.

즉 해당 함수를 실행하는데 DateTimeFormatter 객체가 필수는 아니라는 것이다.

 

하지만 아래 세터 메소드에 @Autowired 애노테이션이 있으므로 스프링 컨테이너는 DateTimeFormatter 빈 객체가 등록되있지 않으면 익셉션을 발생 시킨다.

 

이렇게 자동 주입이 필수가 아닐때 즉, 등록된 빈이 없는 경우 자동 주입을 수행하지 않아도 될 경우 @Autowired 애노테이션에 required 속성을 false로 바꿔준다.

 

@Autowired(required = false)
public void setDateTimeFormatter(DateTimeFormatter dateTimeFormatter)
{
    this.dateTimeFormatter = dateTimeFormatter;
}

 

이제 스프링은 DateTimeFormatter 객체가 빈에 등록되어 있지 않으면 자동주입하지 않고 지나갈 것이다.

(setDateTimeFormatter 메소드를 실행시키지 않음

 

 

또 다른 방법은 주입하는 대상 파라미터 앞에 @Nullable 애노테이션을 붙여도 된다.

@Autowired
public void setDateTimeFormatter(@Nullable DateTimeFormatter dateTimeFormatter)
{
    this.dateTimeFormatter = dateTimeFormatter;
}

@Nullable 이 붙은 객체가 빈에 존재하면 해당 빈을 인자로 전달하고, 존재하지 않으면 NULL을 전달한다. 

@Autowired(required=false)와 차이점은 setDateTimeFormatter 메소드가 호출된다. 

 

이 방법들은 필드에 @Autowired 애노테이션을 적용했을때도 똑같이 적용된다.

 

 

자동 주입과 명시적 의존 주입의 충돌

어떤 빈 객체가 자동 주입과 명시적 의존 주입이 같이 이뤄진다면 자동 주입이 우선시 된다.

다음 설정 클래스를 보자.

@Bean
@Qualifier("printer")
public MemberPrinter memberPrinter1()
{
    return new MemberPrinter();
}

@Bean
@Qualifier("summaryPrinter")
public MemberSummaryPrinter memberPrinter2()
{
    return new MemberSummaryPrinter();
}

@Bean
public MemberInfoPrinter infoPrinter()
{
    MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
    infoPrinter.setPrinter(memberPrinter2());
    return infoPrinter;
}

MemberInfoPrinter infoPrinter에 세터를 이용해 명시적으로 memberPrinter2 빈을 주입하고 있다.

그런데 세터가 다음과 같이 자동 주입이 이뤄지도록 되어 있다.

@Autowired
@Qualifier("printer")
public void setPrinter(MemberPrinter printer)
{
    this.printer = printer;
}

printer 한정자를 갖는 MemberPrinter 형 빈을 자동 주입하도록 되어 있다.

 

이렇게 자동 주입과 명시적 주입이 충돌할때는 자동 주입을 통해 일치하는 빈이 주입된다.

즉 위의 경우에는 memberPrinter2 빈이 아닌, 한정자를 printer로 갖는 memberPrinter1 빈이 주입된다. 

 

이런 충돌을 막기위해 @Autowired 애노테이션을 사용했다면 설정 클래스에서 객체를 명시적으로 주입하기 보다는 스프링이 제공하는 자동 주입 기능을 사용하는것이 좋다.

 

 

출처 : 스프링5 프로그래밍 입문 (최범균 저) 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/02   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
글 보관함