본문 바로가기

SpringFramework Core - I. IoC 컨테이너/9. 어노테이션 기반의 컨테이너 설정

9.2. @Autowired 사용하기

원문: https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-autowired-annotation

 

 

※ JSR-330의 @Inject 어노테이션은 이번 장의 예시들에 쓰인 스프링 @Autowired 어노테이션들을 대체할 수 있다. 자세한 내용은 여기를 참고하라.

 

@Autowired 어노테이션은 다음 예시와 같이 생성자에 적용할 수 있다.

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;
    
    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao){
        this.customerPreferenceDao = customerPreferenceDao;
    }
    
    // ...
}

※ 스프링 프레임워크 4.3에 따르면, 만약 대상 bean이 단 하나의 생성자만을 가지고 있을 때는 생성자에 붙은 @Autowired 어노테이션이 불필요하다. 그러나 만약 여러 개의 생성자가 사용가능하다면 최소한 하나의 생성자에 @Autowired 어노테이션을 붙여 컨테이너에게 어떤 것을 쓰면 될 지 알려주어야 한다.

 

@Autowired는 다음 예시처럼 전통적인 setter 메소드들에도 붙을 수 있다. 

public class SimpleMovieLister {

    private MovieFinder movieFinder;
    
    @Autowired
    public void setMovieFinder(MovieFinder movieFinder){
        this.movieFinder = movieFinder;
    }
    
    // ...
}

또한 임의의 이름을 가진, 여러 인자의 메소드에도 어노테이션을 적용할 수 있다. 다음 예시를 보자.

public class MovieRecommender {

    private MovieCatalog movieCatalog;
    
    private CustomerPreferenceDao customerPreferenceDao;
    
    @Autowired
    public void prepare(MovieCatalog movieCatalog,
                       CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }
    
    // ...
}

필드에 @Autowired를 적용할 수도 있으며, 생성자와 함께 필드에도 사용할 수도 있다. 다음 예시를 보자.

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;
    
    @Autowired
    private MovieCatalog movieCatalog;
    
    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }
    
    // ...
}
 @Autowired 어노테이션이 붙은 주입 지점들에 사용될 수 있도록, 대상 컴포넌트들의 타입을 특정하여 선언하도록 해야 한다. 만약 그렇지 않으면, 런타임 시 "일치하는 타입을 찾을 수 없음"이라는 에러가 발생하며 주입을 실패하게 된다.

XML로 정의된 bean이나 컴포넌트 클래스들을 클래스패스 스캐닝을 통해 검색할 수 있도록, 보통 컨테이너들은 구체적인 타입을 미리 알고 있다. 그러나 @Bean 팩토리 메소드들을 위해서는 반환타입을 충분히 표시해주도록 해야한다. 몇몇 인터페이스를 구현하는 컴포넌트들이나 구현체를 통해 참조되는 컴포넌트들을 위해서, 여러분의 팩토리 메소드가 반환 타입을 최대한 특정해 줄 수 있도록 해야한다(최소한 여러분의 bean을 참조하는 주입 지점들이 필요로하는만큼은 특정해줘야한다). 

@Autowired 어노테이션을 특정 타입의 배열을 타입으로 가진 필드나 메소드에 붙일 수도 있다. 이를 통해, 스프링이  ApplicationContext 안에 있는 해당 타입의 모든 bean들을 제공하게 할 수 있다. 다음 예시를 보자.

public class MovieRecommender {

    @Autowired
    private MovieCatalog[] movieCatalogs;
    
    // ...
}

타입이 특정된 컬렉션에도 똑같이 적용된다. 다음 예시를 보자.

public class MovieRecommender {

    private Set<MovieCatalog> movieCatalogs;
    
    @Autowired
    public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }
    
    // ...
}
만약 여러분이 특정 순서로 정렬된 배열이나 리스트 형태로 아이템들을 원한다면, 대상 bean으로 하여금 org.springframework.core.Ordered 인터페이스를 구현하게 하거나, @Order 또는 표준적인 @Priority 어노테이션을 사용하게 하면 된다. 그렇지 않으면, 그 아이템들의 순서는 대상 bean들이 컨테이너에 정의된 순서를 따르게 될 것이다.

@Order 어노테이션은 대상 클래스 단위, @Bean 메소드, 각 bean 정의들 위에 선언될 수 있다. @Order의 값은 주입 지점들의 우선순위에 영향을 미친다. 그러나 그들이 싱글턴의 시작 순서에는 영향을 미치지 않는다는 점을 주의해야 한다. 의존 관계와 @DependsOn 선언에 의해 결정되는 것들과는 무관하기 때문이다.

표준적인 javax.annotation.Priority 어노테이션이 @Bean 단위에서는 사용할 수 없다는 것도 주의해야한다. 왜냐하면 메소드에 선언할 수 없기 때문이다. 이 어노테이션은 각 타입의 단일한 bean에 있는 @Primary와 결합한 @Order 값들을 통해서 의미가 생겨난다. 

키값의 타입이 String이라면 타입이 정해진 Map 인스턴스들도 autowired 될 수 있다. 맵의 값들은 모든 기대되는 타입의 bean을 가질 수 있다. 그리고 키값들은 일치하는 bean의 이름들을 가진다. 다음 예시를 보자.

public class MovieRecommender {

    private Map<String, MovieCatalog> movieCatalogs;
    
