동기
최근에 회사에서 작업을 하면서, 인프라팀에서 제공해주는 라이브러리를 사용하는데 두 라이브러리가 충돌하는 경우가 있었다. 라이브러리에서 제공하는 빈의 클래스명이 동일한 경우였는데, 이런 경우 @Autowired를 하면 해당 빈을 찾지를 못한다.
예전에도 비슷한 경험을 했었는데, 그 때는
- 프로젝트가 너무 복잡했고
- 빈을 DI하는 어노테이션에 대해 잘 몰라서
그 문제를 해결할수가 없었다. 그래서 빈을 DI하는 어노테이션에 대해 한번 정리할 필요성을 느끼게 되었다. 느꼈으면 해야지!
스프링 빈 DI
스프링에서는 빈을 주입할 수 있다. 빈을 DI하는 것은 xml 설정파일로도 가능하고, 어노테이션을 이용할 수도 있다. 최근에는 주로 어노테이션을 사용하는 경우가 많다.
종류
어노테이션을 통해서 빈을 주입할 수 있는 방법은 총 두 가지가 있다.
- @Resource
- @Autowired
@Resource
아이디
주입할 빈을 아이디로 지정하는 방법이다.
public class Hello {
private Printer printer;
@Resource(name="printer")
public void setPrinter(Printer printer) {
this.printer = pprinter;
}
}setter를 이용하여 클래스에 프린터를 DI하는 코드이다.
필드
@Resource를 필드에 붙일 수 있다. 필드에 붙이면, 필드를 보고 프로퍼티를 추가해준다.
@Componet
public class Hello {
@Resource(name="printer")
private Printer printer;
}- setter가 없이도 DI 가능
- 빈의 이름(위에서는 name=“printer”)를 생략할 수 있음
이름을 생략하는 경우에는 빈의 이름이 필드 이름과 같다고 생각하게 된다(위의 경우는 name=“printer”를 한 것과 마찬가지이다)
@Anotation
타입에 의한 DI이다. @Resource와의 차이점은 리소스는 이름을 가지고 빈을 찾지만, autowired는 타입을 가지고 빈을 찾는다.
필드
public class Hello {
@Autowired
private Printer printer;
}위와 같이 쓰면, 필드의 타입을 통해 빈을 찾는다. 위의 코드로 보면 타입이 Printer인 빈을 찾게 되는 것이다.
setter
setter 메소드에도 사용할 수 있다.
public class Hello {
private Printer printer;
@Autowired
public void setPrinter(Printer printer) {
this.printer = printer;
}
}@Autowired는 타입을 가지고 빈을 찾는다는 걸 기억하자. 위와 같은 코드를 짜면 Printer 타입인 빈을 찾아서 setter 메소드에 DI하게 된다.
심지어 @Autowired는 생성자에 쓸 수도 있다.
@Autowired
public void config(SqlReader sqlReader, SeqlRegistry sqlRegistry) {
this.sqlReader = sqlreader;
this.sqlRegistry = sqlRegistry;
}다만 @Resource나 @Autowired나 모두 생성자/setter에 사용하는 문법은 잘 사용하지 않는 거 같다. 보통 필드에 적용한다.
중복된 타입의 빈이 있는 경우
@Autowired는 타입을 가지고 빈을 DI 하므로, 타입이 중복되면 곤란해진다. 이를 해결하는 여러 가지 방법이 있다
컬렉션을 사용
@Autowired
Printer[] printers;이렇게 하면 Printer타입인 객체들은 모두 DI 된다.
@Qualifier
타입 외의 정보를 추가하여 DI를 할 수 있다. 만약에 타입이 같은 경우가 발생한다면? 만약에 DataSource가 여러 개라면
@Autowired
DataSource dataSource이와 같은 코드는 에러가 발생하게 된다. 이걸 방지하기 위해 @Qualifier가 있다. @Qualifier는 이름과 별개로 빈에 추가적인 메타정보를 지정하여, 해당 빈을 DI 할 수 있는 애노테이션이다.
@Autowired
@Qualifier("mainDB")
DataSource dataSource이와 같이 하고 xml에 Qualifier정보를 추가한다.
<bean id="oracleDataSource" class="...">
<qualifier value="mainDB" />
</bean>이와 같이 하면 된다. 만약에 애노테이션을 통해 빈을 등록했다면
@Componet
@Qualifier("mainDB")
public class OracleDataSource {
}이와 같이 애노테이션을 사용하여 퀄리파이어를 지정할 수 있다.
정리
@Resource : 이름을 가지고 빈을 찾는다. setter, 필드에 사용가능
@Autowired : 타입을 가지고 빈을 찾는다. setter, 필드, 생성자에 사용 가능
@Qualifier : 동일한 타입이 있는 경우 메타정보를 가지고 빈을 찾을 수 있다. 메타정보가 없다면 동일한 이름의 빈이 있는지 확인한다.(이름 검색은 권장되는 방식은 아님)