@Component 어노테이션을 사용하게 되면 처음 스프링 서버가 시작이 될 때 @ComponentScan 어노테이션에 의해 자동으로 스캔되어 해당 클래스를 Bean
으로 등록한다.
@Component 어노테이션이 부여된 어노테이션들은 @Controller, @Service, @Repository 등이 있다.
일반적으로는 @Component 어노테이션을 사용하여 Bean을 자동으로 등록하는 것이 좋지만, 기술적인 문제나 공통적인 관심사를 처리할 때 사용하는 객체들
은 수동으로 등록하는 것이 좋다.
공통 로그처리와 같은 비즈니스 로직을 지원하기 위한 부가적이고 공통적인 기능들을 기술 지원Bean
이라고 부르며, 수동 등록을 한다.
수동 등록된 Bean에서 문제가 발생할 경우 해당 위치를 파악하기 쉽다.
@Configuration
public class TestConfig{
@Bean
public Test testMethod() {
return new Test();
}
}
Bean으로 등록하고자 하는 객체를 반환하는 메서드를 선언하고, 해당 메서드 위에 @Bean 어노테이션을 설정한다.
Bean을 등록한 메서드가 속한 해당 클래스 위에 @Configuration 어노테이션을 설정한다.
스프링 서버가 뜰 때 스프링 컨테이너 안에 이름과 같이 Bean으로 저장이 된다.
'Bean' 이름 : public Test testMethod() {...} -> testMethod
이렇게 수동으로 등록된 빈을 다른 클래스 내의 @Autowired
에서 DI
해주면 된다.
예시를 들어보겠다.
public interface Computer {
void info();
}
Computer라는 인터페이스가 있다고 가정을 하자
하나는 이 컴퓨터 인터페이스를 구현한 최신식 컴퓨터이고, 또 하나는 이 컴퓨터 인터페이스를 구현한 조선식 컴퓨터이다.
@Component
public class NewComputer implements Computer {
@Override
public void info() {
System.out.println("겁나 빠른 최신 컴퓨터");
}
}
@Component
public class ChosunComputer implements Computer {
@Override
public void info() {
System.out.println("겁나 느린 조선 컴퓨터");
}
}
Computer 인터페이스를 구현한 이 두 구현체 위에 @Component 어노테이션을 부여하게 된다면 컴포넌트 스캔을 할 때, 해당 클래스를 빈으로 등록한다.
이제 테스트를 한번 해보자.
@Autowired
Computer computer; // -> 여기서부터 오류가 발생한다.
@Test
@Display("테스트")
void test1() {
}
테스트 환경에서 오류가 발생한다.
해당 오류를 보면 이렇게 출력이 나온다.
Could not autowire. There is more than one bean of 'Computer' type.
즉, "Computer 타입의 Bean 객체가 하나 이상 있다" 이 말이다.
따라서 어떤 Bean으로 DI를 해줘야할 지 모른다는 의미이다.
이 문제를 해결하기 위해 3가지 방법이 있다.
@SpringBootTest
public class Test {
@Autowired
Computer newComputer;
@Autowired
Computer chosunComputer;
}
직접 등록된 Bean
의 이름을 명시하면 해결해 줄 수 있다.
이를 통해 알 수 있는 점은
@Autowired는 기본적으로 Bean Type
으로 DI를 지원한다는 것이다.
하지만 연결이 되지 않을 경우 Bean Name
으로 찾는다는 것을 확인할 수 있다.
위에서 인터페이스를 구현한 두 클래스 중 한 클래스 위에 @Primary
어노테이션을 붙인다.
@Component
@Primary
public class NewComputer implements Computer {
@Override
public void info() {
System.out.println("겁나 빠른 최신 컴퓨터");
}
}
@SpringBootTest
public class Test {
@Autowired
Computer computer;
}
@Primary
어노테이션이 추가되면 같은 타입의 Bean이 여러개 있더라도 우선적으로 @Primary
가 설정된 Bean 객체를 DI 해준다.마지막으로 @Qualifier
어노테이션을 사용하는 것이다.
지금 현재 NewComputer 클래스에 @Primary 어노테이션을 사용하니 ChosunComputer 클래스에 붙여주자.
@Component
@Qualifier("chosunComputer")
public class ChosunComputer implements Computer {
@Override
public void info() {
System.out.println("겁나 느린 조선 컴퓨터");
}
}
그리고 나서 주입하고자 하는 필드에 @Qualifier("chosunComputer") 를 추가해주면 해당 Bean 객체가 주입이 된다.
@SpringBootTest
public class Test {
@Autowired
@Qualifier("chosunComputer")
Computer computer;
}
이를 통해 확인할 수 있는점이 있다.
우선적으로 @Primary 보다 @Qualifier 어노테이션의 우선순위가 높다는 것을 확인할 수 있다.
그리고 @Qualifier는 적용하기 위해 주입하고자 하는 곳에 해당 @Qualifier를 반드시 추가해줘야 한다는 것이다.
같은 타입의 Bean이 여러개 있을 경우, 범용적으로 사용하는 Bean 객체는 @Primary를, 지엽적으로 사용하는 Bean 객체는 @Qualifier 를 사용하는 것이 좋다.