[Spring] @Autowired의 다양한 사용 방법 - required, Primary, Qualifier
의존객체 타입의 빈이 없는 경우
다음과 같이 TestService 클래스와 TestRepository 인터페이스가 있다.
@Service
public class TestService {
TestRepository testRepository;
@Autowired
public TestService(TestRepository testRepository) {
this.testRepository = testRepository;
}
}
public interface TestRepository {
}
----------------------------------------------------------------------------------------
Parameter 0 of constructor in {} required a bean of type {} that could not be found.
TestService 클래스에는 @Service 어노테이션을 붙여 빈으로 등록해주었고 TestRepository는 어노테이션을 붙이지 않은 일반 자바 클래스이다.
TestRepository는 TestService의 의존객체이며, @Autowired를 붙여 TestService의 생성자를 통해 주입받도록 하였다.
이대로 실행하면 에러가 난다.
TestService 빈을 생성하려는 TestRepository 빈이 필요한데 IoC 컨테이너에서 해당하는 타입의 빈을 찾을 수 없으니 TestRepository를 빈으로 등록하라는 뜻이다.
코드를 바꿔 이번엔 setter로 주입받도록 해보자.
@Service
public class TestService {
TestRepository testRepository;
@Autowired
public void setTestRepository(TestRepository testRepository) {
this.testRepository = testRepository;
}
}
public interface TestRepository {
}
----------------------------------------------------------------------------------------
Parameter 0 of constructor in {} required a bean of type {} that could not be found.
실행하면 동일하게 에러가 발생한다.
생성자로 주입받던 것과 달리 setter로 주입받도록 했기때문에 원래는 TestService의 인스턴스는 생성할 수 있는게 맞지만 @Autowired 어노테이션에 의해 TestRepository를 주입받도록 설정한 TestService도 생성되지 않은것이다.
이렇게 @Autowired 어노테이션을 처리하던 중 해당하는 빈의 타입을 못찾거나 의존성 주입을 할 수 없는 경우에는 에러가 발생하여 어플리케이션 구동이 제대로 되지 않는다.
@Autowired에 required 속성값을 추가해보자
@Service
public class TestService {
TestRepository testRepository;
@Autowired(required = false)
public void setTestRepository(TestRepository testRepository) {
this.testRepository = testRepository;
}
}
public interface TestRepository {
}
에러 없이 정상적으로 실행 될것이다.
@Autowired(required = false)은 의존성을 ‘Optional’로 설정하는 것이다.
주입받을 의존객체가 필수적이지 않을 경우 @Autowired(required = flase)로 설정해서 의존객체를 주입받지 못하더라도 빈을 생성하도록 할 수 있다.
결과적으로 TestService는 TestRepository를 주입받지 않은 상태로 IoC 컨테이너에 등록된다.
required 속성의 기본값은 true이기 때문에 지정하지 않을 경우 setter로 주입받게 했더라도 의존객체를 주입받지 못하면 항상 에러가 발생한다.
required = flase는 @Autowired를 필드나 setter에 붙였을 경우에만 사용할 수 있다.
의존객체 타입의 빈이 여러개인 경우
@Service
public class TestService {
@Autowired
TestRepository testRepository;
}
public interface TestRepository {
}
@Repository
public class TestMyRepository implements TestRepository {
}
@Repository
public class TestYourRepository implements TestRepository {
}
TestService에서 TestRepository를 필드로 주입받도록 변경한다.
TestMyRepository, TestYourRepository 클래스를 새로 만들고 TestRepository를 구현하도록 한다.
둘다 @Repository를 붙여 빈으로 등록함으로써 TestRepository 타입의 빈이 두개가 되었다.
이 경우 Spring은 TestService에 어떤 TestRepository 빈을 주입해줄까?
결과는 주입해줄 수 없다.
두 TestRepository 중 어떤 빈을 원하는지 Spring 입장에서는 알 수 없기 때문이다.
@Primary
@Primary 어노테이션을 사용해서 해당하는 타입의 빈이 여러개일 경우 우선적으로 주입할 빈을 지정할 수 있다.
TestMyRepository에 @Primary 어노테이션을 붙이자.
@Service
public class TestService {
@Autowired
TestRepository testRepository;
}
public interface TestRepository {
}
@Repository @Primary
public class TestMyRepository implements TestRepository {
}
@Repository
public class TestYourRepository implements TestRepository {
}
이렇게 하면 Spring은 TestService에 TestRepository를 주입할 때 여러개의 빈이 존재할 경우 @Primary 어노테이션이 붙어있는 빈을 주입해준다.
@Qualifier
@Qualifier 어노테이션을 사용해서 빈 id를 지정해서 주입받을 수 있다.
다음과 같이 주입받는 곳에 @Qualifier 어노테이션을 추가해보자.
@Service
public class TestService {
@Autowired @Qualifier("testMyRepository")
TestRepository testRepository;
}
public interface TestRepository {
}
@Repository
public class TestMyRepository implements TestRepository {
}
@Repository
public class TestYourRepository implements TestRepository {
}
기본적으로 @Repository 어노테이션을 쓰면 빈 id는 소문자로 시작하는 클래스 이름과 동일하다.
따라서 TestMyRepository를 주입받도록 설정하려면 Qualifier 어노테이션에 testMyRepository 라고 지정해주면 된다.
이렇게 @Primary와 @Qualifier 어노테이션은 같은 목적으로 쓸 수 있지만 @Primary가 더 type safe한 방법이다.
해당하는 타입의 빈을 모두 주입 받기
TestService에서 TestRepository를 주입받는 변수의 타입을 List로 변경한다.
@Service
public class TestService {
@Autowired
List<TestRepository> testRepositories;
}
public interface TestRepository {
}
@Repository
public class TestMyRepository implements TestRepository {
}
@Repository
public class TestYourRepository implements TestRepository {
}
이렇게 하면 Spring은 BookRepository 타입의 빈을 모두 주입해준다.
빈 id와 변수명을 동일하게
Spring 에서 추천하는 방법은 아니지만 @Autowired는 타입을 보고 나서 여러개일 경우 변수명과 빈 id도 확인하는 과정을 거친다.
예를 들어 지금처럼 TestRepository 타입의 빈이 TestMyRepository, TestYourRepository가 존재하는 상태에서 TestMyRepository를 주입받고 싶으면 주입받을 변수명을 testMyRepository로 하면 된다.
@Service
public class TestService {
@Autowired
TestRepository testMyRepository;
}
public interface TestRepository {
}
@Repository
public class TestMyRepository implements TestRepository {
}
@Repository
public class TestYourRepository implements TestRepository {
}
여러 개의 빈 중에서 변수명과 빈 id가 동일한 빈이 주입된다.