Chapter 4. 의존 자동 주입

Posted by yunki kim on May 2, 2022

  스프링이 자동으로 의존하는 빈 객체를 주입해주는 기능을 자동 주입이라 한다. 자동 주입을 위해서는 @Autowired 나 @Resource 어노테이션을 사용하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ChangePasswordService {
 
    // 의존성 자동 주입
    // 필드에 @Autowired 사용
    @Autowired
    private MemberDao memberDao;
 
    ...
}
 
// 또는
public class ChangePasswordService {
 
    private MemberDao memberDao;
    
    // setter 메서드에도 사용할 수 있다.    
    // setter에 @Autowired를 사용하면 스프링은 타입이 일치하는 
    // 빈 객체를 찾아 주입한다.
    @Autowired
    public void setMemberDao(MemberDao memberDao) {
        this.memberDao = memberDao;
    }
}
cs

  만약 의존 자동 주입이 필요한 빈이 존재하지 않다면, 다음과 같은 에러가 발생한다.

  만약 의존 자동 주입 대상에 일치하는 빈이 두 개 이상이면, 다음과 같은 에러가 발생한다.

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

  @Qualifier 어노테이션을 사용하면 자동 주입 대상 빈을 한정할 수 있다. 따라서 의존 자동 주입 대상에 일치하는 빈이 두 개 이상일 때, 빈을 특정지을 수 있다.

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
@Configuration
public class Config {
 
    @Bean
    // @Qualifier 로 빈을 특정 짓는다.
    @Qualifier("bean")
    public setter.Bean getBean1() {
        return new setter.Bean();
    }
 
    @Bean
    public setter.Bean getBean2() {
        return new setter.Bean();
    }
}
 
@Component
public class A {
 
    private Bean bean;
 
    @Autowired
    // 특정지은 빈을 setter에 명시한다.
    @Qualifier("bean")
    public void setBean(Bean bean) {
        this.bean = bean;
    }
}
cs

빈 이름과 기본 한정자

  빈 설정에 @Qualifier 어노테이션이 없으면 빈의 이름을 한정자로 지정한다. @Autowired 어노테이션도 @Qualifier 가 없으면 필드나 파라미터 이름을 한정자로 사용한다.

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
@Configuration
public class Config {
 
    @Bean
    public setter.Bean getBean1() {
        return new setter.Bean();
    }
 
    @Bean
    public setter.Bean getBean2() {
        return new setter.Bean();
    }
}
 
@Component
public class A {
 
    @Autowired
    // Config 클래스의 getBean1을 한정자로 사용
    private Bean getBean1;
 
    public void setGetBean1(Bean bean) {
        this.getBean1 = bean;
    }
}
 
cs

 

상위/하위 타입 관계와 자동 주입

  SubBean이 Bean을 extends하고 있다 해보자.

1
2
3
4
5
6
public class Bean {
}
 
 
public class SubBean extends Bean{
}
cs

그 후 다음과 같은 코드를 실행하면 자동 의존 주입 대상과 일치하는 빈이 두 개라는 에러가 발생한다.

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
@Configuration
public class Config {
 
    @Bean
    public setter.Bean getBean1() {
        return new setter.Bean();
    }
 
    @Bean
    public setter.SubBean getBean2() {
        return new setter.SubBean();
    }
}
 
@Component
public class A {
 
    @Autowired
    private Bean bean;
 
    public void setGetBean1(Bean bean) {
        this.bean = bean;
    }
}
 
cs

실행 결과

  이 에러가 발생하는 이유는 SubBean이 Bean을 extends 하고 있기 때문에 Bean에 Bean과 SubBean 두 타입의 빈을 주입할 수 있기 떄문이다. 이 문제 역시 위에서 언급한 @Qualifier 어노테이션을 사용해 해결할 수 있다. 또는 A 클래스 내에 존재하는 Bean을 SubBean으로 변경하면 된다.

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

  @Autowired 어노테이션을 붙인 타입에 해당하는 빈이 없으면 exception이 발생한다. 만약 빈이 존재하지 않는 경우 어떤 로직을 수행해야 한다면, required 속성을 false로 두면 된다. 그러면 매칭되는 빈이 없어도 exception이 발생하지 않는다.

