대략적으론 알지만 자세히는 몰랐던 내용 중 하나로 항상 검색하면 나오는 블로그 게시글을 거의 그대로 퍼 온 내용을 기반으로 한다. 업무하면서 대부분 객체 주입을 위해 @Autowired를 무감각하게 사용하다보니 그렇겠거니 했었는데 이번 기회에 개념과 동작 방식, 예제로 정리를 해보려고 한다.
구분
Class
Package
Wiring Strategy
@Autowired
org.springframework.beans.factory.annotation
타입 우선 - Spring 전용
@Inject
javax.inject
타입 우선 - 자바 범용
@Resource
javax.annotation
이름 우선
@Autowired, @Inject
타입 기반으로 객체를 주입한다. 주어진 타입이 빈으로 등록되어 있다면 자동으로 연결지어 준다.
하지만 다음 상황에 대해 이해하고 주입을 사용해야 올바르게 기능을 활용할 수 있을 것이다.
public class Shape { ... }
public class Triangle extends Shape { ... }
public class Rectangle extends Shape { ... }
public class Square extends Rectangle { ... }
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/context/application-context.xml")
public class ShapeTest {
@Autowired
private Shape shape;
@Test
public void shouldReturnShapeType() {
assertThat(shape.getClass().getSimpleName(), is("Shape"));
}
}
@Autowired를 사용하면 우선 타입을 검사해서 주입할 수 있는 대상을 확인한다. 그런데 @Autowired는 Shape뿐만 아니라 Shape의 구현 타입을 모두 찾는다. 만약 주입할 수 있는 대상이 하나라면 다음과 같은 예외를 발생시킨다.
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'Shape' available: expected single matching bean but found 4: Shape#0,Triangle#0,Rectangle#0,Square#0
한편 Shape형 변수에 주입할 수 있는 빈이 하나라면 그것이 Shape든 Shape의 구현체이든 주입이 가능하다. 여러 구현체가 이미 빈으로 등록된 경우, @Qualifier(name ="...")로 한정할 수 있다.
@Resource
@Bean(name = "...") 혹은 <bean name="..."/>로 빈을 등록하면 @Qualifier(name ="...") 혹은 식별자가 없는 경우에는 변수의 이름과 일치하는 빈을 찾아 주입한다. 만약에 이름으로 빈을 검색하는데 실패했다면 타입으로 검사한다. 함수에 @Bean(name = "...")으로 객체를 제공하는 경우, name이 생략되었다면 함수의 이름을 사용하며 만약 이름을 부여했을 때 함수의 이름과 동일하다면 예외가 발생한다. @Resource도 @Autowired와 마찬가지로 이름이 맞지 않는다면 타입으로 검사하기 때문에, 익명으로 인해 주입해야 할 타입 식별이 어려운 상황이라면 동일하게 에러를 발생시킨다.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/context/application-context.xml")
public class ShapeTest {
@Resource
private Object shape;
@Test
public void shouldReturnShapeType() {
assertThat(shape.getClass().getSimpleName(), is("Object"));
}
}
위의 예제에서 볼 수 있듯이, @Resource가 이름을 보고 먼저 빈을 주입하기 때문에 Shape 타입이 아닌 Object 타입의 빈 객체를 가져오므로 아래 테스트가 통과한다.
id vs name
id는 이 빈 객체를 참조하기 위한 유일한 식별자이며, name은 콤마나 세미콜론, 공백으로 구분하여 여러 별칭을 부여할 수 있다는 것이 큰 차이점이다.
Bean Name Generator
@Qualifier(name ="...") 등으로 이름을 제한하는 경우에는 제한할 이름이 필요한데 @Component를 사용한 경우는 AnnotationBeanNameGenerator, 그 외의 경우는 DefaultBeanNameGenerator를 사용하여 충돌이 나지 않도록 이름을 부여한다.
public class AnnotationBeanNameGenerator implements BeanNameGenerator {
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
if (definition instanceof AnnotatedBeanDefinition) {
String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
if (StringUtils.hasText(beanName)) {
// Explicit bean name found.
return beanName;
}
}
// Fallback: generate a unique default bean name.
return buildDefaultBeanName(definition, registry);
}
}
개요
대략적으론 알지만 자세히는 몰랐던 내용 중 하나로 항상 검색하면 나오는 블로그 게시글을 거의 그대로 퍼 온 내용을 기반으로 한다. 업무하면서 대부분 객체 주입을 위해
@Autowired
를 무감각하게 사용하다보니 그렇겠거니 했었는데 이번 기회에 개념과 동작 방식, 예제로 정리를 해보려고 한다.구분
@Autowired
,@Inject
타입 기반으로 객체를 주입한다. 주어진 타입이 빈으로 등록되어 있다면 자동으로 연결지어 준다. 하지만 다음 상황에 대해 이해하고 주입을 사용해야 올바르게 기능을 활용할 수 있을 것이다.
다음의 상속 계층을 가지는 도메인이 있다고 가정하고 아래처럼 빈으로 등록하자.
@Autowired
를 사용하면 우선 타입을 검사해서 주입할 수 있는 대상을 확인한다. 그런데@Autowired
는Shape
뿐만 아니라Shape
의 구현 타입을 모두 찾는다. 만약 주입할 수 있는 대상이 하나라면 다음과 같은 예외를 발생시킨다.한편
Shape
형 변수에 주입할 수 있는 빈이 하나라면 그것이Shape
든Shape
의 구현체이든 주입이 가능하다. 여러 구현체가 이미 빈으로 등록된 경우,@Qualifier(name ="...")
로 한정할 수 있다.@Resource
@Bean(name = "...")
혹은<bean name="..."/>
로 빈을 등록하면@Qualifier(name ="...")
혹은 식별자가 없는 경우에는 변수의 이름과 일치하는 빈을 찾아 주입한다. 만약에 이름으로 빈을 검색하는데 실패했다면 타입으로 검사한다. 함수에@Bean(name = "...")
으로 객체를 제공하는 경우, name이 생략되었다면 함수의 이름을 사용하며 만약 이름을 부여했을 때 함수의 이름과 동일하다면 예외가 발생한다.@Resource
도@Autowired
와 마찬가지로 이름이 맞지 않는다면 타입으로 검사하기 때문에, 익명으로 인해 주입해야 할 타입 식별이 어려운 상황이라면 동일하게 에러를 발생시킨다.위의 예제에서 볼 수 있듯이,
@Resource
가 이름을 보고 먼저 빈을 주입하기 때문에Shape
타입이 아닌Object
타입의 빈 객체를 가져오므로 아래 테스트가 통과한다.id vs name
id
는 이 빈 객체를 참조하기 위한 유일한 식별자이며,name
은 콤마나 세미콜론, 공백으로 구분하여 여러 별칭을 부여할 수 있다는 것이 큰 차이점이다.Bean Name Generator
@Qualifier(name ="...")
등으로 이름을 제한하는 경우에는 제한할 이름이 필요한데@Component
를 사용한 경우는AnnotationBeanNameGenerator
, 그 외의 경우는DefaultBeanNameGenerator
를 사용하여 충돌이 나지 않도록 이름을 부여한다.예제
객체 주입 예제 링크