    @Autowired
    public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
        this.movieCatalogs = movieCatalogs;
    }
    
    // ...
}

기본적으로, 주어진 주입 지점에 사용가능한 후보 bean들이 없다면 autowiring은 실패한다. 선언된 배열, 컬렉션, 맵의 경우에는, 최소한 하나의 요소라도 매칭이 되어야한다.

 

어노테이션이 붙은 메소드나 필드를 다루기위해, 기본적으로는 필요로하는 의존성들을 가리키게 된다. 여러분들은 다음의 예시처럼 이 행동을 바꿀 수 있는데, non-required라고 표시함으로써 만족시킬 수 없는 주입 지점들을 프레임워크가 건너뛸 수 있도록 할 수 있다 (@Autowired의 required 속성을 false로 세팅하는 것이다).

public class SimpleMovieLister {

    private MovieFinder movieFinder;
    
    @Autowired(required = false)
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
    
    // ...
}

만약 의존성을 사용할 수 없다면(여러 인자 중 하나라도 사용 불가능하다면), non-required 메소드들은 호출되지 않을 것이다. 그런 경우에 non-required 필드는 채워지지 않고 기본값으로 남아있게 된다.

 

주입된 생성자와 팩토리 메서드 인자들은 특별한 경우이다. 왜냐하면 잠재적으로 복수의 생성자와 관련을 맺는 스프링 생성자 알고리즘에 비추어 볼 때, @Autowired 안의 required 속성은 다소 다른 의미를 갖기 때문이다. 생성자와 팩토리 메서드 인자들은 기본적으로 필수적이다. 하지만 단일 생성자의 경우에 몇몇의 특별한 규칙들은 예외다. 배열, 컬렉션, 맵처럼 여러 요소들의 주입 지점을 갖는 경우 매칭되는 bean이 없다면 빈 인스턴스들을 반환하는 것이다. 이는 일반적인 구현 패턴을 가능하게 한다. 이 패턴에서는 모든 의존성들이 복수의 인자를 갖는 유일한 생성자로 선언될 수 있다. 예를 들면, @Autowired 어노테이션 없이 단일한 public 생성자를 선언할 수 있는 것이다.

 

※ 어떤 bean 클래스가 주어지든 단 하나의 생성자만이 required 속성이 true인 @Autowired를 선언할 수 있다. 그러면 스프링 bean으로 사용될 때 autowire될 생성자를 가리키게 된다. 결과적으로, required 값이 기본값인 true면 단 하나의 생성자만이 @Autowired 어노테이션이 붙게된다. 만약 복수의 생성자들이 이 어노테이션을 붙이면, autowiring의 후보가 되는 상황을 위해 그들 모두 'required=false'로 선언되어야 한다(XML에서는 autowire=constructor와 유사함). bean 매칭에 의해 만족되는 의존성들 중 가장 큰 숫자의 생성자가 스프링 컨테이너에게 선택될 것이다. 만약 만족된 생성자가 하나도 없다면 기본 생성자가 사용될 것이다. 만약 클래스가 하나의 생성자만을 가지고 있다면, 어노테이션이 없더라도 항상 그것이 사용될 것이다. 어노테이션이 붙은 생성자는 public일 필요가 없다는 점을 기억하라.

 

@Autowire의 required 속성은 setter 메소드의 폐기된 어노테이션인 @Required로부터 권장된 것이다. required를 false로 세팅하면 프로퍼티가 autowiring에서 필수가 되지 않으며, autowired될 수 없는 상황에서 해당 프로퍼티는 무시된다. 

한편으로, @Required는 컨테이너가 지원하는 모든 방법을 통해 프로퍼티가 세팅되도록 한다는 점, 값이 정의되지 않으면 예외를 발생시킨다는 점에서 매우 강력하다.

 

자바 8의 java.util.Optional을 통해 특정 의존성을 non-required 성질로 표시할 수 있다. 다음 예시를 보자.

public class SimpleMovieLister {

    @Autowired
    public void setMovieFinder(Optional<MovieFinder> movieFinder) {
        ...
    }
}

스프링 프레임워크 5.0에서는 @Nullable 어노테이션이나 코틀린에 내장된 null-safety 지원을 사용하면 된다.

public class SimpleMovieLister {

    @Autowired
    public void setMovieFinder(@Nullable MovieFinder movieFinder) {
        ...
    }
}

@Autowired는 잘 알려진 분석가능한 의존성(resolvable dependencies)인 인터페이스들에도 붙일 수 있다. 예를 들어, BeanFactory, ApplicationContext, Environment, ResourceLoader, ApplicationEventPublisher 그리고 MessageSource가 있다. 이 인터페이스들과 확장된 인터페이스들, 예컨대 ConfigurableApplicationContext이나 ResourcePatternResolver는 특별한 설정없이도 자동으로 분석된다. 다음 예시는 ApplicationContext 객체를 autowire한다.

public class MovieRecommender {

    @Autowired
    private ApplicationContext context;
    
    public MovieRecommender() {
    }
    
    // ...
}

※ @Autowired, @Inject, @Value, @Resource 어노테이션들은 스프링의 BeanPostProcessor 구현체들에 의해 제어된다. 즉, 이 어노테이션들은 여러분의 BeanPostProcessor나 BeanFactoryPostProcessor 타입들 안에서는 사용할 수가 없다. 이러한 타입들은 반드시 XML이나 스프링 @Bean 메소드를 통해 명확히 wired up 되어야 한다.