@Resource 어노테이션과 @Autowired 어노테이션의 차이
두 어노테이션 모두 의존성을 주입하는 어노테이션이다.
해당 어노테이션을 사용하여 IoC 컨테이너에 등록된 빈 객체를 자동으로 주입한다.
@Autowired 어노테이션에 대한 자세한 설명은 검색하면 많은 정보가 나오므로 생략하도록 한다.
@Resource 어노테이션과 어떤 차이가 존재하는지를 중점적으로 살펴본다.
편의상 해당 글에서 모든 의존성 주입 방법 예시는 필드 주입으로 한다.
@Autowired
- 필드, 메서드, 생성자에 사용할 수 있다.
- Bean의 타입을 사용해서 주입할 빈 객체를 찾는다.
- 하나의 인터페이스는 한 개의 클래스로만 구현할 수 있다. (두 개 이상의 클래스가 구현하면 안 된다.)
마지막 특징 때문에 @Resource 어노테이션을 사용한다. (@Qualifier 또는 @Primary 어노테이션을 사용해도 된다.)
A 인터페이스가 존재하고, A 인터페이스를 구현하는 클래스 B, C가 존재한다고 가정한다.
public interface A {}
@Component
public class B implements A {}
@Component
public class C implements A {}
위와 같은 경우 A를 구현하는 클래스 B, C 모두가 @Component 어노테이션을 사용해 스프링 빈 객체로 등록되어 있는 상태이다. 이후 다른 Service 클래스에서 의존성 주입을 받는다고 가정한다.
@Service
public class HelloService {
@Autowired
private A a;
}
이러한 경우 주입받는 구현체가 B 클래스인지, C 클래스인지 알 수 없다.
@Autowired는 Bean의 타입-해당 예시에서는 A 타입-을 이용해서 주입할 빈 객체를 찾는다고 하였다. 빈 객체로 등록된 클래스 B와 C 모두 A 인터페이스를 구현하고 있으니 어떤 클래스를 사용해야 할지 알 수 없는 것이다.
인텔리제이 사용 시 다음과 같은 오류 문구가 나타난다.
Colud not autowire. There is more than one bean of 'A' type.
Beans : b (B.java)
: c (C.java)
@Autowired 어노테이션 사용 시 빈 객체를 찾는 순서는 다음과 같다.
1. 타입이 같은 빈 객체를 찾는다.
2. 만약 타입이 같은 빈 객체가 두 개 이상이고, @Qualifier 어노테이션이 붙어있지 않은 경우 이름이 같은 빈 객체를 찾는다.
3. @Qualifier(또는 @Primary) 어노테이션이 붙어있다면 해당 어노테이션으로 지정된 빈 객체를 찾는다.
이름이 같은 빈 객체의 예시를 잠깐 살펴본다.
@Service
public class HelloService {
@Autowired
private A b;
}
위 HelloService 코드와 동일한 것처럼 보이지만, A a에서 A b로 변경되었다.
이러한 경우 B 클래스로 구현된 빈 객체를 찾아 주입한다.
@Resource
- 필드, 메서드에 사용할 수 있다. 생성자에는 사용할 수 없다.
- Bean의 이름을 사용해서 주입할 빈 객체를 찾는다.
보통 @Resource 어노테이션 사용 시 다음과 같은 형태로 사용한다.
@Resource(name = "memberRepository")
private MemberRepository memberRepository;
이처럼 name 값을 지정하여 사용하기 때문에 한 개의 인터페이스를 여러 개의 클래스가 구현해도 상관없다.
@Resource 어노테이션 사용 시 빈 객체를 찾는 순서는 다음과 같다.
1. name 속성 값으로 지정한 빈 객체를 찾는다.
2. name 속성이 존재하지 않는다면 동일한 타입을 갖는 빈 객체를 찾는다.
3. name 속성이 존재하지 않고, 동일한 타입을 갖는 빈 객체가 두 개 이상인 경우-앞서 A, B, C로 예시를 든 경우- 이름이 같은 빈 객체를 찾는다.
4. name 속성이 존재하지 않고, 동일한 타입을 갖는 빈 객체가 두 개 이상이고, 이름이 같은 빈 객체마저도 없는 경우에는 @Qualifier 어노테이션(또는 @Primary)을 사용해서 주입할 빈 객체를 찾아야 한다.
@Primary, @Qualifier
@Primary 어노테이션과 @Qualifier 어노테이션을 사용하여 @Autowired 어노테이션 사용 시, 한 개의 인터페이스를 여러 개의 클래스가 구현 가능하게 할 수 있다.
두 어노테이션도 검색하면 설명이 잘 되어있으니 자세한 설명은 생략한다.
@Primary
public interface A {}
@Component
public class B implements A {}
@Component @Primary
public class C implements A {}
같은 타입의 Bean 객체가 여러 개 존재할 때 @Primary 어노테이션이 붙은 클래스가 있다면 해당 빈 객체를 주입한다.
이후 Service 클래스에서 A를 의존성 주입받을 때, @Primary가 붙어있는 C 클래스로 구현된 빈 객체가 주입될 것이다.
@Qualifier
@Component
@Qulifier("b")
public class B implements A {}
@Component
@Qulifier("c")
public class C implements A {}
@Service
public class HelloService {
@Autowired @Qulifier("b")
private A a;
}
qualifier value가 b로 설정된 클래스는 B 클래스임을 뜻한다. 따라서 위 예시에서는 필드 이름을 a로 하더라도 B 클래스로 구현된 빈 객체가 주입될 것이다.