[Spring] @Autowired의 다양한 사용 방법 - required, Primary, Qualifier

Summary

1. 의존객체 타입의 빈이 없는 경우
2. 의존객체 타입의 빈이 여러개인 경우

 


 

의존객체 타입의 빈이 없는 경우

다음과 같이 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 입장에서는 알 수 없기 때문이다.

 

1) @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 어노테이션이 붙어있는 빈을 주입해준다.

 

2) @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한 방법이다.

 

3) 해당하는 타입의 빈을 모두 주입 받기

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 타입의 빈을 모두 주입해준다.

 

4) 빈 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가 동일한 빈이 주입된다.


댓글(0)

Designed by JB FACTORY