동기

최근에 회사에서 작업을 하면서, 인프라팀에서 제공해주는 라이브러리를 사용하는데 두 라이브러리가 충돌하는 경우가 있었다. 라이브러리에서 제공하는 빈의 클래스명이 동일한 경우였는데, 이런 경우 @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 : 동일한 타입이 있는 경우 메타정보를 가지고 빈을 찾을 수 있다. 메타정보가 없다면 동일한 이름의 빈이 있는지 확인한다.(이름 검색은 권장되는 방식은 아님)