1
2
3
4
5
6
7
8
9
10
11
@Component
public class A {
 
    @Autowired(required = false)
    private Bean bean;
 
    public void setGetBean1(Bean bean) {
        this.bean = bean;
    }
}
 
cs

 또는 @Autowired 를 그래도 두고, Optional을 사용해도 된다.

1
2
3
4
5
6
7
8
9
10
11
@Component
public class A {
 
    private Bean bean;
 
    @Autowired
    public void setGetBean1(Optional<Bean> bean) {
        ///
    }
}
 
cs

  또는 @Nullable 어노테이션을 사용하면 된다. @Nullable은 스프링이 제공하는 어노테이션으로, 자동 주입할 빈이 존재하지 않으면 null을 주입한다.

1
2
3
4
5
6
7
8
9
10
11
@Component
public class A {
 
    @Autowired
    @Nullable
    private Bean bean;
 
    public void setGetBean1(Bean bean) {
        this.bean = bean;
    }
}
cs

  @Autowired(required=false)와 @Nullable을 setter에 사용했을 때의 차이는 @Autowired(required=false)를 사용하고 대상 빈이 존재하지 않으면 해당 메서드를 호출하지 않는다는 것이다. 그에 반해 필드 주입에서의 @Autowired(required=false)와 @Nullable은 둘 다 대상 빈이 존재하지 않으면 null을 주입한다.

자동 주입과 명시적 의존 주입 간의 관계

  설정클래스에서 행해지는 의존 주입과 자동 주입이 동시에 진행된다면 @Autowired를 통한 자동 주입이 우선순위를 갖는다. 따라서 @Autowired 어노테이션을 사용한다면 설정 클래스에성 객체를 주입하지 말고, 스프링이 제공하는 자동 주입 기능을 사용하는 것이 좋다.

 

@Autowired, @Resource, @Inject

  @Autowired, @Resource, @Inject 어노테이션들은 모두 의존성 주입에 사용되는 어노테이션들이다. @Autowired는 org.springframework,beans.factory.annotation에 존재한다. 그에 반해 @Resouce는 javax.annotation.Resource에, @Inject는 javax.inject.Inject에 존재하며 자바에서 지원하는 어노테이션들이다.

  이 어노테이션들은 의존 객체를 찾을 떄 찾는 순서가 다르다. 또 한, @Autowired와 @Inject 어노테이션은 멤버 변수, setter, 생성자, 일반 메서드에 모두 적용 가능하지만 @Resource는 오직 멤버 변수와 setter 메서드에만 적용할 수 있다.

@Resource

  이름 -> 타입 -> @Qualifier -> 실패

@Autowired

  타입 -> 이름 -> @Qualifier -> 실패

@Inject

  타입 -> @Qualifier -> 이름 -> 실패

Bean 강제 지정

  위의 3가지 어노테이션 모두 @Qualifier 어노테이션을 통해 자동 주입 대상 빈을 지정할 수 있다. 또는 javax.inject에 존재하는 @Named 어노테이션을 사용해도 된다. @Resource 어노테이션의 경우 name 값을 주어 자동 주입 대상 빈을 지정할 수도 있다.

Bean이 없을 경우 처리

  Bean이 존재하지 않는다면 @Resource, @Inject는 애러를 발생시킨다. @Autowired 역시 디폴트로는 에러를 발생시키지만 required를 false로 주어서 빈이 없어도 에러를 발생시키지 않을 수 있다는 점이 다르다.

 

출처 - 초보 웹 개발자를 위한 스프링 5 프로그래밍 입문